-
• #2
At first glance, you are overwriting temp each time through the loop, instead of summing it as I think you mean to do. Well-chosen variable names could help you here.
I would recommend that you only sum together your readings in the for loop, then get the average after the loop finishes and then calculate the temp based on the average, as this could avoid some roundoff error, but it may not matter too much because the sensor isn't very accurate.
The analogRead() has 10 bits of accuracy, so it should return a number between 0 and 1023 which I don't see you compensating for.
So I think your calculations would go something like this:
Ravg = Rsum / numSamples // where Ravg is the average of your Readings. Note that Ravg will be a number between 0 and 1023
Vavg = (Ravg * E.getAnalogVRef()) / 1023 // This will be the average reading in Volts
MVavg = Vavg * 1000mV / 1V // convert to millivolts
Tavg = (MVavg - 400mV) * (1 degree Centigrade) / (19.5 mV) // shift the curve downward and convert to centigrade
This would make much more sense on a blackboard as it is just basically calculation by keeping track of units or whatever its called. The code obviously would be simpler since you won't be needing to multiply or divide by 1, etc.
Your Fahrenheit conversion looks fine. No promises that I haven't goofed up somewhere :)
Keep us posted on progress, thanks!P.S. Google says that one guy factored the internal resistance into his calculations, I don't know if E.getAnalogVRef() does that. If not and you did want to factor that in, you would make a change like this:
Vavg = Ravg * (E.getAnalogVRef() - Vir) / 1023
where Vir is the voltage drop from internal resistance, in voltsP.P.S. I guess they call it the factor-label method of dimensional analysis - LOL!
-
• #3
In regards to your comment about how accurate the temp sensor, can you please recommend another sensor? I will most likely need an already manufactured product that is waterproof since the sensor will be calculating water temperature.
-
• #4
Sorry, didn't mean to imply your sensor is bad at all, it just might not be accurate enough to worry about the roundoff error is all I meant. Perhaps 1 or 2 degrees Centigrade is perfect for your application, I have no idea. What is your application? What is your temperature range? What sort of accuracy can you live with?
-
• #5
No worries :-) My water temperature can range between 18 to 26 degrees Celsius and I am not requiring absolute accurate temps either. As long as my tenth place digit doesn't bounce around I can deal with how accurate the sensor is.
-
• #6
Two minutes ago my jaw dropped when you said tenth place digit; I assumed you meant better than +/- 0.1 degrees centigrade accuracy. Re-reading it, I was hoping you actually meant +/- 1.0 degrees centigrade. The reason my jaw dropped is because the research I had done into this prior to today showed that +/- 0.25 degrees centigrade accuracy was really stretching it. However...
However!
However! You caused me to look again. And here is what I found! And at a VERY reasonable price for the accuracy too. So, I thank you for your question, you helped me to find something I am interested in too.
BTW, did you try any changes to your code?
Is this for an aquarium? 18 is pretty cool for tropicals...Edit: There are some with different characteristics here, but all +/- 0.1 degree Centigrade accuracy, and some of them are cheaper!
P.S. But for such an accurate sensor, you are going to want more accuracy in your analogRead(), you might want a 16-bit DAC, here are the through-holes I could find, some are reasonably priced.
And here are some 24-bit DACs, they aren't through-hole, but the price isn't too bad. BTW, I've never tried anything like this yet, so it's likely there are things I haven't considered... -
• #7
There will always be noise in analog readings. Taking a number of readings and calculating the average is an often used method. But it can still take a large number of readings to get a stable display.
A better way is to take a number of readings and throw away the highest and lowest values and average the rest. Perhaps someone has even better methods to share.Take n readings and save the data in an array.
Sort the readings from lowest to highest.
Throw away the two highest and the two lowest readings.
Calculate the sum of the remaining values in the array and divide by the number of data points to get the average.Run the code by typing in "MedianRead(n,Pin);" to capture five readings from pin A1,
or type in "MedianRead(10,A0);" to capture ten readings from pin A0. Use a minimum of five samples. The original samples will be printed, then the remaining and sorted samples, and finally the result. ( a return statement should be added).var n=5; var Pin=A1; function CompareForSort(a,b) { if (a == b) return 0; if (a < b) return -1; else return 1; } function MedianRead(n,Pin) { var myarr = []; for(i=0; i<n; i++){ myarr[i] = analogRead(Pin); } console.log(myarr); myarr.sort(CompareForSort); myarr.splice(n-2,2); myarr.splice(0,2); console.log(myarr); m=myarr.length; var sum=myarr.reduce(function(a,b) { return a+b; }); console.log(sum/m); }
-
• #8
@Manxome Hi,
Datasheet can be found at: http://www.atlas-scientific.com/product_pages/probes/env-tmp.html? I haven't tried the code yet, but will (hopefully tonight). I am packing and getting ready to move by next weekend. I'm keeping my project unpacked until the last minute :). As soon as I test your code and @tage code I will reply back.Taking a second look at my code, inside the switch I should of wrote:
temp += tempinc; And temp += tempinf
-
• #10
thanks Manxome, I will have to try the shorter version later.
Meanwhile, here is my code for reading temperature from a home made temp sensor.
I soldered an NTC resistor (size 0603) onto a small piece of circuit board, added wires and mounted it inside a stainless ring terminal of the type you crimp onto a wire, using epoxy. One of the two wires connect to GND, the other one to A1, with a 10k pullup to 3.3V.
I run the macro by typing "getTemp(A1);"function getTemp(pin){ var n=10; var val=medianRead(n,pin); var ohms=10000*val/(1-val); var A=0.00088607485; var B=0.00025169965; var C=0.00000019152452; var W=Math.log(ohms); var temp=1/(A+W*(B+C*W*W))-273.15; return temp.toFixed(3); } function CompareForSort(a,b) { if(a==b) return 0; if(a<b) return -1; else return 1; } function medianRead(n,pin){ var myarr = []; for (i=0;i<n;i++){ myarr[i]=analogRead(pin); } myarr.sort(CompareForSort); myarr.splice(n-2,2); myarr.splice(0,2); m=myarr.length; var sum = myarr.reduce(function(a,b) { return a+b; }); return sum/m; }
-
• #11
I see you are using the Steinhart-Hart equation, very nice!
I follow what your code does except could you explain this equation please?var ohms=10000*val/(1-val);
It seems like there should be a factor of 1023 in there somewhere (see here) and maybe something for the voltage too (see here), but perhaps I just don't understand it all (haven't actually tried it myself yet).
Currently, val is some number between 0 and 1023. Lets see what happens to val/(1-val) for some example values:
val :: val/(1-val) 5 :: 5/(-4) == -1.25 100 :: 100/(-99) == -1.01 300 :: 300/(-299) =~ -1 500 :: 500/(-499) =~ -1 1000 :: 1000/(-999) =~ -1
I think you see that something must be wrong here. I am thinking val needs to be normalized to a fraction of 1, ie, something like val/1023.
Also, I am curious how you went about calculating A, B, and C, ie, how did you stabilize and accurately measure the 3 temperature points as well as the 3 resistance values at those temperatures?
Thanks!
-
• #12
The expression will be different for Espruino vs Arduino because analogRead() gives a reading between 0 and 1.
If you have a resistive divider connected to the ADC input and the top resistor is 10k and the NTC resistor is the bottom resistor in the divider, the ADC reading (val) will be R/(10k+R). Where R is the resistance of the NTC. From this simple expression you derive the expression
ohms=10000*val/(1-val),
(ohms in this case is the same as R in the first expression).so by taking a reading we can calculate the resistance of the NTC resistor. then it is relatively straightforward to use the Steinhart-Hart equation to figure out the temperature.
I don't remember exactly how I got the coefficients, it was something I did about a year ago. I probably found some online calculator. I can look into that tomorrow. The NTC resistor I use has a characteristic as shown in this table
-20 68.915
-15 54.166
-10 42.889
-5 34.196
0 27.445
5 22.165
10 18.010
15 14.720
20 12.099
25 10.000
30 8.309
35 6.939
40 5.824
45 4.911
50 4.160
55 3.539
60 3.024
65 2.593
70 2.233
75 1.929
80 1.673You could try this online calculator, and plug in three values from the table.
http://www.thinksrs.com/downloads/programs/Therm%20Calc/NTCCalibrator/NTCcalculator.htm -
• #13
Makes much more sense now, thanks! Don't know how I munged that analogRead(), I think I looked at the ref here real quick and saw the 1023 and assumed it was like Arduino LOL. Thanks for the calculator link, very cool.
-
• #14
@tage Hi, I implemented your example code and wow, my readings have stabilized quite nicely. I also tweaked the code to get degrees Celsius and degrees Fahrenheit. My original code was returning a temperature of about 80 F, but that was with only 20 samples. Now I am taking 200 samples every 3 seconds and the temp using the new code is at approximately
Temp: 77.86076511596 Fixed Temp: 77.9
which I understand is due to usingtoFixed(1)
The more samples that I take the 'more accurate' my readings become. I wonder how many samples I can take before I notice a lag between readings.@Manxome Thank you for your code recommendations. Accuracy for the temp sensor is:
± 1°CI checked my room thermostat and it says 77 F and my temp sensor says 78.1 so it's 'not off by much'. The sensor is encapsulated in epoxy and a stainless steal tube which is probably the cause of the 1 degrees difference.
Code:
function CompareForSort(a,b) { if (a == b) return 0; if (a < b) return -1; else return 1; } function MedianRead(n, tempType) { var myarr = []; for(i=0; i<n; i++){ var vOut = E.getAnalogVRef() * analogRead(A0); // if you attached VOUT to Ao var vZero = 0.4; var tCoeff = 19.5 / 1000; var tempinc = (vOut - vZero) / tCoeff; var tempinf = tempinc * (9 / 5) + 32; switch(tempType) { case 'c': myarr[i] = tempinc; break; case 'f': myarr[i] = tempinf; break; default: myarr[i] = tempinc; } } //console.log(myarr); myarr.sort(CompareForSort); myarr.splice(n-2,2); myarr.splice(0,2); //console.log(myarr); m=myarr.length; var sum=myarr.reduce(function(a,b) { return a+b; }); var temp = sum/m; var tempFixed = temp.toFixed(1); console.log("Temp: " + temp + " Fixed Temp: " + tempFixed); console.log(tempFixed); } var ph = new Sensor("ph", 0x63); var phDisplay = new s7s("ph", 0x71); var resTempDisplay = new s7s("res", 0x40); setInterval(function (e) { //var t = getResTempValue(100, 'C'); MedianRead(200, 'f'); //resTempDisplay.sendValue(t, "c"); //console.log(t); }, 3000);
-
• #15
Datasheet says "fast read time < 1mSec", so I am guessing you could theoretically get over 1000 samples per second. Not sure how fast that sort would be though, plus you are doing way more work than needed inside the loop.
function AverageRead(n) { var sum = 0; for(i=0;i<n; i++){sum += analogRead(A0); } var avg = sum/n; var vZero = 0.4; var tCoeff = 19.5 / 1000; var tempinc = (E.getAnalogVRef()*avg - vZero)/tCoeff; var tempinf = tempinc * (9 / 5) + 32; return { 'C':tempinc, 'F':tempinf}; }
I know this doesn't throw away the 2 highest/lowest outliers like you were doing, but if you printed them, I bet they wouldn't be far from the average, especially if you are taking so many samples anyhow. But it does allow you to get rid of the big array and the sorting and you could easily do as many samples as you wanted now without eating up so much memory and you could do it just as fast now. But try it both ways and see how far apart the readings are :)
-
• #17
There are many ways to filter out noise. Here is a test that I did comparing the readings from Espruino with a system multimeter 34401A. I made the same test with three different settings of the filter, in the one with 21 samples where I only keep one single reading (the median) you can clearly see the effect of the 12bit resolution. I think the performance of the Espruino ADC is quite good even in this very noisy test setup (and using long wires to connect the Espruino to the switching converter I am working on). Using 100 samples the worst error was 20mV- with a measurement range of 68V that is not bad.
1 Attachment
-
• #18
@tage, I am trying to understand your graphs. Referring to just the top graph for a sec, does each red dot (reading) represent 100 samples, winnowed down to 80 as you described, then averaged? And you took one such reading, then increased the load by 100mA, and then took another, and so on?
Why do you think the readings of the top two graphs are almost exclusively above the blue line?
Almost seems like there is some accumulated positive error which drives the readings upwards...Also, why do you think the readings on the bottom graph are often repeated two, three, even four times in a row for successively higher loads? I mean, you are throwing away 20 points, yet the remaining point just happens to be the same for successive readings? My guess is that you are only getting a few distinct readings (maybe 3-4 or fewer?), ie, the readings are pretty tightly grouped. Oh, and those successive readings...are almost exclusively above the blue line!
Thanks!
Oh, btw, I would be interested in a side-by-side comparison graph where no data was thrown out for each of the 3 sample sizes :)
-
• #19
Manxome, you interpreted the graphs correctly. Each red dot is the result of capturing 100, 40 or 21 samples and crunching the readings into one single result.
I start at zero load, take a series of readings, save the result after filtering and increase the load by about 100mA, then repeat.
The blue dots are single readings from the 34401A multimeter. it contains its own filter so there is no need to do any processing on those readings. But this is a $700 instrument so one would expect accuracy.
The measurements are done by Excel. I wrote a VBA macro that controls the instruments and the Espruino.
The reason why the red line is above the blue line is that there is a small gain error. I need to adjust the program to correct. There is also a small offset error.
The reason why the voltage is drooping as load is increased is that there is a long cable from the bench power supply to the test object where I am measuring the input voltage. In this measurement, the output voltage is set at 15V, so the output power at 4A load is 60W. The test object is a buck-boost converter that handles input voltages to 60V (80V max). The output voltage of this particular setup is 12V to 60V but it can be much wider. It is also possible to control power flow in the reverse direction. I am using the Espruino in my test setup to generate signals that control the power converter. It also measures input and output current and voltage plus a number of other signals. And that is where I needed to filter out noise, as the power converter can handle 500W or more and this means a lot of noise on measurement signals.
I will see if I can capture some data without filtering so we can see the noise. I did not have time today. The reason why the last curve shows three samples with same value, then the next sample is 16.6mV lower, then comes four more samples that are 2*16.6mV lower is that the 12 bit resolution shows up. when you only have one sample and no averaging the value must always be n * 16.6mV. -
• #20
@tage, thanks for the very thorough explanation; makes sense! I am jealous of both your equipment and your knowledge :)
BTW, if you don't get to the extra graphs, no problem. But I really am curious to see if the tossed values would have actually affected the results had they been left in. In other words, I wonder if the equipment is so accurate that there might really only be one or two bad values in those samples, and the sheer number of samples diminishes their contribution.
-
• #21
Last noob questions: Have you considered using an external 16 or even 24-bit DAC? Would it not provide more accuracy? Would the added complexity make it not worth it? Seems like they can be had for about $3-$6 in quantity 1.
-
• #22
Manxome, I will generate a data file with readings so it is possible to apply different filtering algorithms and see the effect. I think that the noise can be very different in different applications.
The noise from switching regulators can be especially hard to filter as it contains both very large spikes and very high frequencies and also lower frequency ripple that contains the 100kHz or so basic frequency, plus perhaps other lower frequencies that come from load current variations etc. An algorithm that works well for a switcher application may be overkill if you only want a steady reading from a temperature sensor.
Some background to my experiments:
A good quality ADC with 16 bits or higher is definitely much better if measurement accuracy is important. The project I am working on right now will result in a demo board that demonstrates the characteristics of a power converter. The power converter can deliver output voltage that is lower than, equal to or higher than the input voltage. And it can run backwards. So I need a micro controller to make it possible to generate a number of control signals that control the power converter and to keep track of what is happening. Accuracy is not necessarily the top priority for this project. But it is nice to have steady voltage and current readings to display.
A few examples: the output voltage and current limit should be adjustable, operating temperature needs to be monitored so cooling fans can be controlled, different operating modes must be selectable - such as making the converter charge a battery or super capacitor, and then reverse the power flow and use the stored energy to power a load when the main power source fails.
Some people want to use the power converter with a solar panel as a power source to charge a battery. Other want to connect many power converters together to handle very large power levels. These functions can all be handled with analog and discrete logic, but a micro controller makes it possible to create a very flexible demo board that can do a lot of different tricks using the same circuit board, and without requiring hours of soldering and building prototype circuitry for each different configuration.
This is where I am using the Espruino. It allows me to quickly write demo programs without having to invest the time in C programming. Before a final product is made based on the demo board testing, a specific micro controller is usually selected by each customer, and it is programmed in C. But not by me. I just want to show that the power converter works and quickly write some code to make it do its tricks. I am a power converter guy and not a programmer. -
• #23
@Manxome,
I tested both yours and tage code. Using tage code I seem to receive more of a steady temp reading with very few temperature jumps. With your example I seem to receive a lot of temp fluctuations. For example, 73.0, 73.2, 73.3, 73.4, 73.5 and 73.7. I am taking 300 readings with both yours and tage code.
I'm still struggling to get MCP9701 temp sensor to work efficiently. I believe my calculations are off or possibly the leads on the sensor are too long causing interference and unreliable readings. My goal is to get my temp readings stable and return either the temp in degrees C or degrees F which will be chosen by setting a 'c' or 'f' in the functions arguments.
Also, I am attempting to stabilize the readings by using a for loop to return the average temp. I recently changed my code around to include a switch statement to return the temp in degrees C or degrees F which is where I think I did something wrong.
code: