Multiple DS18B20 with Photon


#137

Yes, that’s what I thought it was…
Indeed, it was targeting a lower version!
I set it to v0.6.2 and Hoopla it compiles!
Thanks for pointing that out @ScruffR

All my other Particle devices have v0.6.2 targeted… How did it change back on this device?

Q: Do we need to set this target version manually for each device or will it be set sometimes by Particle?


#138

@Ric : Don’t worry and enjoy your adventure @Yosemite!

I believe the QSORT filtering you described earlier (LINK) will really perfect your sketch (without library)

It’s probably all we need to filter out all stray values. Isn’t it?

This is how the recording for one boiler (6 sensors) looks currently:


#139

This is a per device setting and will not be adapted by Particle (deliberately - but glitches might occure).
The only “exception” to this is when you chose Default (x.y.z) (currently Default (0.6.2)) as target. This instructs Web IDE to keep targeting the most recent official release.

This is intended, since some projects (for particlular devices) might rely on some “glitches” of older versions and if Particle would mess with your settings, you’d not be too happy if a fine running project suddenly threw up unexpectedly.


#140

Very useful, thanks @ScruffR!


#141

@FiDel,

This uses @ScruffR 's library that includes CRC checking. I introduced the object-oriented approach I mentioned before.

Of course, i cannot test it but you would have to check that the top, mid and bot sensors were the correct addresses.

#include <DS18B20.h>

struct Zone{
    char sensorName[4];
    uint8_t hiSensor[8]; 
    uint8_t loSensor[8]; 
    double hiTemp;
    double loTemp;
    double avgTemp;
    double energy;
};

void readSensorSet(Zone* zone);
double getTemp(uint8_t addr[8]);

Zone tempSensor[]{
    {"Top", {0x28,0xFF,0x0D,0x4C,0x05,0x16,0x03,0xC7}, {0x28,0xFF,0x25,0x1A,0x01,0x16,0x04,0xCD}}, 
    {"Mid", {0x28,0xFF,0x89,0x19,0x01,0x16,0x04,0x57}, {0x28,0xFF,0x21,0x9F,0x61,0x15,0x03,0xF9}}, 
    {"Bot", {0x28,0xFF,0x16,0x6B,0x00,0x16,0x03,0x08}, {0x28,0xFF,0x90,0xA2,0x00,0x16,0x04,0x76}}
};

Zone* Top = &tempSensor[0];
Zone* Middle = &tempSensor[1];
Zone* Bottom = &tempSensor[2];

Zone* sensorSet[] = {Top, Middle, Bottom};

double Qtot;

const int pinOneWire = D2;
const int pinLED = D7;

DS18B20 ds18b20(pinOneWire);

void setup() 
{
  Serial.begin(9600);
  
  for (auto set : tempSensor)
  {
      Particle.variable(String(set.sensorName) + "H", set.hiTemp);
      Particle.variable(String(set.sensorName) + "L", set.loTemp);  // EDIT fixed typo
  }
  Particle.variable("ECO-Qtot", Qtot);
}

void loop() 
{
  double accumulatedEnergy = 0;
  for (auto& set : tempSensor)
  {
      set.hiTemp = getTemp(set.hiSensor);
      set.loTemp = getTemp(set.loSensor);
      set.avgTemp = (set.hiTemp + set.loTemp) / 2;
      set.energy = (set.avgTemp - set.loTemp) * 110 * 1.163 / 1000;
      accumulatedEnergy += set.energy;
  }
  Qtot = accumulatedEnergy;
  delay(5000);
}

double getTemp(uint8_t addr[8]) 
{
    static const int MAXRETRY = 3;
    double _temp;
    int i = 0;

    do {
         _temp = ds18b20.getTemperature(addr);
    } while (!ds18b20.crcCheck() && MAXRETRY > i++);

    if (i < MAXRETRY) 
    {
        //celsius = _temp;
        //fahrenheit = ds18b20.convertToFahrenheit(_temp);
        Serial.println(_temp);
    }
    else 
    {
        _temp = NAN;
        //celsius = fahrenheit = NAN;
        Serial.println("Invalid reading");
    }
    return _temp;
}

i’m not using:

Zone* Top = &tempSensor[0];
Zone* Middle = &tempSensor[1];
Zone* Bottom = &tempSensor[2];

Zone* sensorSet[] = {Top, Middle, Bottom};

#142

