Thanks for any hint!
]]>Thanks for any hint!
]]>10K NTC
this is simple analog sensor  thermistor
connect it to any analog pin  here you have sample for arduino
but sample is valid for lopy  only difference is that you connect it to 3V3 pin not to 5V pin because pycom board are 3V3 compatibile and not tollerant to 5V
![alt text]( image url)
here you have pinout of lopy
https://www.pycom.io/wpcontent/uploads/2016/11/lopy_pinout.pdf
and here you have how to program it
https://docs.pycom.io/pycom_esp32/pycom_esp32/tutorial/includes/adc.html
here you have sample scaling table
I connected 3V3 to the thermistor on one end, G3 (P16) and the 10k resistor to the other end of the termistor. GND is connected to the 10k resistor (other end).
10K Thermistor
++ ++
   
 3V3 ++ ++
  ++ 
  
 G3 ++
  
  ++ 
    
 GND ++ ++
++ ++
10K Resistor
All I get is the value 4095 when I read it with the following code:
import machine
import time
adc = machine.ADC()
apin = adc.channel(pin='P16')
for i in range(10):
val = apin()
print(val)
time.sleep(1)
What did I understand wrong?
]]>Edit:
Be warned that the ESP32 ADC is nonlinear and rather noisy.
To reduce the jitter in readings, I take 5 readings at 100us cadence, discard the highest and lowest and average the remaining 3.
The non linearity has to be dealt with using a lookup table or conversion function for now.
]]>apin=adc.channel(attn=3, pin='P16') # should work for you
Great, this changes the bevahiour and looks much better now. Where do I find documentation about this? The official documentation about ADC doesn't talk f.e. about the attn
parameter.
Where do I find documentation about this?
I answer myself: Here in the forum under LoPy ADCs. Next step: How to calculate the temperature from the measured value. Any hints appreciated.
]]>R_NTC = R1 * (ADC_FULL_RANGE/adc_reading  1)
R1 = 10000, and ADC_FULL_RANGE = 4096
Line said below, you might take the average of a few readings, and you might have to interpolate between values not in your mapping table.
]]>import machine
import time
import math
# which analog pin to connect
THERMISTORPIN = 'P16'
# resistance at 25 degrees C
THERMISTORNOMINAL = 10000
# temp. for nominal resistance (almost always 25 C)
TEMPERATURENOMINAL = 25
# how many samples to take and average, more takes longer
# but is more 'smooth'
NUMSAMPLES = 5
# The beta coefficient of the thermistor (usually 30004000)
BCOEFFICIENT = 3950
# the value of the 'other' resistor
SERIESRESISTOR = 10000
# take N samples in a row, with a slight delay
adc = machine.ADC(0)
adcread = adc.channel(attn=3, pin=THERMISTORPIN)
samples = [0.0]*NUMSAMPLES
for i in range(NUMSAMPLES):
samples[i] = adcread()
print("sample %u: %u" %(i, samples[i]))
time.sleep(1)
# average all the samples out
average = 0
for i in range(NUMSAMPLES):
average += samples[i]
average /= NUMSAMPLES
print("average sample: %u" %average)
# convert the value to resistance
resistance = 4095 / average  1
resistance = SERIESRESISTOR / resistance
print("resistance: %u" %resistance)
steinhart = resistance / THERMISTORNOMINAL # (R/Ro)
steinhart = math.log(steinhart) # ln(R/Ro)
steinhart /= BCOEFFICIENT # 1/B * ln(R/Ro)
steinhart += 1.0 / (TEMPERATURENOMINAL + 273.15) # + (1/To)
steinhart = 1.0 / steinhart # Invert
steinhart = 273.15 # convert to C
celsius = ((steinhart  32) / 1.8)  1
print("celsius: %u" %celsius)
To test it, I've connected a 10k resistor instead of the NTC thermistor. The output is:
sample 0: 1879
sample 1: 1857
sample 2: 1863
sample 3: 1878
sample 4: 1863
average sample: 466
resistance: 1284
celsius: 25
This looks good at first sight. But then I connected a 1k resistor instead of the NTC thermistor, this should output around 85c, but the output is this:
sample 0: 181
sample 1: 211
sample 2: 199
sample 3: 211
sample 4: 212
average sample: 52
resistance: 130
celsius: 75
The value isn't really correct. Any ideas what I'm doing wrong? I'm also wondering about the comment # convert to C
in the Steinhart equation, the output looks more like Farenheit to me, this is why I added the conversion to celsius (hopefully correct).
For the record: I'm using the following NTC thermistor: http://shop.boxtec.ch/temperatursensorntcmitstahlkappep42342.html
]]>average /= NUMSAMPLES
without the indents, otherwise the division is done in every loop.
and line 38 has to read
resistance = SERIESRESISTOR * resistance
The SteinhartHart equation calculates Kelvin. I found another example here, which may be the same you were using: https://gist.github.com/100ideas/1119533.
]]>Line 32 should not be indented
Ouch. This of course should not have happened! Thanks a lot to @roberthh for spoting this! Looks much better now... But: It seems that the measurement is not really accurate, connecting the 10k resistor instead of the NTC thermistor should give me a value around this (I know that it won't be 100% perfect), but it looks like this:
sample 0: 1827
sample 1: 1869
sample 2: 1823
sample 3: 1843
sample 4: 1845
average sample: 1841
resistance: 8170
the code:
import machine
import time
THERMISTORPIN = 'P16'
NUMSAMPLES = 5
SERIESRESISTOR = 10000
adc = machine.ADC(0)
adcread = adc.channel(attn=3, pin=THERMISTORPIN)
samples = [0.0]*NUMSAMPLES
for i in range(NUMSAMPLES):
samples[i] = adcread()
print("sample %u: %u" %(i, samples[i]))
time.sleep(1)
average = 0
for i in range(NUMSAMPLES):
average += samples[i]
average /= NUMSAMPLES
print("average sample: %u" %average)
resistance = SERIESRESISTOR / ((4095 / average)  1)
print("resistance: %u" %resistance)
Could it be that the ADC isn't very accurate? What can I do to get a more accurate result of the measurement? Or could it be that the equation for calculating the resistance is wrong?
]]>https://twitter.com/ba0sh1/status/815759185816129536/
For example up to 0.1v it registers 0.0. So at the least you need to account for that.
You may be able to compensate these in software. Personally I'd just use a much more accurate dedicated external ADC  connected over I2C for example  for NTC measurements. For example a ADS1115 breakout can be had for less than $2.
]]>I'm still thinking how you have connected the thermistor a) between Vcc and ADC (like sketched below) or b) between ADC and GND. From your code, it looḱs like b)
It's currently connected like this:
3V3
+


++

 10k resistor (instead of NTC)
++


++ P16


++

 10k resistor
++


+
GND
Do you think the code doesn't fit this scheme?
@jmarcelino said in NTC temperature sensor:
use a much more accurate dedicated external ADC  connected over I2C for example
I've ordered a Velleman MM112 external ADC with I2C interface, I can then compare the values.
]]>resistance = SERIESRESISTOR / ((4095 / average)  1)
should be:
resistance = SERIESRESISTOR * ((4095 / average)  1)
For instance:
at 50°C, the value of the NTC is 3588 Ohm. That should give an ADC reading of:
4095 * (10000/(3588 + 10000)) = 3014
Put that into your formula, and you get 27881 Ohm
Put that into the other formula, and you get 3587 Ohm.
Regards, Robert
resistance = SERIESRESISTOR * ((4095 / average)  1)
Yes, that looks better, I use the following test code now:
import machine
import time
THERMISTORPIN = 'P16'
NUMSAMPLES = 5
SERIESRESISTOR = 10000
adc = machine.ADC(0)
adcread = adc.channel(attn=3, pin=THERMISTORPIN)
samples = [0.0]*NUMSAMPLES
for i in range(NUMSAMPLES):
samples[i] = adcread()
print("sample %u: %u" %(i, samples[i]))
time.sleep(1)
average = 0
for i in range(NUMSAMPLES):
average += samples[i]
average /= NUMSAMPLES
print("average sample: %u" %average)
resistance = SERIESRESISTOR * ((4095 / average)  1)
print("resistance: %u" %resistance)
Vin = 3.2
buffer = average * Vin
Vout = buffer / 4095
buffer = (Vin / Vout)  1
R2 = 10000 * buffer
print("resistance2: %u" %R2)
and the output is:
sample 0: 1850
sample 1: 1836
sample 2: 1837
sample 3: 1845
sample 4: 1829
average sample: 1839
resistance: 12262
resistance2: 12262
Nevertheless, the ADC accuracy seems to be very bad so measuring the temperature this way seems useless. I'll try it with an external ADC as soon as it arrived.
]]>Another observation: You connect the NTC to 3.2 V (are you sure it's not 3.3V?). The ADC max range is at 3.6 V, the reading at 3.2 V would be 3631. Instead of 4095, you should therefore use 3631 as the virtual maximum value. Then, the resistor value is 9793 (10412 for 3.3 V), which is 2% off the nominal value, and depending of the precision class of the resistor that could be the real value.
]]>if you requirement for precision is not extreme (like with 1% error)
There is no exterme precision requirement. 1% would be completely fine.
a) the zero point. look at which input voltage the output is different to zero. According to a comment below, this is somewhat like 0.1 V
b) the scaling: Look at which input voltage the output just reaches the maximum value.
c) nonlinearity. You could make a compensation table, or ignore it, if it is small enough and outside the range of interest.
d) Noise
a) and b) is easily compensated for. d) is reduced with taking an average of many samples, like you did. You do not have to wait a second in between, and a small capacitor like 10 nF with short leads directly at the ADC helps.
Thanks for the inputs!
Another observation: You connect the NTC to 3.2 V (are you sure it's not 3.3V?).
It is connected to 3V3, so it is of course 3.3V. I've chosen 3.2V for Vin
in the second equation because of the parameter attn=3
. I've read that in this threads: https://forum.pycom.io/topic/226/lopyadcs and here https://forum.pycom.io/topic/602/ntctemperaturesensor/6
The ADC max range is at 3.6 V, the reading at 3.2 V would be 3631. Instead of 4095, you should therefore use 3631 as the virtual maximum value. Then, the resistor value is 9793 (10412 for 3.3 V), which is 2% off the nominal value, and depending of the precision class of the resistor that could be the real value.
Interesting. I must have missed this information in the documentation, I thought 3.3V is the maximum.
]]>Vrange = Vinput * (4095/reading)
]]>attn = 3
value 0 until 125 mV
107 mV > 0
200 mV > 95
300 mV > 215
500 mV > 455
700 mV > 700
1000 mV > 1070
1500 mV > 1700
1600 mV > 1815
2000 mV > 2310
2500 mV > 2930
2900 mV > 3540
3000 mV > 3740
3100 mV > 3955
3170 mV > 4095
attn = 0
Value 0 until 57 mV
100 mV > 127
200 mV > 550
300 mV > 974
500 mV > 1820
700 mV > 2650
900 mV > 3510
1000 mV > 3933
1044 mV > 4095
If you look at that in a graph, at attn 3 the bends up beyond 2.5 V, while the attn=0 graph looks more linear, besides the 0Offset.
That's worse than I expected. The best one can get is to work with attn = 0 and use an external voltage divider.
Edit: I found a link that shows an experience, which pretty much matches mine:
http://www.esp32.com/viewtopic.php?t=1045
The Figures I got are pretty much the same. So 6db attn would be an option too.
https://github.com/espressif/espidf/issues/164
https://github.com/espressif/arduinoesp32/issues/92
The official response from Espressif is:
The ADC frontend indeed has a nonlinear response. Once characterization data is ready, we will take it into account inside ADC APIs, and update the datasheet with correct values of INL/DNL and effective resolution.
So any efforts to compensate it at this stage will likely be rendered useless in a future update once the characterisation is complete (at least at ESPIDF/MicroPython level)
That's also why I suggested an external ADC for the time being, at least that won't change.
]]>