Multiple DS18B20 with Photon


#81

@FiDel, @MORA, @BulldogLowell, I just wanted to be clear that the only purpose of my code was to show how you could get the temperatures into meaningful variable names using arrays (I assume FiDel will use better names than T1, T2, etc.). I’m agnostic with regard to which library to use, I only used the OneWire library because that’s the one I’d been using in my own code, and had code snippets that I could easily grab.


#82

Indeed @Ric, that was indeed one of my important criteria.
After testing your code, I feel very comfortable in creating the control structures in a very clean way.
Congratulations for having made this in such a short time!!!

of course, I learned a lot from all contributions and I hope to use some of these tips in other sketches.
Thank you all for participating in this search for the holy grail…


Now only my “Bus2” issue must be solved…
:fearful:


#83

I don’t see any problem in the code that would cause you to get those readings on bus2 (assuming your addresses are correct), so I think it must be something physical. Do you know the exact part number of those sensors? Have you had these sensors working correctly before?


#84

Yes, I will troubleshoot it as soon as I can.
Very little time right now…
Thanks again @Ric!


#85

Hurray! The “Bus2” issue is solved!

Bus1 which worked fine is made of 6 “waterproof” DS18B20 sensors.
For Bus2, I used the smaller TO-92 format DS18S20 sensors.

We found in the spec sheets of both sensors that they are more different than we thought:
The S is a 9 bit resolution device, and the B is a 12 bit resolution device! That’s why the output values in Bus2 are too small…

@Ric came with a simple IF structure in the Dallas.cpp file, so that you can use both.
(Keep one bus populated with one type only!)

Note: I’m glad to say that @MORA’s library allows mixed use of those sensors, and that’s great!
But that program is not adapted to what I want to achieve… :yum:


SUMMARY TILL NOW:

To summarize the work done till now, I want to put everything in a row for those who want to use this:
here is my latest (working) version which now does exactly what I wanted it to do:

To upload, I recommend to use particle Dev.
Place all 5 files in a folder on your PC. I call it “Project”:

  • Open the Ric_Dallas.ino main file in Particle Dev.
  • Modify 2 files to suit your needs: Dallas.cpp and Ric_Dallas.ino.
  • Select a Particle which is on-line and flash it via the cloud…

Here are the 3 files which were built by Ric and modified by me to suit my testing needs:

1) Dallas.h

#include "Particle.h"

void getTemperatures(float temps[], int tempsCount, int pin, int select);

2) Dallas.cpp

/* Ric_Dallas.ino = Sketch to read hardcoded DS18B20 sensors by their HEX sensor code and call them from the loop() by their names...
Created by @Ric here: https://community.particle.io/t/multiple-ds18b20-with-photon/13332/68?u=fidel

These are the ROM codes (+ HEX format) of my 8 test-sensors: (Starting with 10=DS18S20, with 28=DS18B20)
Sensor# 1	= 28FF0D4C051603C7 = 0x28,0xFF,0x0D,0x4C,0x05,0x16,0x03,0xC7 (DS18B20 Waterproof wired sensor #1)
Sensor# 2	= 28FF251A011604CD = 0x28,0xFF,0x25,0x1A,0x01,0x16,0x04,0xCD (DS18B20 Waterproof wired sensor #2)
Sensor# 3	= 28FF891901160457 = 0x28,0xFF,0x89,0x19,0x01,0x16,0x04,0x57 (DS18B20 Waterproof wired sensor #3)
Sensor# 4	= 28FF219F611503F9 = 0x28,0xFF,0x21,0x9F,0x61,0x15,0x03,0xF9 (DS18B20 Waterproof wired sensor #4)
Sensor# 5	= 28FF166B00160308 = 0x28,0xFF,0x16,0x6B,0x00,0x16,0x03,0x08 (DS18B20 Waterproof wired sensor #5)
Sensor# 6	= 28FF90A200160476 = 0x28,0xFF,0x90,0xA2,0x00,0x16,0x04,0x76 (DS18B20 Waterproof wired sensor #6)
Sensor# 7	= 10E96B0A030800AC = 0x10,0xE9,0x6B,0x0A,0x03,0x08,0x00,0xAC (DS18S20 TO92 #1)
Sensor# 8	= 10444E0B0308001F = 0x10,0x44,0x4E,0x0B,0x03,0x08,0x00,0x1F (DS18S20 TO92 #2)
*/

#include "Dallas.h"
// Choose one of 2 possibilities:
// 1) For use in the IDE:
//#include "OneWire/OneWire.h"

// 2) For use with Particle Dev:
#include "OneWire.h"

double celsius;