Thanks for this effort @BulldogLowell!

I will certainly come back later to this possible alternative when I have more time for experimenting AND when I’m close to the Photon used for this (I’m living one hour drive from there and cannot use serial output. That’s why I use Particle.variables instead)

First I prefer to further “polish” the sketch from @Ric, integrating another of his good ideas: QSORT “median filtering”. I believe that combination will do exactly what I need, even without CRC checks.

Also, I’d like to see the clear benefits of your proposed approach first.
For my home project it is a very essential application, as it will finally be running on 10+ Photons (and also 2 Electrons later)

When comparing the application size, it is not an advantage:

Your application (sketch + library)

  • Your sketch = 1968 bytes
  • DS18B20.cpp = 4251 bytes
  • DS18B20.h = 1129 bytes

Total = 7350 bytes => vs @Ric’s sketch Total = 2121 bytes

To be continued… :wink:


#143

I included the Particle Variables, just as you have.

adding filtering to this is somewhat trivial.

either fits fine on a Photon and neither are doing things that may affect stability in the long term.

i feel that it is:

  1. easier to read and understand what is happening… you are not moving pointers around an array which is a composite of several physical objects. this organizes the objects as they are “installed”
  2. does the CRC checking, which is basically necessary to avoid data blips (the “belt”)
  3. easier to make changes to the code, like adding the “suspenders” (software filtering)
  4. simpler code (well, if you happen to be the one writing it!)

:wink:


#144

This is a common misconception - that file size is important in defining what is “good” or not. Clarity, portability and sustainability (ability to manage or extend the code) is more important. I suspect the compiled file size is a) very similar and b) irrelevant since both will leave plenty of space for other code, if needed at all.


#145

Of course @peekay123, I can agree with what you state about “size does not matter”… :wink:

But in my 10 room sketches for the 10 Photons in my (home automation) project in progress, so many other libraries and scripts need to be ‘interwoven’ for various functions that I am constantly alerted to keep the footprint of each app as small (and simple) as possible…

Is that not a correct approach?


#146

hard to argue the contrary there.

So, I too and a bit sniped by this problem.

I thought about this a bit and figured that if I am adding CRC checks from @ScruffR 's library, then all we need to do is add the filtering.

If you are filtering, why arbitrarily de-select the highest or lowest values? If they are valid readings, then you could use a variation based on reasonable mathematics (e.g. standard deviation).

so I added a method (in this example up to 25) for temperatures to accumulate and added new readings to the value if and only if the current reading is inside a given multiple of standard deviations (I used 1.5 in this code, but you could use any number).

I thought about using qsort() but I’d have to learn how, so I decided to use C++ methods instead.

I whacked this out at lunch… maybe take it for a try while the forum gurus evaluate the code. :blush:

#include <algorithm>
#include <vector>

#include <DS18B20.h>

#define SAMPLE_SIZE 25
#define STANDARD_DEVIATION_FILTER 1.5  // we will toss any value that isnt within this number of standard deviations of the past BUFFER_SIZE readings

struct Zone{
    char sensorName[4];
    uint8_t hiSensor[8]; // high sensor
    uint8_t loSensor[8];  // low sensor
    double hiTemp;
    double loTemp;
    double avgTemp;
    double energy;
    std::vector<double> hiArray;
    std::vector<double> loArray;
    
    bool addHiTemp(double newVal)
    {
        if (hiArray.size() == 0)
        {
            hiArray.push_back(newVal);
            return true;
        }
        double sum = std::accumulate(hiArray.begin(), hiArray.end(), 0.0);
        double mean = sum / hiArray.size();
        std::vector<double> delta(hiArray.size());
        std::transform(hiArray.begin(), hiArray.end(), delta.begin(), [mean](double x) { return x - mean; });
        double squareSum = std::inner_product(delta.begin(), delta.end(), delta.begin(), 0.0);
        double stdDeviation = std::sqrt(squareSum / hiArray.size());
        if (std::abs(newVal - mean) > STANDARD_DEVIATION_FILTER * stdDeviation)
        {
            return false;
        }
        hiArray.insert(hiArray.begin(), newVal);
        if(hiArray.size() >= SAMPLE_SIZE)
        {
            hiArray.pop_back();
        }
        return true;
    }
    bool addLoTemp(double newVal)
    {
        if (loArray.size() == 0)
        {
            loArray.push_back(newVal);
            return true;
        }
        double sum = std::accumulate(loArray.begin(), loArray.end(), 0.0);
        double mean = sum / loArray.size();
        std::vector<double> delta(loArray.size());
        std::transform(loArray.begin(), loArray.end(), delta.begin(), [mean](double x) { return x - mean; });
        double squareSum = std::inner_product(delta.begin(), delta.end(), delta.begin(), 0.0);
        double stdDeviation = std::sqrt(squareSum / loArray.size());
        if (std::abs(newVal - mean) > STANDARD_DEVIATION_FILTER * stdDeviation)
        {
            return false;
        }
        hiArray.insert(hiArray.begin(), newVal);
        if(loArray.size() >= SAMPLE_SIZE)
        {
            loArray.pop_back();
        }
        return true;
    }
};

