IRQ on Pin throwing TypeError: can't convert to int

Questions and discussion about The WiPy 1.0 board and CC3200 boards.
Target audience: Users with a WiPy 1.0 or CC3200 board.
User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: IRQ on Pin throwing TypeError: can't convert to int

Post by Roberthh » Wed Apr 20, 2016 5:27 am

I do not know if I understand your intention right, but if you want to access the symbols declared outside your handler, like raising_time, falling_time and delay, in the function changingEdge, you have to declare them global inside your handler. Add the statement:

Code: Select all

global raising_time, falling_time, delay 
to your function changingEdge. Otherwise these symbols are declared local to the function, and any changes to these would not be visible in the main loop.

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

Re: IRQ on Pin throwing TypeError: can't convert to int

Post by pythoncoder » Wed Apr 20, 2016 5:55 am

The best way to use interrupt handlers is to have variables accessible to both the handler and the main program. Typically the main program executes a loop which runs forever. The interrupt handler updates the variables and sets a flag to tell the main loop it has done so. The main loop reads the data and clears the flag. As a start I'd re-jig your test program to work that way, moving all the print statements into the main loop. The interrupt handler then becomes very simple indeed.

The flag and shared data can be global variables, but in programs of any complexity it's better to create a class. The shared data is in bound variables and the interrupt handler is a bound method. The latter "just works" with no special coding required.

If interrupts occur at a high rate you may need to think about the case where a subsequent interrupt occurs before the main loop has cleared the flag. If it's liable to occur and how you handle it depends on the application: sometimes all that's required is for the interrupt handler simply to do nothing if the flag is set on entry - i.e. just discard the sample.
Peter Hinch
Index to my micropython libraries.

Gabi
Posts: 18
Joined: Sat Mar 05, 2016 3:29 pm

Re: IRQ on Pin throwing TypeError: can't convert to int

Post by Gabi » Wed Apr 20, 2016 5:34 pm

pythoncoder wrote:The best way to use interrupt handlers is to have variables accessible to both the handler and the main program. Typically the main program executes a loop which runs forever. The interrupt handler updates the variables and sets a flag to tell the main loop it has done so. The main loop reads the data and clears the flag. As a start I'd re-jig your test program to work that way, moving all the print statements into the main loop. The interrupt handler then becomes very simple indeed.

The flag and shared data can be global variables, but in programs of any complexity it's better to create a class. The shared data is in bound variables and the interrupt handler is a bound method. The latter "just works" with no special coding required.

If interrupts occur at a high rate you may need to think about the case where a subsequent interrupt occurs before the main loop has cleared the flag. If it's liable to occur and how you handle it depends on the application: sometimes all that's required is for the interrupt handler simply to do nothing if the flag is set on entry - i.e. just discard the sample.
Thank you very much for this feedback.

I have updated my code accodingly:

Code: Select all

import time
import machine
from machine import Pin, RTC

RTC_USEC_POS = 6
RTC_SEC_POS = 5
RTC_MIN_POS = 4

MIN_TO_SEC = 60
SEC_TO_USEC = 1000

TRIGGER_TIME = 10

TRIGGER_PIN = 'GP11'
ECHO_PIN = 'GP12'

LOW = 0
HIGH = 1

class DistanceSensor:
	def __init__(self, triggerGPIO, echoGPIO):
		self.triggerPin = Pin(triggerGPIO, mode = Pin.OUT)
		self.echoPin = Pin(echoGPIO, mode = Pin.IN)
		self.rtc = machine.RTC()
		self.delay = 0

	def changingEdge(self, pin):
		print("Changing Edge ")
		print(self)
		print(pin)
		flags = pin.irq().flags()
		print(flags)

		if flags & Pin.IRQ_RISING:
			self.raising_time = self.rtc.now()
			print(self.raising_time)

		elif flags & Pin.IRQ_FALLING:
			self.falling_time = self.rtc.now()
			print(self.falling_time)

			ellapsed_minutes = self.falling_time[RTC_MIN_POS] - self.raising_time[RTC_MIN_POS]
			ellapsed_seconds = self.falling_time[RTC_SEC_POS] - self.raising_time[RTC_SEC_POS]
			ellapsed_useconds = self.falling_time[RTC_USEC_POS] - self.raising_time[RTC_USEC_POS]

			self.delay = ellapsed_minutes * MIN_TO_SEC * SEC_TO_USEC + ellapsed_seconds * SEC_TO_USEC + ellapsed_useconds
			print("Delay: %d" % self.delay)	

distanceSensor = DistanceSensor(TRIGGER_PIN, ECHO_PIN)

callback = distanceSensor.echoPin.irq(trigger = Pin.IRQ_RISING | Pin.IRQ_FALLING, handler = distanceSensor.changingEdge)
print(callback.flags())
callback()



while distanceSensor.delay==0:
	input("Press Enter to trigger...")
	distanceSensor.triggerPin.value(HIGH)
	time.sleep_us(TRIGGER_TIME)
	distanceSensor.triggerPin.value(LOW)
I will gladfully listen to any other feedback ;)

Also, the interruption is triggered by an external pheripheral (an HC-SR04 ultrasonic sensor) so the flag is supposed to be embedded in the pin's interruption (see the link I gave in my previous post) so I do not store it anywhere in my main, I only want to get it from the pin so I can know if, on the pin I am watching, there is a rising or a falling edge and so I could calculate the distance.

So, if you have any feedback on since, I would really appreciate it :)

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

Re: IRQ on Pin throwing TypeError: can't convert to int

Post by pythoncoder » Thu Apr 21, 2016 8:44 am

Peter Hinch
Index to my micropython libraries.

Gabi
Posts: 18
Joined: Sat Mar 05, 2016 3:29 pm

Re: IRQ on Pin throwing TypeError: can't convert to int

Post by Gabi » Thu Apr 21, 2016 7:40 pm

pythoncoder wrote:@Gabi the definitive guide is here http://docs.micropython.org/en/latest/p ... rules.html
Thank you for that :)

Well, I have decided to store the callback as a global variable in order to be able to get the IRQ flag to know whether the handler has been triggered by a RISING or a FALLING edge:

Code: Select all

import time
import machine
import micropython
from machine import Pin, RTC

micropython.alloc_emergency_exception_buf(100)

RTC_USEC_POS = 6
RTC_SEC_POS = 5
RTC_MIN_POS = 4

MIN_TO_SEC = 60
SEC_TO_USEC = 1000

TRIGGER_TIME = 100000

TRIGGER_PIN = 'GP11'
ECHO_PIN = 'GP12'

LOW = 0
HIGH = 1

class DistanceSensor:
	def __init__(self, triggerGPIO, echoGPIO):
		self.triggerPin = Pin(triggerGPIO, mode = Pin.OUT)
		self.echoPin = Pin(echoGPIO, mode = Pin.OUT)
		self.rtc = machine.RTC()
		self.delay = 0

	def getEchoPin(self):
		return self.echoPin

	def setEchoPin(self, newValue):
		self.echoPin.value(newValue)

	def changingEdge(self, pin):
		global callback
		print("Changing Edge ")
		print(callback)
		print(pin)
		flags = callback.flags()
		print(flags)
		print(Pin.IRQ_RISING)
		print(Pin.IRQ_FALLING)

		if flags & Pin.IRQ_RISING:
			self.raising_time = self.rtc.now()
			print(self.raising_time)

		elif flags & Pin.IRQ_FALLING:
			self.falling_time = self.rtc.now()
			print(self.falling_time)

			ellapsed_minutes = self.falling_time[RTC_MIN_POS] - self.raising_time[RTC_MIN_POS]
			ellapsed_seconds = self.falling_time[RTC_SEC_POS] - self.raising_time[RTC_SEC_POS]
			ellapsed_useconds = self.falling_time[RTC_USEC_POS] - self.raising_time[RTC_USEC_POS]

			self.delay = ellapsed_minutes * MIN_TO_SEC * SEC_TO_USEC + ellapsed_seconds * SEC_TO_USEC + ellapsed_useconds
			print("Delay: %d" % self.delay)	

distanceSensor = DistanceSensor(TRIGGER_PIN, ECHO_PIN)

