Welcome to the Pycom forum Forum
    • \[\[global:header.categories\]\]
    • \[\[global:header.recent\]\]
    • \[\[global:header.tags\]\]
    • \[\[global:header.popular\]\]
    • Login

    Explore Pybytes | Official Documentation | Report a Firmware Bug/Issue | GitHub

    NTC temperature sensor

    Scheduled Pinned Locked Moved LoPy
    sensorslopyadctemperaturentc
    26 Posts 6 Posters 15.0k Views 6 Watching
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • T Offline
      tobru
      last edited by

      I have a 10K NTC temperature sensor and I'm a bit lost how I can use it with my LoPy. How do I connect it? How do I read the temperature from it? I just know that I most probably need to use the adc functionality, but thats all.

      Thanks for any hint!

      L 1 Reply Last reply Reply Quote 0
      • L Offline
        livius @tobru
        last edited by livius

        @tobru said in NTC temperature sensor:

        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](0_1486232865306_upload-a895ee6f-3065-4b8a-ba98-d1eee4b6cdc2 image url)

        here you have pinout of lopy
        https://www.pycom.io/wp-content/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
        alt text

        think about people first, then computers

        1 Reply Last reply Reply Quote 1
        • T Offline
          tobru
          last edited by tobru

          Thanks a lot for the explanation. But I don't get it yet =(

          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?

          1 Reply Last reply Reply Quote 0
          • T Offline
            tobru
            last edited by

            Ok, looks like the 10K resistor doesn't fit here with 3V3. Nevertheless I'm a bit lost and not sure how to chose the correct resistor and calculate the temperature from the measured value.

            S 1 Reply Last reply Reply Quote 0
            • S Offline
              slavik73
              last edited by

              "The 10k resitor" should be the same value as your termistor resistance (it can be different than 10k, I used the one with 50k resistance). This schematic worked for me with esp8266 (on LUA), so I don't think it have a problem with 3.3V

              1 Reply Last reply Reply Quote 0
              • S Offline
                Shaun @tobru
                last edited by Shaun

                @tobru The problem is that the ADC is saturating.
                Try change the ADC input attenuation, which will increase the ADC full scale conversion voltage.
                attn = (0 = 0db : 1.1V max), (1 = 2.5db : 1.4V max), (2 = 6db : 1.9V max), (3 = 11db : 3.2V max)
                apin=adc.channel(attn=3, pin='P16') # should work for you

                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.

                T 1 Reply Last reply Reply Quote 2
                • T Offline
                  tobru @Shaun
                  last edited by

                  @Shaun said in NTC temperature sensor:

                  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.

                  T 1 Reply Last reply Reply Quote 0
                  • T Offline
                    tobru @tobru
                    last edited by

                    @tobru said in NTC temperature sensor:

                    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.

                    1 Reply Last reply Reply Quote 0
                    • robert-hhR Offline
                      robert-hh
                      last edited by

                      At least you need a known mapping between the temperature and the NTC value. If you have a data sheet of that part, you'll find it there.
                      From the adc value you can calculate back the resistor value of the NTC, and with the mapping between resistor value and the temperature the temperature.
                      Assuming that the ADC's range is set to 3.2 V, and the Range is 12 bit, and the connection is as shown below,

                      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.

                      1 Reply Last reply Reply Quote 0
                      • S Offline
                        slavik73
                        last edited by

                        You can find some Arduino code here: https://github.com/adafruit/Thermistor-test/blob/master/thermistortest.pde. Hope you can convert it to Python. Note that our ADC full range is 4096, so you should use 4095 instead of 1023

                        T 1 Reply Last reply Reply Quote 0
                        • T Offline
                          tobru @slavik73
                          last edited by

                          I've tried to convert the arduino code to MicroPython, but I'm not sure if I've done this right (especially the calculation part):

                          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 3000-4000)
                          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/temperatursensor-ntc-mit-stahlkappe-p-42342.html

                          1 Reply Last reply Reply Quote 0
                          • robert-hhR Offline
                            robert-hh
                            last edited by robert-hh

                            Hello @tobru. I see a small glitch in you code. Line 32 should not be indented. It should read:

                            average /= NUMSAMPLES
                            without the indents, otherwise the division is done in every loop.
                            and line 38 has to read
                            resistance = SERIESRESISTOR * resistance

                            The Steinhart-Hart equation calculates Kelvin. I found another example here, which may be the same you were using: https://gist.github.com/100ideas/1119533.

                            1 Reply Last reply Reply Quote 0
                            • S Offline
                              slavik73
                              last edited by slavik73

                              I also think the problem was with average. I did not used your latest line (celsius = ((steinhart - 32) / 1.8) - 1) in my code and I think I got adequate results, but I didn't check it without thermistor connected. As of line 38 - it is actualy "/", not "*" - it make 1/x

                              1 Reply Last reply Reply Quote 0
                              • T Offline
                                tobru
                                last edited by

                                Line 32 should not be indented

                                Ouch. This of course should not have happened! Thanks a lot to @robert-hh 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?

                                S J 2 Replies Last reply Reply Quote 0
                                • S Offline
                                  slavik73 @tobru
                                  last edited by

                                  @tobru I think the problem could be also caused by non-exactly-accurate assumption about an ADC range. Even if we assume it's a 3.2V, it's still not the same as the one you are giving to thermister. I think it could cause some non-linear behavior. (In arduino I think they are using external Vref in this mode.)

                                  1 Reply Last reply Reply Quote 0
                                  • J Offline
                                    jmarcelino @tobru
                                    last edited by jmarcelino

                                    @tobru
                                    There is some known non-linearity in the ESP32 ADC which is not (yet) being compensated on the ESP-IDF (and thus neither in micropython)

                                    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.

                                    1 Reply Last reply Reply Quote 0
                                    • robert-hhR Offline
                                      robert-hh
                                      last edited by robert-hh

                                      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)
                                      And yes, there were quite a few notes about the ADC's poor performance. If you need more precise values, you should compensate at least for the actual range.

                                      T 1 Reply Last reply Reply Quote 0
                                      • T Offline
                                        tobru @robert-hh
                                        last edited by

                                        @robert-hh said in NTC temperature sensor:

                                        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.

                                        1 Reply Last reply Reply Quote 0
                                        • robert-hhR Offline
                                          robert-hh
                                          last edited by robert-hh

                                          I think it should. If you NTC is connected between Vcc and ADC, this line:

                                          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

                                          T 1 Reply Last reply Reply Quote 0
                                          • T Offline
                                            tobru @robert-hh
                                            last edited by

                                            @robert-hh said in NTC temperature sensor:

                                            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.

                                            1 Reply Last reply Reply Quote 0

                                            Hello! It looks like you're interested in this conversation, but you don't have an account yet.

                                            Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.

                                            With your input, this post could be even better 💗

                                            Register Login
                                            • 1
                                            • 2
                                            • 1 / 2
                                            • First post
                                              Last post

                                            Pycom on Twitter

                                            Pycom