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.
#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.