void readSensorSet(Zone* zone);
double getTemp(uint8_t addr[8]);

Zone tempSensor[]{
    {"Top", {0x28,0xFF,0x0D,0x4C,0x05,0x16,0x03,0xC7}, {0x28,0xFF,0x25,0x1A,0x01,0x16,0x04,0xCD}}, 
    {"Mid", {0x28,0xFF,0x89,0x19,0x01,0x16,0x04,0x57}, {0x28,0xFF,0x21,0x9F,0x61,0x15,0x03,0xF9}}, 
    {"Bot", {0x28,0xFF,0x16,0x6B,0x00,0x16,0x03,0x08}, {0x28,0xFF,0x90,0xA2,0x00,0x16,0x04,0x76}}
};

Zone* Top = &tempSensor[0];
Zone* Middle = &tempSensor[1];
Zone* Bottom = &tempSensor[2];

Zone* sensorSet[] = {Top, Middle, Bottom};

double Qtot;

const int pinOneWire = D2;
const int pinLED = D7;

DS18B20 ds18b20(pinOneWire);

void setup() 
{
  Serial.begin(9600);
  
  for (auto set : tempSensor)
  {
      Particle.variable(String(set.sensorName) + "H", set.hiTemp);
      Particle.variable(String(set.sensorName) + "L", set.loTemp);
  }
  Particle.variable("ECO-Qtot", Qtot);
}

void loop() 
{
  double accumulatedEnergy = 0;
  for (auto& set : tempSensor)
  {
      int attempts = 0;
      double newTemp;
      do {
          newTemp = getTemp(set.hiSensor);
          attempts++;
      } while (!set.addHiTemp(newTemp) and (attempts < 5));
      set.hiTemp = set.hiArray[0];
      
      attempts = 0;
      do{
          newTemp = getTemp(set.loSensor);
          attempts++;
      } while (!set.addLoTemp(newTemp) and (attempts < 5));
      set.loTemp = set.loArray[0];
      
      set.avgTemp = (set.hiTemp + set.loTemp) / 2.0;
      set.energy = (set.avgTemp - set.loTemp) * 110 * 1.163 / 1000;
      accumulatedEnergy += set.energy;
  }
  Qtot = accumulatedEnergy;
  delay(5000);
}

double getTemp(uint8_t addr[8]) 
{
    static const int MAXRETRY = 3;
    double _temp;
    int i = 0;

    do {
         _temp = ds18b20.getTemperature(addr);
    } while (!ds18b20.crcCheck() && MAXRETRY > i++);

    if (i < MAXRETRY) 
    {
        //celsius = _temp;
        //fahrenheit = ds18b20.convertToFahrenheit(_temp);
        Serial.println(_temp);
    }
    else 
    {
        _temp = NAN;
        //celsius = fahrenheit = NAN;
        Serial.println("Invalid reading");
    }
    return _temp;
}

I was too time constrained (lazy) to reduce the two member functions to one. :wink:


#147

OK @BulldogLowell, I have now seen also your Particle.variables, but I saw also that you use serial output:


  if (i < MAXRETRY)
    {
      // Serial.println(_temp); // For debugging only!
    }
    else
    {
      _temp = NAN;
      // Serial.println("Invalid reading"); // For debugging only!
    } 

I have removed those lines now and compiled/uploaded the sketch remotely.
The result was not as expected:

With @ric’s sketch I get this:

As the temperatures are not published, it seems not to work.


Also, the fact that you combined the sensors in pairs is clever in this particular application, but is something which will not always work for me:
Depending on the application, I want to be able to flexibly use any combination of 1, 2, 3, 4, 5, 6, 12, 20 … sensors and use as well the waterproof (12 bit DS18B20) type or the pcb-mount (8 bit DS18S20) type of sensors.