callback = distanceSensor.echoPin.irq(trigger = Pin.IRQ_RISING | Pin.IRQ_FALLING, handler = distanceSensor.changingEdge)

(in my code id set the echoPin to OUT but later it will be IN to listen to a response from the sensor)

When I try to update manually the echoPin, nothing is triggered...does anyone have an suggestion about that?

I really want to thank you, @pythoncoder and @Roberthh, for your very precious help :D

Gabi
Posts: 18
Joined: Sat Mar 05, 2016 3:29 pm

Re: IRQ on Pin throwing TypeError: can't convert to int

Post by Gabi » Thu Apr 21, 2016 9:55 pm

Hi guys,
I have managed to complete my code and as it would not have been possible without your precious help, here is my code:

Code: Select all

import time
import machine
import micropython
from machine import Pin

micropython.alloc_emergency_exception_buf(100)

RTC_USEC_POS = 6
RTC_SEC_POS = 5
RTC_MIN_POS = 4

MIN_TO_SEC = 60
SEC_TO_USEC = 1000

TRIGGER_TIME = 10

TRIGGER_PIN = 'GP11'
ECHO_PIN = 'GP12'

LOW = 0
HIGH = 1

class DistanceSensor:
	def __init__(self, triggerGPIO, echoGPIO):
		self.triggerPin = Pin(triggerGPIO, mode = Pin.OUT)
		self.echoPin = Pin(echoGPIO, mode = Pin.IN)
		self.mm_decimal = ""
		self.mm = -1
		self.cm = -1

	def isDistanceCalculated(self):
		return self.mm != -1 & self.cm != -1

	def setTriggerPinValue(self, value):
		self.triggerPin.value(value)

	def getDistanceString(self):
		return str(self.cm) + "," + self.mm_decimal + str(self.mm) + "cm"
	def changingEdge(self, pin):
		global callback

		flags = callback.flags()
		if flags & Pin.IRQ_RISING:
			self.raising_time = time.ticks_us()

		elif flags & Pin.IRQ_FALLING:
			self.falling_time = time.ticks_us()
			delay = time.ticks_diff(self.raising_time, self.falling_time)
			#We use 17 instead of 0,017
			distance = delay * 17
			#We rescale the distance in cm by separating cm and mm
			self.cm = distance // 1000
			self.mm = distance % 1000
		
			#in case we have a distance like 49028
			# cm = 49
			# mm = 028 but the 0 would be discared so we check it
			if distance % 100 == distance % 1000:
				self.mm_decimal = "0"
		
distanceSensor = DistanceSensor(TRIGGER_PIN, ECHO_PIN)

callback = distanceSensor.echoPin.irq(trigger = Pin.IRQ_RISING | Pin.IRQ_FALLING, handler = distanceSensor.changingEdge)
distanceSensor.setTriggerPinValue(LOW)
time.sleep(1)

distanceSensor.setTriggerPinValue(HIGH)
time.sleep_us(TRIGGER_TIME)
distanceSensor.setTriggerPinValue(LOW)

while distanceSensor.isDistanceCalculated() == False:
	time.sleep_us(50000)
	pass

print(distanceSensor.getDistanceString())
So, I have removed the RTC because it was not possible to get the "now" inside the interruption, there is obviously writting something in the heap and as in interruption, the heap is locked, we can't use that.
Instead I used the timer.tick_us() method and it works like a charm ;)

In my loop, I am monitoring the end of the distance calculation (it means we had rising_edge then falling_edge) and to save some power, I sleep 0,05 second before checking again. As I don't need ultra-real time on the distance display (or whatever I'll do with it later), it is fine.

I have done a "toString"-like method named getDistanceString(). I used that name to be cleared when reading the code.

I will not bother you to explain the calculation of cm and mm, I think it is pretty clear. I used 17 instead of 0,017 (see https://tuixte.wordpress.com/2013/03/05 ... r-hc-sr04/ for reference about this number) as there is no float in micropython (yet? ^^) and then I had to "re-scale" the value in cm and mm.

If you have any remarks I would (once again) gladfully take them :)

Thanks again :D

Post Reply