Multiple DS18B20 with Photon

Hi @FiDel

At this point I would double and triple check the addresses you are using for sensors 3-6. If those are not right, then you would get the results you are getting. You can run separate code that scans the one wire bus and reports the addresses found.

One problem with your approach here has been that the information you are sending is derived and not the raw data, so sometimes it seemed to me like you were complaining that something was wrong in the code that computed these derived values or in the Particle.variable() code itself. I was trying to point out that does not appear to be the case at all and all the values make sense given that you are getting zeros for some of the sensor values.

1 Like

Thanks @bko ! Tonight I will definitely re-scan all sensors on that bus.
I had marked each of them with their code, but you never know...

I will try to make a sketch which can send the addresses via Particle.events, because the Photon is 50 km away...

I don't understand exactly what this means...
Do you mean that I send the calculated average temperatures instead of the temperatures v ia Particle.variables?

The reason of that is that the temperatures are floating variables which can't be published as Particle.variables. Calculating average values in double format solves this issue.
Anyway, I appreciater your previous comments also!

:older_man:

You really should try to fix that problem first, rather than trying to kluge something together. There's no reason you shouldn't be able to do everything in doubles. Maybe you can email me again, and send me your entire code, so I can look at what you have now.

2 Likes

@bko : I just uploaded another sketch to that Photon 50 km away and promptly got confirmation that all 6 sensors are OK, outputting expected temperatures and they have the same addresses as used in the current sketch…

Here is the current console output:

Sensor# 6 (28FF90A200160476) = : +19.7500
Sensor# 5 (28FF166B00160308) = : +21.0625
Sensor# 4 (28FF219F611503F9) = : +26.1250
Sensor# 3 (28FF891901160457) = : +38.1875
Sensor# 2 (28FF251A011604CD) = : +43.4375
Sensor# 1 (28FF0D4C051603C7) = : +45.9375

@Ric : Thanks for your generous offer: I will send you my current project folder(s), OK?

:older_man:

1 Like

In case some of you develop a similar project, this is the final sketch @ric has helped to create last weekend:

#include <OneWire.h>
const int oneWirePin = D3;
OneWire ds = OneWire(oneWirePin);

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}};
byte addrs1[3][8] = {{0x10,0xE9,0x6B,0x0A,0x03,0x08,0x00,0xAC}, {0x10,0x44,0x4E,0x0B,0x03,0x08,0x00,0x1F}};

double TopH, TopL, MidH, MidL, BotH, BotL;
double* temps[] = {&TopH, &TopL, &MidH, &MidL, &BotH, &BotL};

double celsius;
double Tmin = 35;
double Av1, Av2, Av3, Av4, Av5;
double Q1, Q2, Q3, Q4, Q5, Qtot;


void setup()
{
  Particle.variable("TopH", TopH);
  Particle.variable("TopL", TopL);
  Particle.variable("MidH", MidH);
  Particle.variable("MidL", MidL);
  Particle.variable("BotH", BotH);
  Particle.variable("BotL", BotL);
  Particle.variable("ECO-Qtot", Qtot);
}


void loop()
{
  getTemperatures(0);

  Av1 = (TopH + TopL)/2;
  Av2 = (TopL + MidH)/2;
  Av3 = (MidH + MidL)/2;
  Av4 = (MidL + BotH)/2;
  Av5 = (BotH + BotL)/2;

  Q1 = (Av1-Tmin)*110*1.163/1000;
  Q2 = (Av2-Tmin)*90*1.163/1000;
  Q3 = (Av3-Tmin)*90*1.163/1000;
  Q4 = (Av4-Tmin)*90*1.163/1000;
  Q5 = (Av5-Tmin)*110*1.163/1000;
  Qtot = Q1+Q2+Q3+Q4+Q5;

  delay(5000);
}


void getTemperatures(int select)
{
    ds.reset();
    ds.skip(); 
    ds.write(0x44, 0);
    delay(1000);
    ds.reset();

    for (int i=0; i< sizeof(temps)/sizeof(temps[0]); 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 (select == 0)
        {
            int16_t raw = (data1 << 8) | data0;
            celsius = (double)raw * 0.0625;
        }
        else if (select == 1)
        {
            int16_t raw = data0;
            celsius = (double)raw * 0.5;
        }

        *temps[i] = celsius;
    }
}

It is in my opinion simplified as much as possible and it works really perfectly, thanks @ric!
I could not have developed this myself…
:older_man:

4 Likes

Above sketch is working great: 6 temperatures of a hot water boiler can be monitored in order to decide when to pump excess (solar or woodfire) energy to another boiler.

But as you can see, the sampling rate is every 5’ and now and then (interval = 4 - 10 h) an “odd” value is read:

This makes the system unreliable…
In order to mitigate these errors, I have constrained the values within fixed minimum/maximum margins in the loop() function of above sketch:

void loop()
{
  getTemperatures(0);

  // Constrain the 6 temperatures between 20 - 80:
  TopH = constrain(TopH, 20, 80);
  TopL = constrain(TopL, 20, 80);
  MidH = constrain(MidH, 20, 80);
  MidL = constrain(MidL, 17, 80);
  BotH = constrain(BotH, 17, 80);
  BotL = constrain(BotL, 17, 80);
  // TO DO: Limit or ignore the incorrect readings! For example: Constrain the temperatures between + and - 1 degree from previously known correct value... How can this be done in this sketch? (The Particle.publish function will publish them anyway...???

  // Calculate the average temperature in each of the 5 zones:
  Av1 = (TopH + TopL)/2;
  Av2 = (TopL + MidH)/2;
  Av3 = (MidH + MidL)/2;
  Av4 = (MidL + BotH)/2;
  Av5 = (BotH + BotL)/2;

  // Calculate the "spare" energy in each of the 5 zones:
  Q1 = (Av1-Tmin)*110*1.163/1000; // Spare energy in 110 liter top zone (kWh)
  Q2 = (Av2-Tmin)*90*1.163/1000; // Spare energy in 90 liter top/mid zone (kWh)
  Q3 = (Av3-Tmin)*90*1.163/1000; // Spare energy in 90 liter middle zone (kWh)
  Q4 = (Av4-Tmin)*90*1.163/1000; // Spare energy in 90 liter mid/bottom zone (kWh)
  Q5 = (Av5-Tmin)*110*1.163/1000; // Spare energy in 110 liter bottom zone (kWh)
  Qtot = Q1+Q2+Q3+Q4+Q5; // Total spare energy in Buffer (kWh)

  // Constrain Qtot between 0 - 30:
  Qtot = constrain(Qtot, 0, 30);

  delay(5000);
}


Still, the values fluctuate too much at times. And of course, I can not constrain the values more than the ‘normal’ values we can expect to read…

Does anyone know a simple method I could use in above (yesterday’s) sketch to NOT publish any of the temperatures to the “Particle.variables” if one of the 6 is more than 1 degree higher or lower than the previous correct value?

PS: I found a few related threads like this one: LINK1 by @ethelder or LINK2 by @mdma but don’t know which the best practice to follow…

Anybody with experience in filtering DS18x20 data?

:older_man:

Aside from the obvious electronic reasons that you may be getting bad reads, have you thought about implementing CRC checking?

@LukeUSMC posted a library with CRC checking.

something to consider…

2 Likes

I’m using the DS18B20 library (based on @LukeUSMC’s library) that I tweaked for multi sensor support and better reliability. I only sample my data once ever 15 minutes but I didn’t have a single stray reading for weeks in a row.
https://build.particle.io/libs/DS18B20/0.1.6/tab/example/ds18b20_MultiDrop.ino

Which also features CRC check

3 Likes

Also, @FiDel, you may still wish to consider some software filtering, even with the CRC checking. The dallas sensor is notorious for these weird readings.

I missed the opportunity to mention that... belt and suspenders!

3 Likes

Thanks @ScruffR and @BulldogLowell for your tips!

Indeed, I am aware of the electronic vulnerability of the one-wire system: Manufacturer's site

Also, I knew some libraries use this CRC mechanism to filter out stray values.
It would be fantastic if this could be integrated in the final sketch I am using right now: LINK

You can see I joined this thread in march 2016 with very specific requirements for my projects:

  • Must allow MANY sensors (DS18B20 and DS18S20 versions mixed) on the same I/O pin (around 25...)
  • The sensors, with known hex addresses, should ALWAYS keep the same (double variable) NAME (= "hardwired" sensor addresses!) => So, no "searching" for sensors!

Many sketches with different libraries were tried but did not fulfill my wishes, until @ric developed a beautifully complete solution. That was simplified even more last weekend, and I really love it for the clarity, simplicity and reliability it offers.

