How do you properly handle external interrupts?
How do you properly handle external interrupts?
I'm trying to understand how external interrupt request should be handled. I own an ESP-01 whose GPIO2 is connected to ground through a button switch. What I'm trying to achieve here is run a callback function when I press the button.
Here's what I've written so far:
[code]import micropython
from machine import Pin
from time import sleep
micropython.alloc_emergency_exception_buf(100)
def call_this(change):
print(change)
p2 = Pin(2, Pin.IN)
p2.irq(trigger=Pin.IRQ_FALLING, handler=call_this)
while 1:
pass
[/code]
However, the callback above is called only the first time I press the button, if I try more than once nothing happens.
What is wrong?
Here's what I've written so far:
[code]import micropython
from machine import Pin
from time import sleep
micropython.alloc_emergency_exception_buf(100)
def call_this(change):
print(change)
p2 = Pin(2, Pin.IN)
p2.irq(trigger=Pin.IRQ_FALLING, handler=call_this)
while 1:
pass
[/code]
However, the callback above is called only the first time I press the button, if I try more than once nothing happens.
What is wrong?
Last edited by Rf3w8 on Fri Feb 24, 2017 10:19 am, edited 1 time in total.
Re: How do you properly handle external interrupts?
I was just dabbling with this the other day so my incomplete but working example may help. I would also recommend reading dhyland's post at viewtopic.php?p=17995#p17995
Code: Select all
import machine
import micropython
import network
from time import sleep, sleep_ms, ticks_ms, ticks_diff
button = machine.Pin(14, machine.Pin.IN)
led = machine.Pin(4, machine.Pin.OUT)
ap = network.WLAN(network.AP_IF)
ap.active(False)
time_stamp = ticks_ms()
micropython.alloc_emergency_exception_buf(100)
# Bad example since enable_irq is being set in this every time
# We would want to have enable_irq triggered by a.. IRQ via Timer?
def callback_rising(p):
state = machine.disable_irq()
global time_stamp
current_time = ticks_ms()
if ticks_diff(current_time, time_stamp) >= 500:
print('rising edge detected')
led.value(not led.value())
time_stamp = current_time
machine.enable_irq(state)
button.irq(trigger=machine.Pin.IRQ_RISING, handler=callback_rising)
while True:
sleep_ms(1000)
Re: How do you properly handle external interrupts?
Thank you, I'll test your code and see how it does!
I'm aware of bouncing issues but right now I'm focusing on getting the basics work. I imagine the switch off -> on of the IRQ is done to avoid bouncing..
Do you know if the IRQ is turned off each time we enter the callback? I was thinking my code is not working because maybe the IRQ is turned off each time you enter the callback..
I'm aware of bouncing issues but right now I'm focusing on getting the basics work. I imagine the switch off -> on of the IRQ is done to avoid bouncing..
Do you know if the IRQ is turned off each time we enter the callback? I was thinking my code is not working because maybe the IRQ is turned off each time you enter the callback..
Re: How do you properly handle external interrupts?
Your code has the same issue as mine, BUT... I was finally able to make it work properly. The needed fix to my code was to set up pin 2 as pull_up explicitly rather than leave it on None (default value).
Here's the updated code:
In particular, this was the line that was fixed:
Note that this IRQ has bouncing issues, so probably an approach as the one used by tknp should be adopted.
Hope it can help other users who are starting with esp-01!
Here's the updated code:
Code: Select all
import micropython
import machine
from time import sleep
micropython.alloc_emergency_exception_buf(100)
def call_this(change):
state = machine.disable_irq()
print(change)
machine.enable_irq(state)
p2 = machine.Pin(2, machine.Pin.IN, machine.Pin.PULL_UP)
p2.irq(trigger=machine.Pin.IRQ_FALLING, handler=call_this)
while 1:
print("P2 value {}".format(p2.value()))
sleep(1)
Code: Select all
p2 = machine.Pin(2, machine.Pin.IN, machine.Pin.PULL_UP)
Hope it can help other users who are starting with esp-01!
Re: How do you properly handle external interrupts?
Here's My first script! It sends a MQTT signal when a button is pressed. It's set up to have 3 buttons, the button callback only sets a global variable. Then the main loop checks if that variable is set and sends the command. This means that slow network code stays out of the callback, makes sure the MQTT publishing sends one command at a time, and has a 1 sec pause between sending messages to account for bounce and avoid flooding the broker.
Code: Select all
import time
import ubinascii
import machine
from simple import MQTTClient
from machine import Pin
#some dummy variables that are set by the callback, and read by the main loop
pinSetUp = 0 # pin D3, pin 0
pinSetDown = 0 # pin D2, pin 4
pinSetToggle = 0 # pin D4, pin 2
# this is called when a button is pressed
# it sets a global variable to 1 to indicate a button has been activated
def callbackUp(p):
#print('pin changed', p)
global pinSetUp
pinSetUp= 1
print("button up pressed")
def callbackDn(p):
#print('pin changed', p)
global pinSetDown
pinSetDown = 1
print("button down pressed")
def callbackTog(p):
#print('pin changed', p)
global pinSetToggle
pinSetToggle = 1
print("button toggle pressed")
#setup the pins connected to the buttons
pUp = Pin(0, Pin.IN)
pDn = Pin(4, Pin.IN)
pTog = Pin(2, Pin.IN)
pUp.irq(trigger=Pin.IRQ_RISING, handler=callbackUp)
pDn.irq(trigger=Pin.IRQ_RISING, handler=callbackDn)
pTog.irq(trigger=Pin.IRQ_RISING, handler=callbackTog)
# Default MQTT server to connect to
SERVER = b"192.168.1.1"
CLIENT_ID = b"bedroomButton"
TOPIC = b"home/bedroom/button"
# Main loop checks the dummy variables to see if the button has been pressed
def mainLoop(server=SERVER):
c = MQTTClient(CLIENT_ID, server, port=1883, user=b"USER", password=b"PW")
c.connect()
print("Connected to %s, waiting for button presses" % server)
c.publish(TOPIC, b"Bedroom button connected")
global pinSetUp, pinSetDown, pinSetToggle
while True:
while True:
if pinSetUp == 1:
print("sending up pressed")
c.publish(TOPIC, b"up")
time.sleep_ms(1000) # enforces a 1 sec pause after sending command, eliminates bounce
pinSetUp = 0
if pinSetDown == 1:
print("sending down pressed")
c.publish(TOPIC, b"down")
time.sleep_ms(1000)
pinSetDown = 0
if pinSetToggle == 1:
print("sending toggle pressed")
c.publish(TOPIC, b"toggle")
time.sleep_ms(1000)
pinSetToggle = 0
time.sleep_ms(80)
c.disconnect()
time.sleep_ms(5000) #required pause after boot before trying to connect to MQTT broker
mainLoop()
- pythoncoder
- Posts: 5956
- Joined: Fri Jul 18, 2014 8:01 am
- Location: UK
- Contact:
Re: How do you properly handle external interrupts?
I appreciate the use of interrupts might be as a learning exercise. But in this context it's worth bearing in mind that the purpose of an interrupt is to cater for situations where hardware needs to be serviced in a short period of time; where "short" might be measured in microseconds.
Where a response time of the order of one second is involved it's valid to poll the state of the buttons in the main loop.
Where a response time of the order of one second is involved it's valid to poll the state of the buttons in the main loop.
Peter Hinch
Index to my micropython libraries.
Index to my micropython libraries.
Re: How do you properly handle external interrupts?
Is it a bad idea an approach like the one proposed by mr_exit if you need to handle http request? I'd do the same honestly..pythoncoder wrote:I appreciate the use of interrupts might be as a learning exercise. But in this context it's worth bearing in mind that the purpose of an interrupt is to cater for situations where hardware needs to be serviced in a short period of time; where "short" might be measured in microseconds.
Where a response time of the order of one second is involved it's valid to poll the state of the buttons in the main loop.
Isn't continuous polling power expensive?
- pythoncoder
- Posts: 5956
- Joined: Fri Jul 18, 2014 8:01 am
- Location: UK
- Contact:
Re: How do you properly handle external interrupts?
On any platform with a low power sleep mode you could poll at (say) 100ms intervals using such a mode for the 100ms delays. This should be very nearly as power efficient as using a hardware interrupt.
I'm not saying there's anything wrong with the interrupt approach - merely outlining alternatives.
I'm not saying there's anything wrong with the interrupt approach - merely outlining alternatives.
Peter Hinch
Index to my micropython libraries.
Index to my micropython libraries.
Re: How do you properly handle external interrupts?
Thank you!pythoncoder wrote:On any platform with a low power sleep mode you could poll at (say) 100ms intervals using such a mode for the 100ms delays. This should be very nearly as power efficient as using a hardware interrupt.
I'm not saying there's anything wrong with the interrupt approach - merely outlining alternatives.
I'm very interested in possible alternatives as I'd like to build a battery powered "wifi remote", so I need a super low power consumption.
Would a polling solution like the following work fine?
Code: Select all
while True:
# Check if GPIO(0 or 2) is high/low
# Act accordingly
time.sleep_ms(100)
- pythoncoder
- Posts: 5956
- Joined: Fri Jul 18, 2014 8:01 am
- Location: UK
- Contact:
Re: How do you properly handle external interrupts?
I suggest you look at the ESP8266 low power modes e.g. the esp library http://docs.micropython.org/en/latest/e ... y/esp.html and search this forum to see what others have done. It seems that you set the sleep mode and everything works (with code such as you've outlined) auto-magically but I haven't tried this in practice.
Peter Hinch
Index to my micropython libraries.
Index to my micropython libraries.