// Group sensors in their bus (on one I/O pin) ; [6][8] means: 6 sensor codes with 8 bytes.
// Attention: If the 2 types of sensors are mixed, you must group DS18S20 sensors in one bus and DS18B20 in another bus. You must adapt below code (if(select == 0 or 1)) to the sensor type...
byte addrs0[6][8] = {{0x28,0xFF,0x0D,0x4C,0x05,0x16,0x03,0xC7}, {0x28,0xFF,0x25,0x1A,0x01,0x16,0x04,0xCD}, {0x28,0xFF,0x89,0x19,0x01,0x16,0x04,0x57}, {0x28,0xFF,0x21,0x9F,0x61,0x15,0x03,0xF9}, {0x28,0xFF,0x16,0x6B,0x00,0x16,0x03,0x08}, {0x28,0xFF,0x90,0xA2,0x00,0x16,0x04,0x76}};// 6x DS18B20 Waterproof wired sensors
byte addrs1[3][8] = {{0x10,0xE9,0x6B,0x0A,0x03,0x08,0x00,0xAC}, {0x10,0x44,0x4E,0x0B,0x03,0x08,0x00,0x1F}, {0x28, 0xD, 0xD3, 0xE2, 0x3, 0x0, 0x0, 0xEE}};// 2x DS18S20 TO92 sensors


void getTemperatures(float temps[], int tempsCount, int pin, int select)
{
    OneWire ds = OneWire(pin);
    ds.reset();
    ds.skip();          // Make all devices start the temperature conversion
    ds.write(0x44, 0);  // Tell it to start a conversion (second argument: 1 = parasite power, 0 = powered mode)

    delay(1000);       //  Wait 1 sec for conversion
    ds.reset();

    for (int i=0; i<tempsCount; i++)
    {
        switch (select)
        {
            case 0:
                ds.select(addrs0[i]);
                break;
            case 1:
                ds.select(addrs1[i]);
                break;
        }

        ds.write(0xBE,0);

        byte data0 = ds.read();
        byte data1 = ds.read();
        ds.reset();

        // If you mix both types of sensors, you need this branching:
        if (select == 0)
        {
            int16_t raw = (data1 << 8) | data0;
            celsius = (float)raw * 0.0625; // DS18B20 sensor = 12 bit resolution device
        }
        else if (select == 1)
        {
            int16_t raw = data0;
            celsius = (float)raw * 0.5; // DS18S20 sensor = 9 bit resolution device
        }

        /*
        // If you use only DS18B20 sensors, you can keep it simpler:
        int16_t raw = (data1 << 8) | data0;
        celsius = (float)raw * 0.0625; // For DS18B20 sensors
        */

        temps[i] = celsius;
    }
}

3) Ric_Dallas.ino

#include "Dallas.h"

float T1, T2, T3, T4, T5, T6, T7, T8, T9; // The names you want to call the sensors, created in the array(s) in Dallas.cpp

// Group the sensors by which pin they're connected to:
float* temps1[] = {&T1, &T2, &T3, &T4, &T5, &T6}; // Group1: 6 waterproof sensors
float* temps2[] = {&T7, &T8, &T9};                // Group2: 2 TO92 sensors + unused code (for testing)

void setup()
{
  Serial.begin(9600);
  delay(3000);
}


void loop()
{
  // Read sensor values! Argument 1: ??? (N° of sensors?) , Argument 2: Particle I/O pin , Argument 3: Select array of addresses (addrs0, addrs1 ...) in Dallas.cpp
  getTemperatures(*temps1, 6, D1, 0);
  getTemperatures(*temps2, 3, D0, 1);

  // From now you can use the chosen sensor names as if it were ordinary floating type variables...

  // Here's an example: Using one of the temperatures in a condition: Warm it up >25°C, then prints all temperatures to serial monitor by bus...

  if (T4<30) //print all temperatures to serial monitor by bus...
  {
   Serial.printf("D0 BUS: T7 = %.1f T8 = %.1f", T7, T8);    Serial.printf(" - D1 BUS: T1 = %.1f T2 = %.1f T3 = %.1f T4 = %.1f T5 = %.1f T6 = %.1f", T1, T2, T3, T4, T5, T6);    Serial.println();
  }
  else // Alert!
  {
   Serial.print("Wait a while: T4 > 30C! : "); Serial.println(T4);
  }
  delay(2000); // Minimum 1000 !!!
}

That’s it!
I would like to thank all those who contributed again, but especially Ric who made exactly what I was dreaming of… :rainbow:

Now, as I monitor these 8 sensors, I can see that now and then there is one which outputs a strange value, way out compared to the actual temperature.
So, we will need some filtering of data in Dallas.cpp.