The only 2 disadvantages of this sketch I see:

  • You must run another sketch to identify the sensor addresses before being able to use it "hardwired"
  • No CRC checking is used, to filter out stray values

So, if some of you see the possibility to add this CRC checking to this sketch, it would make it more reliable!

On top of that, you are right @BulldogLowell, I will try to add a filtering method, checking the "continuity" of the data along these lines:
I check temperatures every 10 seconds or so.
And the water temperature won't change quickly.
So, for each value, I must check if it is less than 1°C bigger or smaller than the previous value.
If yes, I will replace the variable with the new value.
If not, I will keep it the same.

Only, I'm not sure if I have already enough experience to develop that in a simple way...

:older_man:

Sounds great @ScruffR, thanks for sharing this link!
It is certainly worth checking it against my requirements (see previous post).

I have just tried it first in the Web IDE and I got this error message:

Actually, I got this every time I compile a sketch using OneWire library in the Web IDE...

I tried it in ParticleDev with your library and with the OneWire library. It compiles and uploads OK.

Although I adapted the sensor pin to D3 and number of sensors to 6 for my current setup, I got only 2 values of which one seems wrong:

I don't know what I am doing wrong, but never mind:
I believe this is a great tool to "discover" a number of sensors and publish their value, but it (probably) will be a big job again to adapt it to both my above requirements...

Good night...
:frowning:

The only 2 disadvantages of this sketch I see:

  • You must run another sketch to identify the sensor addresses before being able to use it “hardwired”
  • No CRC checking is used, to filter out stray values

I get it, you need to know which sensors are which.

most libraries, while including searching method, should allow you to hardcode the unique addresses when reading.

the better libraries include CRC checking, because of the known issues (which you are well aware).

While @Ric did a great job organizing your code, I’d still go the extra step of creating an object oriented approach as to greatly simplify your code and organize your data. But that is another lesson!

I’ve watched this thread for a while, I’m rooting for you to get this perfected!!!

1 Like

Thanks @BulldogLowell, I see you are with me!

I guess that's right, but I am so happy with @Ric's solution that I would prefer to refine that one further with CRC checking rather than to try all over again starting with another library. Let me know if you don't agree and which one gives us a better solution... :wink:

Concerning the extra "filtering" needed:
I remembered another exercise with @Ric, developing a "filtering method": LINK
Probably that's an even better way than my previous idea...

What do you think?
:sleeping:

Do you mean with arrays?
More simplification would be great, but usability is more important for my applications. The requirements in my earlier post are vital!
:sleeping:

sounds like the gauntlet has been thrown... I'm sure @Ric is up to the task.

I'd create a struct like this for the pairs of sensors:

struct Zone{
    byte highAddress[8]; // high sensor
    byte lowAddress[8];  // low sensor
    double highTemp;
    double lowTemp;
    double averageTemp;
    double energy;
};

void doSomethingWith(Zone* zone);

and an array of those objects like this:

Zone tempSensor[]{
    {{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}}
};

you can assign names to them as you did in @Ric 's code:

Zone* Tank1 = &tempSensor[0];
Zone* Tank2 = &tempSensor[1];
Zone* Tank3 = &tempSensor[2];

Zone* myTanks[] = {Tank1, Tank2, Tank3};

then call a function on the objects:

for (auto& eachTank : myTanks)
{
    doSomethingWith(eachTank);
}

maybe a bit tricky to grasp, but there are (in this example) three tank objects each that retain two temperatures, their average and the energy... kind-of-thing.

1 Like

I might be in a couple of days, but I don’t want to try to write code on my phone ( I’m in Yosemite park right now).:wink:

2 Likes

I guess you are not targeting a system version greater 0.5.2 in Web IDE.

1 Like

I was targeting v0.6.2
Should I aim lower?

Sounds interesting, but indeed it’s over my head…

As far as I’m concerned I’m fine with @Ric 's final sketch (without library!) as it is easy to use and very reliable.

What would improve with the way you suggest here?

Hmm, that’s surprising.
When I USE THIS EXAMPLE ds18b20-multidrop.ino in Web IDE as is I only get this error when targeting a version 0.5.2 or before.

You may be mistaking the installed version on the device (v0.6.2) in the bottom right corner in Web IDE as the target version.
The actual target version is set in the target drawer (third icon up on the left hand bar) and I guess when you open the device info (chevron >) next to your device you’ll see you are not targeting 0.6.2.

1 Like