Remember one of my 2 important requirements:

  • Must allow MANY sensors (DS18B20 and DS18S20 versions mixed) on the same I/O pin (around 25…)

Also this mixing of types is easy with @ric 's sketch.

Due to this use of sensors in pairs, the calculation of the energy content in your sketch is wrong: You calculate the energy in only 3 of the 6 zones (you only calculate 3 averages of the H and L sensors). And this is difficult to change unless you change it completely and don’t combine sensors in pairs.

In @Ric 's sketch, the series of sensor variables is created independently and from then I am free to use them as needed.

@BulldogLowell, I just saw your endeavour during lunch time and I am grateful for your help, but I believe this is getting a bit too complicated for me…

This is the simple filtering method, ensuring the “continuity” of the data, I have in mind:

  • The current sketch updates the 6 temperatures every 5 seconds.
  • The water temperature changes so slowly that the difference between samples is very small. False readings are either much bigger or smaller.
  • Five sequential values are stored in an array, they are sorted, and then the middle value is picked and published as Particle.variable.
  • This means that the extremes will never be published.

As I mentioned earlier, you can see details here: LINK.

Let’s first see how @Ric can integrate his QSORT filtering in his current sketch.
You called this trivial, but I think it is probably a good way to achieve my goal.

The proof of the pudding will be the eating… :wink:


#148

If there is no Serial device attached, then the serial buffer will simply overflow, it won’t affect the program’s flow.

The values suggest that you haven’t validated the sensors. Did you confirm that the six addresses are indeed laid out correctly and change the pin to the proper pin for your one-wire bus? I changed the pin to D3 as in your code and posted below.

So if you exclude the bad readings, using CRC, my guess is that you are half way there. Like I said, I cannot test but thought I’d walk you along an OO path using @ScruffR 's library (CRC checking).

using other sensors is also quite easy… you know the hardware we just need to update the code to reflect those changes.

well, I guess I was demonstrating with a possible base hit, not a home run!

code with your one wire bus on D3:

#include <algorithm>
#include <vector>

#include <DS18B20.h>

#define SAMPLE_SIZE 25
#define STANDARD_DEVIATION_FILTER 1.5  // we will toss any value that isnt within this number of standard deviations of the past BUFFER_SIZE readings

struct Zone{
    char sensorName[4];
    uint8_t hiSensor[8]; // high sensor
    uint8_t loSensor[8];  // low sensor
    double hiTemp;
    double loTemp;
    double avgTemp;
    double energy;
    std::vector<double> hiArray;
    std::vector<double> loArray;
    
    bool addHiTemp(double newVal)
    {
        if (hiArray.size() < 10)
        {
            hiArray.push_back(newVal);
            return true;
        }
        double sum = std::accumulate(hiArray.begin(), hiArray.end(), 0.0);
        double mean = sum / hiArray.size();
        std::vector<double> delta(hiArray.size());
        std::transform(hiArray.begin(), hiArray.end(), delta.begin(), [mean](double x) { return x - mean; });
        double squareSum = std::inner_product(delta.begin(), delta.end(), delta.begin(), 0.0);
        double stdDeviation = std::sqrt(squareSum / hiArray.size());
        if (std::abs(newVal - mean) > STANDARD_DEVIATION_FILTER * stdDeviation)
        {
            return false;
        }
        hiArray.insert(hiArray.begin(), newVal);
        if(hiArray.size() >= SAMPLE_SIZE)
        {
            hiArray.pop_back();
        }
        return true;
    }
    bool addLoTemp(double newVal)
    {
        if (loArray.size() < 10)
        {
            loArray.push_back(newVal);
            return true;
        }
        double sum = std::accumulate(loArray.begin(), loArray.end(), 0.0);
        double mean = sum / loArray.size();
        std::vector<double> delta(loArray.size());
        std::transform(loArray.begin(), loArray.end(), delta.begin(), [mean](double x) { return x - mean; });
        double squareSum = std::inner_product(delta.begin(), delta.end(), delta.begin(), 0.0);
        double stdDeviation = std::sqrt(squareSum / loArray.size());
        if (std::abs(newVal - mean) > STANDARD_DEVIATION_FILTER * stdDeviation)
        {
            return false;
        }
        hiArray.insert(hiArray.begin(), newVal);
        if(loArray.size() >= SAMPLE_SIZE)
        {
            loArray.pop_back();
        }
        return true;
    }
};

void readSensorSet(Zone* zone);
double getTemp(uint8_t addr[8]);

Zone tempSensor[]{
    {"Top", {0x28,0xFF,0x0D,0x4C,0x05,0x16,0x03,0xC7}, {0x28,0xFF,0x25,0x1A,0x01,0x16,0x04,0xCD}}, 
    {"Mid", {0x28,0xFF,0x89,0x19,0x01,0x16,0x04,0x57}, {0x28,0xFF,0x21,0x9F,0x61,0x15,0x03,0xF9}}, 
    {"Bot", {0x28,0xFF,0x16,0x6B,0x00,0x16,0x03,0x08}, {0x28,0xFF,0x90,0xA2,0x00,0x16,0x04,0x76}}
};

Zone* Top = &tempSensor[0];
Zone* Middle = &tempSensor[1];
Zone* Bottom = &tempSensor[2];

Zone* sensorSet[] = {Top, Middle, Bottom};

double Qtot;

const int pinOneWire = D3;
const int pinLED = D7;

DS18B20 ds18b20(pinOneWire);

void setup() 
{
  Serial.begin(9600);
  
  for (auto set : tempSensor)
  {
      Particle.variable(String(set.sensorName) + "H", set.hiTemp);
      Particle.variable(String(set.sensorName) + "L", set.loTemp);
  }
  Particle.variable("ECO-Qtot", Qtot);
}

void loop() 
{
  double accumulatedEnergy = 0;
  for (auto& set : tempSensor)
  {
      int attempts = 0;
      double newTemp;
      do {
          newTemp = getTemp(set.hiSensor);
          attempts++;
      } while (!set.addHiTemp(newTemp) and (attempts < 5));
      set.hiTemp = set.hiArray[0];
      
      attempts = 0;
      do{
          newTemp = getTemp(set.loSensor);
          attempts++;
      } while (!set.addLoTemp(newTemp) and (attempts < 5));
      set.loTemp = set.loArray[0];
      
      set.avgTemp = (set.hiTemp + set.loTemp) / 2.0;
      set.energy = (set.avgTemp - set.loTemp) * 110 * 1.163 / 1000;
      accumulatedEnergy += set.energy;
  }
  Qtot = accumulatedEnergy;
  delay(5000);
}

double getTemp(uint8_t addr[8]) 
{
    static const int MAXRETRY = 3;
    double _temp;
    int i = 0;

    do {
         _temp = ds18b20.getTemperature(addr);
    } while (!ds18b20.crcCheck() && MAXRETRY > i++);

    if (i < MAXRETRY) 
    {
        //celsius = _temp;
        //fahrenheit = ds18b20.convertToFahrenheit(_temp);
        Serial.println(_temp);
    }
    else 
    {
        _temp = NAN;
        //celsius = fahrenheit = NAN;
        Serial.println("Invalid reading");
    }
    return _temp;
}

#149

also, I have yet to (ever) make a program and it worked the first time out! :cry:

one day, I suppose that could happen if I make all the right mistakes.


#150

I did validate all 6 sensors
I did change the pin to D3


#151

Oh, I see…

I guess I’ll wait for @ScruffR to chime in on the library configuration… I may have called the wrong constructor for your sensors!