Yes, I know that @MORA’s library has also CRC checking which is great! (The sensors have functions built in for that)
Surely that’s also possible with @Ric’s system. But I’m not experienced enough to make that work…

This morning I ordered 50 more waterproof DS18B20 sensors…
To be continued… in 3 buses!
:hand::older_man:


#86

Actually the DS18B20 is a configurable 9-12bit sensor, see page 9 in the datasheet, the more bits you require, the slower the convert command will be, 750ms at 12bits, less than 100ms at 9bits, it can be useful to lower resolution for tracking fast changing temperature.


#87

That’s a good tip @MORA!
And how do you select this accuracy level with your library?


#88

You have to write the config you want to scratchpad (0x4E)


#89

OK, thanks @MORA
When I ever need to scratch that pad, I’ll knock on your door again…
:older_man:


#90

Newbies - I loaded this code and two libs on my photon exactly as provided. I swapped my yellow lead from D2 to D0 and opened my browser to the logs. I plugged my 4 probes in one by one to get their addresses and then marked them for future use. This was a huge time saver. Thank you for the code.


#91

Thanks for the tip,

Are you referring the code by @Ric or @Mora ?


#92

I tried the code

on a Photon with last firmware (0.6.1) and everything works well (by chance I have 6 waterproof sensors too, so I just changed the addresses), but when I add a bit more code (mainly a couple of long arrays), it fails to read the temperature(s) last sensor(s).

I’m quite new to C and C++ and maybe the issue is also related to my code, but there is some strange behaviour and it’s related to the pointer:

float* temps1[] = {&T1, &T2, &T3, &T4, &T5, &T6};

I made the following changes and everything works smoothly also adding more code:

float temps1[7]{0, 0, 0, 0, 0, 0};

instead of

float T1, T2, T3, T4, T5, T6, T7, T8, T9; // The names you want to call the sensors, created in the array(s) in Dallas.cpp
// Group the sensors by which pin they're connected to:
float* temps1[] = {&T1, &T2, &T3, &T4, &T5, &T6}; // Group1: 6 waterproof sensors
float* temps2[] = {&T7, &T8, &T9}; 

and

getTemperatures(temps1, 6, D6, 0);
..
sprintf(resultstr, "T1=%.1f T2=%.1f T3=%.1f T4=%.1f T5=%.1f T6=%.1f", temps1[0], temps1[1], temps1[2], temps1[3], temps1[4], temps1[5]);

instead of

getTemperatures(*temps1, 6, D6, 0);
//getTemperatures(*temps2, 3, D0, 1);
...
sprintf(resultstr, "T1=%.1f T2=%.1f T3=%.1f T4=%.1f T5=%.1f T6=%.1f", T1, T2, T3, T4, T5, T6);

There are surely more elegant and efficient ways to address it, but this worked for me and I hope can be of help for someone else too.


#93

Thanks for your feedback!
I will try out your improvements when I need to review my code… :wave::older_man:


#94

Hi all!
It’s been since march '16 that we put our heads together, and I am still grateful to all of you who contributed to a good solution to use these great sensors reliably!

Since then I have successfully been using @Ric 's elegant solution with his own library to read multiple DS18B20 sensors divided over 2 different buses (2 I/O pins).

Today, I have installed another series of 6 sensors on a water heating buffer, in order to calculate the total energy contents (5 volumes of water with each an average temperature between 2 sensors)

Here’s an image:

In this case, I want to use only one series, using one I/O pin.
Therefore I have tried to simplify @Ric 's library as follows:

Here’s @Ric 's library “Dallas.cpp”:


#include "Dallas.h"

// For the OneWire library, choose one of 2 possibilities:
//#include "OneWire/OneWire.h"  // For use in the IDE:
#include "OneWire.h"            // For use with Particle Dev:

float celsius;

// Group sensors in their bus (on one I/O pin) ; [6][8] means: 6 sensor codes with 8 bytes.
byte addrs0[6][8] = {{0x28,0xFF,0x0D,0x4C,0x05,0x16,0x03,0xC7}, {0x28,0xFF,0x25,0x1A,0x01,0x16,0x04,0xCD}, {0x28,0xFF,0x89,0x19,0x01,0x16,0x04,0x57}, {0x28,0xFF,0x21,0x9F,0x61,0x15,0x03,0xF9}, {0x28,0xFF,0x16,0x6B,0x00,0x16,0x03,0x08}, {0x28,0xFF,0x90,0xA2,0x00,0x16,0x04,0x76}}; // 6x DS18B20 Waterproof wired sensors

