interrupt callbacks and passing time variables

General discussions and questions abound development of code with MicroPython that is not hardware specific.
Target audience: MicroPython Users.
Post Reply
ikkeT
Posts: 17
Joined: Sat Mar 06, 2021 7:01 pm

interrupt callbacks and passing time variables

Post by ikkeT » Sat Mar 06, 2021 8:15 pm

Hi

I have been sketching a moped computer with my son. What I try to achieve is simple meter that displays speed, records distances and info about RPM. Later also info about different liquids like engine temp and oil and gas. I also will put limits to RPM and speed. I happen to have NodeMCU for the job, and wanted to learn micro python while doing it. I have basic python skills, nothing fancy.

So I am successfully running and detecting spark and hall sensor. Now I want to record times of those, and filter out the noise. At this point I'd like to do minimal stuff in interrupt while rising edge of pin happens. I want to increase a counter and compare time between this and previous interrupt, to filter out noise (debounce).

For this I was reading manuals, and understood I can use global vars of integers, bytearray or array. It kinda works with just increasing the index with global variable for it. But what is the size of time_ms() variable, if I'd use array to store those? How should it be done?

I could use array of few time values for counting average speed of interrupts, and I'd like to drop any value which is e.g. < 5ms from the previous interrupt as noise. Now I didn't figure out how to initialize such array for for storing times. What's the sizeof(time_ms)?

I tried with globals like this:

Code: Select all

micropython.alloc_emergency_exception_buf(100)

ARRAYSIZE = const(20)
data = array.array('H', 0 for x in range(ARRAYSIZE))
spark_index = 0
prev_spark_ms = 0
this_spark_ms = 0

def callback_spark(p):
    global spark_index, prev_spark_ms, this_spark_ms
    this_spark_ms = time.ticks_ms()

    # filter away any bounces under 12k rpm (< 5ms)
    if (time.ticks_diff(this_spark_ms, prev_spark_ms) > 4):
        spark_index += 1
        prev_spark_ms = this_spark_ms
Questions:

1. Is it OK to use time.tics_ms() in interrupt?
2. Is size of tick_ms enough to fit into integer?
3. If I'd use array like the data above, what should be the size for time_ms() entry there?

With this I currently get some memory allocation errors, so clearly something wrong there. I have a main loop which initializes the values and assigns the callback to pin. The main loop checks spark index has grown and does the calcs for RPM.

Happy to get guided a bit. Micro python seems fun, easy to explain this stuff to son with no coding experience. Thanks in advance!

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: interrupt callbacks and passing time variables

Post by Roberthh » Sat Mar 06, 2021 8:54 pm

The data size returned by ticks_ms() and ticks_us() is 31 bit. It fits in an array of 32 bit values, type "I".

P.S.: Because they are 31 bit only, the values can overflow. At least for ticks_us() that happens about every 17 minutes. An that's the reason why ticks_diff() should be used instead of simple difference. ticks_diff() returns always positive values.

ikkeT
Posts: 17
Joined: Sat Mar 06, 2021 7:01 pm

Re: interrupt callbacks and passing time variables

Post by ikkeT » Sat Mar 06, 2021 9:40 pm

Excellent, thanks! I'll give it a shot at morning. And yes, I already read about and use ticks_diff.

ikkeT
Posts: 17
Joined: Sat Mar 06, 2021 7:01 pm

Re: interrupt callbacks and passing time variables

Post by ikkeT » Sun Mar 07, 2021 6:04 pm

just reporting back here that the advice worked fine. I made an array:

Code: Select all

ARRAYSIZE = const(20)
sparks = array.array('i', 0 for x in range(ARRAYSIZE))
spark_write_index = -1
spark_read_index = 0
spark_count = 0
sparks_overflow = 0
sparks_disqualified = 0
and used it in my interrupt to fill the circular buffer:

Code: Select all

def callback_spark(p):
    global this_spark_ms, sparks, spark_count, spark_write_index, sparks_disqualified, sparks_overflow

    this_spark_ms = utime.ticks_ms()

    # first ever spark?
    if (spark_write_index == -1):
        spark_write_index = 0
        sparks[spark_write_index] = this_spark_ms
        spark_count += 1
    # filter away any bounces over 12k rpm (< 5ms)
    elif (utime.ticks_diff(this_spark_ms, sparks[spark_write_index]) > 4):
        if (spark_count <= ARRAYSIZE):
            spark_write_index = (spark_write_index + 1) % ARRAYSIZE
            sparks[spark_write_index] = this_spark_ms
            spark_count += 1
        else:
            sparks_overflow += 1
    else:
        sparks_disqualified += 1
And I get to read it from my loop:

Code: Select all

    spark.irq(trigger=Pin.IRQ_RISING, handler=callback_spark)

    while True:

        spark_intervals_ms = 0
        if (spark_count > 0):
            rounds = spark_count
            # read average of spark intervals
            while (spark_count):
                latest_spark_ms = sparks[spark_read_index]
                spark_read_index = (spark_read_index + 1) % ARRAYSIZE
                spark_count -= 1
                spark_intervals_ms += utime.ticks_diff(latest_spark_ms, prev_spark_ms)
                prev_spark_ms = latest_spark_ms
            avg_interval_ms = spark_intervals_ms / rounds
            # if more than 3 sparks, count rpm, if less, also utilise old rpm value
            if (rounds > 2):
                rpm = rounds * 60000 / avg_interval_ms
            else:
                rpm = (rpm * 2 + (rounds * 60000 / avg_interval_ms)) / 3
        else:
            no_sparks += 1
            rpm = 0
            avg_interval_ms = 1

        print("rpm: ", rpm, " rounds: ", rounds,
            " no_sparks:", no_sparks,
            " sparks_overflow: ", sparks_overflow,
            " sparks_disqualified: ", sparks_disqualified)

        display_rpm(rpm, oled, font_writer_20)
        utime.sleep_ms(300)
Hopefully someone will have re-use for it, seems to work fine for rpm calculations. Next to try hooking it up to moped, now just tested on a push button.
Last edited by ikkeT on Sun Mar 07, 2021 6:18 pm, edited 1 time in total.

ikkeT
Posts: 17
Joined: Sat Mar 06, 2021 7:01 pm

Re: interrupt callbacks and passing time variables

Post by ikkeT » Sun Mar 07, 2021 6:17 pm

One more thing as it's still related, even not within topic. Why can't I use pin pull up/down resistors if using pin as interrupt?

See how I had to comment out the resistor pull:

Code: Select all

    # spark = Pin(12, Pin.IN, Pin.PULL_DOWN)
    spark = Pin(12, Pin.IN)
as if I don't, it complains this:

Code: Select all

AttributeError: type object 'Pin' has no attribute 'PULL_DOWN'
Where as without hooking up the irq the pin with resistor pull works fine. The board is esp8266, nodemcu.

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: interrupt callbacks and passing time variables

Post by pythoncoder » Mon Mar 08, 2021 8:47 am

I think the ESP8266 does not have pull down resistors. The options made available by MicroPython are constrained by the physical hardware.
Peter Hinch
Index to my micropython libraries.

Post Reply