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

    Time & Interrupts

    Scheduled Pinned Locked Moved MicroPython
    interrupttimesensors
    7 Posts 5 Posters 3.8k 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.
    • M Offline
      MaSePreetz Pybytes Beta
      last edited by

      Dear Developers,

      i am currently trying to develop a driver for the DHT11/DHT22 by using
      interrupt and callback handlers.
      The sensor sends its data by toggling the data pin from high to low.
      These flanks are already detected by my code, i can count them and all are
      detected.

      But i need to measure the time difference between the flanks, a 1
      has the length of approx. 130 us and a 0 of approx. 78 us.
      When executing the code, the recorded time stamps are all above 130 us,
      so they are unusable. Is there any possibility to speed things up here?
      I assume that the queuing of interrupts produces those long times.

      Here is the result of a measurement:

      [120.575, 256.6, 401.2, 547.225, 682.625, 829.225, 973.1, 1109.5, 1255.1, 1402.6, 1548.6, 1681.4, 1826.45, 1979.675, 2122.75, 2265.425, 2389.325, 2535.075, 2668.4, 2802.9, 2957.15, 3092.35, 3227.575, 3360.9, 3505.95, 3640.4, 3786.925, 3925.85, 4060.325, 4184.05, 4307.825, 4431.625, 4555.325, 4679.05, 4802.825, 4934.1, 5057.8, 5181.525, 5305.3, 5429.1, 5552.8, 5676.525]

      Here is the code of the handler:

      def handler(self, arg):
      	self._samples[self._samplesIndex] = self._chronometer.read_us()
      	# Increase the index if we are still within the bounds of the array	
      	if(self._samplesIndex < self._MAXSAMPLES - 1):
      		self._samplesIndex = self._samplesIndex + 1
      

      and here the code to register the handler:

      self._dataPin.callback(Pin.IRQ_FALLING, self._irqHandler.handler)
      

      Any ideas how to improve the situation?

      Many thanks in advance,
      Marc

      L robert-hhR 2 Replies Last reply Reply Quote 0
      • L Offline
        livius @MaSePreetz
        last edited by livius

        @MaSePreetz

        When executing the code, the recorded time stamps are all above 130 us,

        You register for IRQ_FALLING, then how you detect how long is 0 and how long is 1?

        For IRQ_FALLING you get info that it is changing from 1 to 0. and e.g. it stay in 0 for 50us and it raising to 1 and stay for 100us and then back you get IRQ_FALLING
        you count this as 150us but do not know how long is 0 and how long is 1.

        EDIT
        Ah i forgot that DHT send low 50us and variable length of high like 28 for 0 and 70 for 1
        then your concept is good - you should got 78us for 0 or 120us for 1

        But for dht better is to Pull pin
        https://github.com/adafruit/DHT-sensor-library/blob/master/DHT.cpp
        and i do not know how long your operation in handler take time?

        Try to measure it in e.g. 1000 loop - what you got?

        think about people first, then computers

        E 1 Reply Last reply Reply Quote 0
        • E Offline
          Erik
          last edited by

          Hi Marc,

          I've also tried to measure a DHT22's pulse length using an interrupt callbacks. Even with the smallest (and hopefully fastest) interrupt routine I got the same results as you did; I never measured a pulse length below 100 us.

          For what I did and the results see here: https://github.com/erikdelange/WiPy-2.0-DHT22

          In the end the only thing which worked for me was to use polling, and even then I had to temporarily disable interrupts to get a usable result.

          Regards
          Erik

          1 Reply Last reply Reply Quote 0
          • E Offline
            Emmanuel Goudot @livius
            last edited by

            Hello @livius ,
            with such interrupt handler:
            0_1496851136055_upload-3c17553f-7d68-4049-8975-35503897beba
            tested with
            0_1496851203728_upload-34cea6a6-2b1e-469e-b625-26b43073f32e
            I get such results:
            0_1496851278272_upload-f164b2cf-737d-49be-8e0b-0180a6750b68
            empty loop ~25µs, loop with hadler: ~85µs (handler alone: 65µs ?)
            deltas are differences of time.ticks_us() and minimum value I get is ~84µs

            Would it be possible to add value of timer inside the object passed to interrupt handler, or as second parameter ???

            BR,
            Emmanuel.

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

              @MaSePreetz There was a discussion about pin interrupt latency here: https://forum.pycom.io/topic/936/pin-interrupt-latency
              Unless newer version of the firmware improve things drastically, there is no chance to get data from the DHT11/DHT22 with an interrupt using driver. The only Python method that works for me is a busy polling method.

              E 1 Reply Last reply Reply Quote 0
              • E Offline
                Emmanuel Goudot @robert-hh
                last edited by

                Hello @robert-hh ,
                I guess you have succeeded working with DHT11/DHT22 ?
                Could you share python library (that work) ?
                Thanks a lot,
                Emmanuel.

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

                  @Emmanuel-Goudot Yes, that's the piece of code below, which works for my DHT22 on a Lopy. It is buried far below in a thread, where two people fight to get it running(https://forum.pycom.io/topic/1172/pure-python-library-for-reading-dht-sensor-on-pycom-boards). No clue what their problem is, since I consider it solved, even if not bullet proof.

                  import time
                  from machine import enable_irq, disable_irq,  Pin
                  
                  
                  class DTHResult:
                      'DHT sensor result returned by DHT.read() method'
                  
                      ERR_NO_ERROR = 0
                      ERR_MISSING_DATA = 1
                      ERR_CRC = 2
                  
                      error_code = ERR_NO_ERROR
                      temperature = -1
                      humidity = -1
                  
                      def __init__(self, error_code, temperature, humidity):
                  	self.error_code = error_code
                  	self.temperature = temperature
                  	self.humidity = humidity
                  
                      def is_valid(self):
                  	return self.error_code == DTHResult.ERR_NO_ERROR
                  
                  
                  class DTH:
                      'DHT sensor (dht11, dht21,dht22) reader class for Pycom'
                  
                      __pin = Pin('P3', mode=Pin.OPEN_DRAIN)
                      __dhttype = 0
                  
                      def __init__(self, pin, sensor=0):
                  	self.__pin = pin
                  	self.__pin(1)
                  	self.__dhttype = sensor
                  
                      def read(self):
                  	time.sleep(1)
                  
                  	# send initial high
                  	#self.__send_and_sleep(1, 25000)
                  
                  	# pull down to low
                  	#self.__send_and_sleep(0, 40000)
                  
                  	# collect data into an array
                  	data = self.__collect_input()
                  
                  	# parse lengths of all data pull up periods
                  	pull_up_lengths = self.__parse_data_pull_up_lengths(data)
                  	# if bit count mismatch, return error (4 byte data + 1 byte checksum)
                  	print(pull_up_lengths)
                  	if len(pull_up_lengths) != 40:
                  	    return DTHResult(DTHResult.ERR_MISSING_DATA, 0, 0)
                  
                  	# calculate bits from lengths of the pull up periods
                  	bits = self.__calculate_bits(pull_up_lengths)
                  
                  	# we have the bits, calculate bytes
                  	the_bytes = self.__bits_to_bytes(bits)
                  	print(the_bytes)
                  	# calculate checksum and check
                  	checksum = self.__calculate_checksum(the_bytes)
                  	if the_bytes[4] != checksum:
                  	    return DTHResult(DTHResult.ERR_CRC, 0, 0)
                  
                  	# ok, we have valid data, return it
                  	[int_rh, dec_rh, int_t, dec_t, csum] = the_bytes
                  	if self.__dhttype==0:		#dht11
                  		rh = int_rh		#dht11 20% ~ 90%
                  		t = int_t	#dht11 0..50°C
                  	else:			#dht21,dht22
                  		rh = ((int_rh * 256) + dec_rh)/10
                  		t = (((int_t & 0x7F) * 256) + dec_t)/10
                  		if (int_t & 0x80) > 0:
                  			t *= -1
                  	return DTHResult(DTHResult.ERR_NO_ERROR, t, rh)
                  
                      def __send_and_sleep(self, output, mysleep):
                  	self.__pin(output)
                  	time.sleep_us(mysleep)
                  
                      def __collect_input(self):
                  	# collect the data while unchanged found
                  	unchanged_count = 0
                  	# this is used to determine where is the end of the data
                  	max_unchanged_count = 100
                  	last = -1
                  	data = []
                  	m = [1]*500	   # needs long sample size to grab all the bits from the DHT
                  	self.__pin(0)
                  	time.sleep_us(20000)
                  	self.__pin(1)
                  	irqf = disable_irq()
                  	for i in range(len(m)):
                  	    m[i] = self.__pin()      ## sample input and store value
                  	enable_irq(irqf)
                  	for i in range(len(m)):
                  	    current = m[i]
                  	    data.append(current)
                  	    if last != current:
                  		unchanged_count = 0
                  		last = current
                  	    else:
                  		unchanged_count += 1
                  		if unchanged_count > max_unchanged_count:
                  		    break
                  	#print(data)
                  	return data
                  
                      def __parse_data_pull_up_lengths(self, data):
                  	STATE_INIT_PULL_DOWN = 1
                  	STATE_INIT_PULL_UP = 2
                  	STATE_DATA_FIRST_PULL_DOWN = 3
                  	STATE_DATA_PULL_UP = 4
                  	STATE_DATA_PULL_DOWN = 5
                  
                  	state = STATE_INIT_PULL_UP
                  
                  	lengths = [] # will contain the lengths of data pull up periods
                  	current_length = 0 # will contain the length of the previous period
                  
                  	for i in range(len(data)):
                  
                  	    current = data[i]
                  	    current_length += 1
                  
                  	    if state == STATE_INIT_PULL_DOWN:
                  		if current == 0:
                  		    # ok, we got the initial pull down
                  		    state = STATE_INIT_PULL_UP
                  		    continue
                  		else:
                  		    continue
                  	    if state == STATE_INIT_PULL_UP:
                  		if current == 1:
                  		    # ok, we got the initial pull up
                  		    state = STATE_DATA_FIRST_PULL_DOWN
                  		    continue
                  		else:
                  		    continue
                  	    if state == STATE_DATA_FIRST_PULL_DOWN:
                  		if current == 0:
                  		    # we have the initial pull down, the next will be the data pull up
                  		    state = STATE_DATA_PULL_UP
                  		    continue
                  		else:
                  		    continue
                  	    if state == STATE_DATA_PULL_UP:
                  		if current == 1:
                  		    # data pulled up, the length of this pull up will determine whether it is 0 or 1
                  		    current_length = 0
                  		    state = STATE_DATA_PULL_DOWN
                  		    continue
                  		else:
                  		    continue
                  	    if state == STATE_DATA_PULL_DOWN:
                  		if current == 0:
                  		    # pulled down, we store the length of the previous pull up period
                  		    lengths.append(current_length)
                  		    state = STATE_DATA_PULL_UP
                  		    continue
                  		else:
                  		    continue
                  
                  	return lengths
                  
                      def __calculate_bits(self, pull_up_lengths):
                  	# find shortest and longest period
                  	shortest_pull_up = 1000
                  	longest_pull_up = 0
                  
                  	for i in range(0, len(pull_up_lengths)):
                  	    length = pull_up_lengths[i]
                  	    if length < shortest_pull_up:
                  		shortest_pull_up = length
                  	    if length > longest_pull_up:
                  		longest_pull_up = length
                  
                  	# use the halfway to determine whether the period it is long or short
                  	halfway = shortest_pull_up + (longest_pull_up - shortest_pull_up) / 2
                  	bits = []
                  
                  	for i in range(0, len(pull_up_lengths)):
                  	    bit = False
                  	    if pull_up_lengths[i] > halfway:
                  		bit = True
                  	    bits.append(bit)
                  
                  	return bits
                  
                      def __bits_to_bytes(self, bits):
                  	the_bytes = []
                  	byte = 0
                  
                  	for i in range(0, len(bits)):
                  	    byte = byte << 1
                  	    if (bits[i]):
                  		byte = byte | 1
                  	    else:
                  		byte = byte | 0
                  	    if ((i + 1) % 8 == 0):
                  		the_bytes.append(byte)
                  		byte = 0
                  	#print(the_bytes)
                  	return the_bytes
                  
                      def __calculate_checksum(self, the_bytes):
                  	return the_bytes[0] + the_bytes[1] + the_bytes[2] + the_bytes[3] & 255
                  
                  
                  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 / 1
                  • First post
                    Last post

                  Pycom on Twitter

                  Pycom