void getTemperatures(float temps[], int tempsCount, int pin, int select)
{
    OneWire ds = OneWire(pin);
    ds.reset();
    ds.skip();          // Make all devices start the temperature conversion
    ds.write(0x44, 0);  // Tell it to start a conversion (second argument: 1 = parasite power, 0 = powered mode)

    delay(1000);       //  Wait 1 sec for conversion
    ds.reset();
    ds.select(addrs0[0]);

    ds.write(0xBE,0);

    byte data0 = ds.read();
    ds.reset();

    int16_t raw = (data0 << 8) | data0;
    celsius = (float)raw * 0.0625; // For DS18B20 sensors

    temps[0] = celsius;
}

My sketch looks as follows:


/* Buffer-TEST.ino = Sketch to read hardcoded DS18B20 sensors by their HEX sensor code and call them from the loop() by their names...
*/

#include "Dallas.h" // This library is NOT the same as in the Particle IDE!

float TopH, TopL, MidH, MidL, BotH, BotL; // The names you want to call the sensors, created in the array(s) in Dallas.cpp

// Group the sensors by which pin they're connected to:
float* temps1[] = {&TopH, &TopL, &MidH, &MidL, &BotH, &BotL}; // Group1: 6 waterproof sensors

void setup()
{
  Serial.begin(9600);
  delay(3000);

  // Particle.variable does not work with 'float' variables. OK with 'double'... (But then I get weird errors...)

  //Particle.variable("ECO_Top-H", TopH);
  //Particle.variable("ECO_Top-L", TopL);
  //Particle.variable("ECO_Mid-H", MidH);
  //Particle.variable("ECO_Mid-L", MidL);
  //Particle.variable("ECO_Bot-H", BotH);
  //Particle.variable("ECO_Bot-L", BotL);

}


void loop()
{
  // Read sensor values! Argument 1: N° of sensors, Argument 2: Particle I/O pin , Argument 3: Select array of addresses (addrs0, addrs1 ...) in Dallas.cpp
  getTemperatures(*temps1, 6, D3, 0); // From now you can use the chosen sensor names as if it were ordinary floating type variables...

  // Example: Serial output:
  Serial.printf("D1 BUS: TopH = %.1f TopL = %.1f MidH = %.1f MidL = %.1f BotH = %.1f BotL = %.1f", TopH, TopL, MidH, MidL, BotH, BotL);
  Serial.println();
  Serial.println(TopH);
  Serial.println(TopL);
  Serial.println(MidH);
  Serial.println(MidL);
  Serial.println(BotH);
  Serial.println(BotL);
  Serial.println();

  // Final purpose: Calculate the energy in each zone and add them together: Total buffer energy content.

  delay(2000); // Minimum 1000 !!!
}

This combination set-up compiles OK in Particle DEV and it seems to work, but now I have 2 problems:

  1. The temperatures are not shown in the serial output, which I use to test it. (see serial output below)

  2. I cannot use Particle.variable in this sketch (I blanked them out) as it uses ‘float’ type variables. I know that float is not supported in Particle.variable, but double is. However, when I change all variables to double I get weird errors…

Can somebody get me “afloat” again please?

Here’s the serial output I am currently getting:


D1 BUS: TopH = 1461.7 TopL = 0.0 MidH = 0.0 MidL = 0.0 BotH = 0.0 BotL = 0.0
1461.69
0.00
0.00
0.00
0.00
0.00

There seems to be also a ‘scaling’ error:
The first temperature (TopH) is showing, but around 20 times higher than expected… The water temperature is around 70°C.

Which mistakes did I make?
Crossing my fingers…

PS: If possible, I’d like to integrate the contents of that library in my final sketch, so that I can also flash via the web IDE. (That library is not the same as the Dallas library available in the web IDE)

FiDel
:older_man:


#95

Something is wrong in the code for dealing with the return values–I don’t know your library but here is the way that works with one wire:

    one.write(0xBE);
    one.read_bytes(resp, 9);

    byte MSB = resp[1];
    byte LSB = resp[0];
    
    int16_t intTemp = ((MSB << 8) | LSB); //using two's compliment
    int32_t intTempC = (int32_t)intTemp;
    double tempC = (double)intTemp / 16.0;
    double tempF = ((tempC * 9.0) / 5.0 + 32.0);

You can see you send 0xBE and then read 9 bytes, two of which are the temperature.


#96

It has been a long time, so I’m not remembering everything we did. I don’t seem to have a copy of my modified Dallas.cpp library any more. I don’t know why you get a weird result for the first sensor, but the reason you’re not getting readings from the other sensors is that you never ask for them. In getTemperatures(), you’ve hard coded a 0 for the index into temps[], so you’re only going to get a value from the first sensor.