Meanwhile I modified the functions to get a bigger sample size, there may be a problem wtihthe maths.


    bool addLoTemp(double newVal)
    {
        if (loArray.size() < 10) //<both functions modified for a bigger sample size before the maths kick in.
        {
            loArray.push_back(newVal);
            return true;
        }

#152

I caught one error… new code with the revision commented inside

#include <algorithm>
#include <vector>

#include <DS18B20.h>

#define SAMPLE_SIZE 25
#define STANDARD_DEVIATION_FILTER 1.5  // we will toss any value that isnt within this number of standard deviations of the past BUFFER_SIZE readings

struct Zone{
    char sensorName[4];
    uint8_t hiSensor[8]; // high sensor
    uint8_t loSensor[8];  // low sensor
    double hiTemp;
    double loTemp;
    double avgTemp;
    double energy;
    std::vector<double> hiArray;
    std::vector<double> loArray;
    
    bool addHiTemp(double newVal)
    {
        if (hiArray.size() < 10)
        {
            hiArray.push_back(newVal);
            return true;
        }
        double sum = std::accumulate(hiArray.begin(), hiArray.end(), 0.0);
        double mean = sum / hiArray.size();
        std::vector<double> delta(hiArray.size());
        std::transform(hiArray.begin(), hiArray.end(), delta.begin(), [mean](double x) { return x - mean; });
        double squareSum = std::inner_product(delta.begin(), delta.end(), delta.begin(), 0.0);
        double stdDeviation = std::sqrt(squareSum / hiArray.size());
        if (std::abs(newVal - mean) > STANDARD_DEVIATION_FILTER * stdDeviation)
        {
            return false;
        }
        hiArray.insert(hiArray.begin(), newVal);
        if(hiArray.size() >= SAMPLE_SIZE)
        {
            hiArray.pop_back();
        }
        return true;
    }
    bool addLoTemp(double newVal)
    {
        if (loArray.size() < 10)
        {
            loArray.push_back(newVal);
            return true;
        }
        double sum = std::accumulate(loArray.begin(), loArray.end(), 0.0);
        double mean = sum / loArray.size();
        std::vector<double> delta(loArray.size());
        std::transform(loArray.begin(), loArray.end(), delta.begin(), [mean](double x) { return x - mean; });
        double squareSum = std::inner_product(delta.begin(), delta.end(), delta.begin(), 0.0);
        double stdDeviation = std::sqrt(squareSum / loArray.size());
        // if (std::abs(newVal - mean) > STANDARD_DEVIATION_FILTER * stdDeviation)
        // {
        //     return false;
        // }
        hiArray.insert(hiArray.begin(), newVal);
        if(loArray.size() >= SAMPLE_SIZE)
        {
            loArray.pop_back();
        }
        return true;
    }
};

void readSensorSet(Zone* zone);
double getTemp(uint8_t addr[8]);

Zone tempSensor[]{
    {"Top", {0x28,0xFF,0x0D,0x4C,0x05,0x16,0x03,0xC7}, {0x28,0xFF,0x25,0x1A,0x01,0x16,0x04,0xCD}}, 
    {"Mid", {0x28,0xFF,0x89,0x19,0x01,0x16,0x04,0x57}, {0x28,0xFF,0x21,0x9F,0x61,0x15,0x03,0xF9}}, 
    {"Bot", {0x28,0xFF,0x16,0x6B,0x00,0x16,0x03,0x08}, {0x28,0xFF,0x90,0xA2,0x00,0x16,0x04,0x76}}
};

Zone* Top = &tempSensor[0];
Zone* Middle = &tempSensor[1];
Zone* Bottom = &tempSensor[2];

Zone* sensorSet[] = {Top, Middle, Bottom};

double Qtot;

const int pinOneWire = D3;
const int pinLED = D7;

DS18B20 ds18b20(pinOneWire);

void setup() 
{
  Serial.begin(9600);
  
//   for (auto set : tempSensor)
//   {
//       Particle.variable(String(set.sensorName) + "H", set.hiTemp);
//       Particle.variable(String(set.sensorName) + "L", set.loTemp);
//   }
  for (int i = 0; i < sizeof(tempSensor)/sizeof(tempSensor[0]); i++)
  {
      Particle.variable(String(tempSensor[i].sensorName) + "H", tempSensor[i].hiTemp);
      Particle.variable(String(tempSensor[i].sensorName) + "L", tempSensor[i].loTemp);
  }
  Particle.variable("ECO-Qtot", Qtot);
}

void loop() 
{
  double accumulatedEnergy = 0;
  for (auto& set : tempSensor)
  {
      int attempts = 0;
      double newTemp;
      do {
          newTemp = getTemp(set.hiSensor);
          attempts++;
      } while (!set.addHiTemp(newTemp) and (attempts < 5));
      set.hiTemp = set.hiArray[0];
      
      attempts = 0;
      do{
          newTemp = getTemp(set.loSensor);
          attempts++;
      } while (!set.addLoTemp(newTemp) and (attempts < 5));
      set.loTemp = set.loArray[0];
      
      set.avgTemp = (set.hiTemp + set.loTemp) / 2.0;
      set.energy = (set.avgTemp - set.loTemp) * 110 * 1.163 / 1000;
      accumulatedEnergy += set.energy;
  }
  Qtot = accumulatedEnergy;
  delay(5000);
}

double getTemp(uint8_t addr[8]) 
{
    static const int MAXRETRY = 3;
    double _temp;
    int i = 0;

    do {
         _temp = ds18b20.getTemperature(addr);
    } while (!ds18b20.crcCheck() && MAXRETRY > i++);

    if (i < MAXRETRY) 
    {
        //celsius = _temp;
        //fahrenheit = ds18b20.convertToFahrenheit(_temp);
        Serial.println(_temp);
    }
    else 
    {
        _temp = NAN;
        //celsius = fahrenheit = NAN;
        Serial.println("Invalid reading");
    }
    return _temp;
}

#153

@BulldogLowell, are you all set with the error you found already?


#154

yeah, thanks!

I PMed the latest to @FiDel so maybe he can try it. He brought up a good point that (while his above sketch isn’t using the feature) it can handle two types of Dallas sensors… I wasn’t sure if your Library could support both. Perhaps you can look at the code he posted above.

:slight_smile:

the latest code I gave him created a band of limits to sort bad data:

#include <algorithm>
#include <vector>

#include <DS18B20.h>

#define SAMPLE_SIZE 25 
//#define USING_STANDARD_DEVIATION  true

#ifdef USING_STANDARD_DEVIATION
  #define STANDARD_DEVIATION_FILTER 2.0  // we will toss any value that isnt within this number of standard deviations of the past BUFFER_SIZE readings
#else
  #define RANGE_BASED_FILTER 10.0  // total band 
#endif

struct Zone {
  char sensorName[4];
  uint8_t hiSensor[8]; // high sensor
  uint8_t loSensor[8];  // low sensor
  double hiTemp;
  double loTemp;
  double avgTemp;
  double energy;
  std::vector<double> hiArray;
  std::vector<double> loArray;

  bool addHiTemp(double newVal)
  {
    if (hiArray.size() < 10)
    {
      hiArray.push_back(newVal);
      return true;
    }
    double sum = std::accumulate(hiArray.begin(), hiArray.end(), 0.0);
    double mean = sum / hiArray.size();
    
#ifdef USING_STANDARD_DEVIATION
    std::vector<double> delta(hiArray.size());
    std::transform(hiArray.begin(), hiArray.end(), delta.begin(), [mean](double x) {
      return x - mean;
    });
    double squareSum = std::inner_product(delta.begin(), delta.end(), delta.begin(), 0.0);
    double stdDeviation = std::sqrt(squareSum / hiArray.size());
    if (std::abs(newVal - mean) > STANDARD_DEVIATION_FILTER * stdDeviation)
    {
      return false;
    }
#else
    if (std::abs(newVal - mean) > RANGE_BASED_FILTER / 2.0)
    {
        return false;
    }
#endif

    hiArray.insert(hiArray.begin(), newVal);
    if (hiArray.size() >= SAMPLE_SIZE)
    {
      hiArray.pop_back();
    }
    return true;
  }
  bool addLoTemp(double newVal)
  {
    if (loArray.size() < 10)
    {
      loArray.push_back(newVal);
      return true;
    }
    double sum = std::accumulate(loArray.begin(), loArray.end(), 0.0);
    double mean = sum / loArray.size();
    
#ifdef USING_STANDARD_DEVIATION
    std::vector<double> delta(loArray.size());
    std::transform(loArray.begin(), loArray.end(), delta.begin(), [mean](double x) {
      return x - mean;
    });
    double squareSum = std::inner_product(delta.begin(), delta.end(), delta.begin(), 0.0);
    double stdDeviation = std::sqrt(squareSum / loArray.size());
    if (std::abs(newVal - mean) > STANDARD_DEVIATION_FILTER * stdDeviation)
    {
        return false;
    }
#else
    if (std::abs(newVal - mean) > RANGE_BASED_FILTER / 2.0)
    {
        return false;
    }
#endif

    loArray.insert(loArray.begin(), newVal);
    if (loArray.size() >= SAMPLE_SIZE)
    {
      loArray.pop_back();
    }
    return true;
  }
};

void readSensorSet(Zone* zone);
double getTemp(uint8_t addr[8]);

Zone tempSensor[] {
  {"Top", {0x28, 0xFF, 0x0D, 0x4C, 0x05, 0x16, 0x03, 0xC7}, {0x28, 0xFF, 0x25, 0x1A, 0x01, 0x16, 0x04, 0xCD}},
  {"Mid", {0x28, 0xFF, 0x89, 0x19, 0x01, 0x16, 0x04, 0x57}, {0x28, 0xFF, 0x21, 0x9F, 0x61, 0x15, 0x03, 0xF9}},
  {"Bot", {0x28, 0xFF, 0x16, 0x6B, 0x00, 0x16, 0x03, 0x08}, {0x28, 0xFF, 0x90, 0xA2, 0x00, 0x16, 0x04, 0x76}}
};

Zone* Top = &tempSensor[0];
Zone* Middle = &tempSensor[1];
Zone* Bottom = &tempSensor[2];

Zone* sensorSet[] = {Top, Middle, Bottom};

double Qtot;
double Tmin = 35;

const int pinOneWire = D3;
const int pinLED = D7;

DS18B20 ds18b20(pinOneWire);

void setup()
{
  Serial.begin(9600);
  
  for (int i = 0; i < sizeof(tempSensor) / sizeof(tempSensor[0]); i++)
  {
    Particle.variable(String(tempSensor[i].sensorName) + "H", tempSensor[i].hiTemp);
    Particle.variable(String(tempSensor[i].sensorName) + "L", tempSensor[i].loTemp);
  }
  Particle.variable("ECO-Qtot", Qtot);
}

void loop()
{
  for (auto& set : tempSensor)
  {
    int attempts = 0;
    double newTemp;
    do {
      newTemp = getTemp(set.hiSensor);
      attempts++;
    } while (!set.addHiTemp(newTemp) and (attempts < 5));
    set.hiTemp = set.hiArray[0];

    attempts = 0;
    do {
      newTemp = getTemp(set.loSensor);
      attempts++;
    } while (!set.addLoTemp(newTemp) and (attempts < 5));
    set.loTemp = set.loArray[0];
    set.avgTemp = (set.hiTemp + set.loTemp) / 2.0;
  }
  
  double Av1, Av2, Av3, Av4, Av5;
  Av1 = (Top->hiTemp + Top->loTemp) / 2.0;
  Av2 = (Top->loTemp + Middle->hiTemp) / 2.0;
  Av3 = (Middle->hiTemp + Middle->loTemp) / 2.0;
  Av4 = (Middle->loTemp + Bottom->hiTemp) / 2.0;
  Av5 = (Bottom->hiTemp + Bottom->loTemp) / 2.0;

  Av1 = (Av1 - Tmin) * 110 * 1.163 / 1000;
  Av2 = (Av2 - Tmin) * 90 * 1.163 / 1000;
  Av3 = (Av3 - Tmin) * 90 * 1.163 / 1000;
  Av4 = (Av4 - Tmin) * 90 * 1.163 / 1000;
  Av5 = (Av5 - Tmin) * 110 * 1.163 / 1000;

  Qtot = Av1 + Av2 + Av3 + Av4 + Av5;

  delay(5000);
}

double getTemp(uint8_t addr[8])
{
  static const int MAXRETRY = 3;
  double _temp;
  int i = 0;

  do {
    _temp = ds18b20.getTemperature(addr);
  } while (!ds18b20.crcCheck() && MAXRETRY > i++);

  if (i < MAXRETRY)
  {
    //celsius = _temp;
    //fahrenheit = ds18b20.convertToFahrenheit(_temp);
    Serial.println(_temp);
  }
  else
  {
    _temp = NAN;
    //celsius = fahrenheit = NAN;
    Serial.println("Invalid reading");
  }
  return _temp;
}


#155

I’m not sure if this particular type is actually supported, since I can’t test without such a sensor, but I know the lib does read out some info about sensor type (as part of the address).

byte DS18B20::getChipType(uint8_t addr[8]) {
  return addr[0];
}

char* DS18B20::getChipName(uint8_t addr[8]) {
  char szName[MAX_NAME];

  switch (addr[0]) {
    case 0x10:  sprintf(szName, "DS18S20"); break;
    case 0x28:  sprintf(szName, "DS18B20"); break;
    case 0x22:  sprintf(szName, "DS1822"); break;
    default:  sprintf(szName, "Unknown"); break;
  }

  return szName;
}

#156

If you could point me towards an example that uses individual units working on different pins, or provide a brief summary of how to do it, I’d appreciate it. I currently have it working on one pin using oneWire.h … My application requires two temperature sensors. One for indoor temperature and one for the outdoor temperature. Thanks!