#97

Hi @bko ! Thanks for your feedback.
I understand just a bit of that “bit banging”, but not enough to see how I can apply it to my combination library/sketch…

"double tempC = (double)intTemp / 16.0;"
is indeed the same as used in @Ric 's library:
celsius = (float)raw * 0.0625;

I keep looking for some more tips…
:older_man:


#98

@Ric , thanks for coming back!
Here is your original “Dallas.cpp”:


#include "Dallas.h"
// Choose one of 2 possibilities:
// 1) For use in the IDE:
//#include "OneWire/OneWire.h"
// 2) For use with Particle Dev:
#include "OneWire.h"

double celsius;

// Group sensors in their bus (on one I/O pin) ; [6][8] means: 6 sensor codes with 8 bytes.
// Attention: If the 2 types of sensors are mixed, you must group DS18S20 sensors in one bus and DS18B20 in another bus. You must adapt below code (if(select == 0 or 1)) to the sensor type...
byte addrs0[12][8] = {{0x28,0xFF,0x0D,0x4C,0x05,0x16,0x03,0xC7}, {0x28,0xFF,0x25,0x1A,0x01,0x16,0x04,0xCD}, {0x28,0xFF,0x89,0x19,0x01,0x16,0x04,0x57}, {0x28,0xFF,0x21,0x9F,0x61,0x15,0x03,0xF9}, {0x28,0xFF,0x16,0x6B,0x00,0x16,0x03,0x08}, {0x28,0xFF,0x90,0xA2,0x00,0x16,0x04,0x76}, {0x28,0x3A,0xBC,0x07,0x00,0x00,0x80,0x58}, {0x28,0x72,0x03,0x04,0x00,0x00,0x80,0x24}, {0x28,0xD4,0xE7,0x03,0x00,0x00,0x80,0x89}, {0x28,0x78,0xF9,0x03,0x00,0x00,0x80,0x76}, {0x28,0x70,0xAD,0x07,0x00,0x00,0x80,0x53}, {0x28,0x40,0xE1,0x03,0x00,0x00,0x80,0x78}}; // 12x DS18B20 Waterproof wired sensors
byte addrs1[3][8] = {{0x10,0xE9,0x6B,0x0A,0x03,0x08,0x00,0xAC}, {0x10,0x44,0x4E,0x0B,0x03,0x08,0x00,0x1F}, {0x28, 0xD, 0xD3, 0xE2, 0x3, 0x0, 0x0, 0xEE}};// 2x DS18S20 TO92 sensors (Third code is from Ric, not on the bus: Test error handling!)


void getTemperatures(float temps[], int tempsCount, int pin, int select)
{
    OneWire ds = OneWire(pin);
    ds.reset();
    ds.skip();          // Make all devices start the temperature conversion
    ds.write(0x44, 0);  // Tell it to start a conversion (second argument: 1 = parasite power, 0 = powered mode)

    delay(1000);       //  Wait 1 sec for conversion
    ds.reset();

    for (int i=0; i<tempsCount; i++)
    {
        switch (select)
        {
            case 0:
                ds.select(addrs0[i]);
                break;
            case 1:
                ds.select(addrs1[i]);
                break;
        }

        ds.write(0xBE,0);

        byte data0 = ds.read();
        byte data1 = ds.read();
        ds.reset();

        // If you mix both types of sensors, you need this branching:
        if (select == 0)
        {
            int16_t raw = (data1 << 8) | data0;
            celsius = (float)raw * 0.0625; // DS18B20 sensor = 12 bit resolution device
        }
        else if (select == 1)
        {
            int16_t raw = data0;
            celsius = (float)raw * 0.5; // DS18S20 sensor = 9 bit resolution device
        }

        /*
        // If you use only DS18B20 sensors, you can keep it simpler:
        int16_t raw = (data1 << 8) | data0;
        celsius = (float)raw * 0.0625; // For DS18B20 sensors
        */

        temps[i] = celsius;
    }
}

#99
    byte data0 = ds.read();
    ds.reset();

    int16_t raw = (data0 << 8) | data0;

Hi @FiDel

Your code above only reads one byte and then uses it twice with the shift and or into the variable raw. This is not correct in any way.


#100

I don’t know why you made the changes that you did. As you can see from the library, there is a for-loop that loops through all the members of the array, which you need to read all your sensors. Also, notice that ds.read() is called twice and the values are put into data0 and data1. You need both of those reads to get the correct temperature.

int16_t raw = (data1 << 8) | data0;