Error: KeyboardInterrupt leads to Exit code 143 when busy with analogRead

Hi,
I found that i cannot interrupt my code with ^C or the Stop button in Pycharm, if python is occupied with reading the Infrared Sensor. If i try to use this Keyboardinterrupt, the programm will keep running for a few more seconds, before exiting with exit code 143, and no further information. This is bad, because then the programm is not stopped nicely, and other functions go wrong.
I made a short example code to show my problem, shown below.

When the wait time is much longer, say 5 seconds, than you can be sure to interrupt during the waiting, which then works fine with exit code 0. But that is not possible for my programm, i want to be able to interrupt the analogRead.

import time
import grovepi


sensor = 0
grovepi.pinMode(sensor, "INPUT")
sensor_value = 0

try:
    while True:
        i = 0
        sensor_value_total = 0

        while i < 100:
            # Read sensor value
            sensor_value = grovepi.analogRead(sensor)
            sensor_value_total += sensor_value
            i += 1

        # Durchschnittswert ueber die letzten i Messwerte
        sensor_value_average = sensor_value_total / i
        print(sensor_value_average)

        time.sleep(0.1)
except KeyboardInterrupt:
    pass

print("nicely quit")

Now an example output:
ssh://pi@ipadres/usr/bin/python3.7 -u /home/pi/Documents/Python/Testprogram.py
292.1
291.4
291.4
292.1
^C293.1
292.0
^Cnicely quit

Process finished with exit code 0

Where you can see a ^C 2 seconds before I quit, this one thus didnt quit the programm. Later I tried again, and appearently timed well such that it quit with exit code 0
another example:

ssh://pi@ipadres/usr/bin/python3.7 -u /home/pi/Documents/Python/Testprogram.py
292.14
291.98
292.14
^C291.52
291.98
291.85
292.18
292.16
291.88
292.04

Process finished with exit code 143

Exit code 143 approximately 7 seconds after ^C

I really hope someone can help me :slight_smile:

1 Like

I am not a PyCharm user but it looks to me you might want to just use
except instead of except KeyboardInterrupt

At least it will always exit nicely.

Now as to why it’s doing an exit 143, I have no idea. is there no text description ?

try:
# whatever you need to do goes here
except Exception as e:
print(e)

1 Like

Well, I want it to be excepted by the keyboard intterupt, and it is, but somehow when bussy with this analogue read, the Keyboard intterupt does not manage to exit the programm directly. Instead, it stays running and than crashes later. If not, at the moment of the keyboard interrupt, bussy doing this analogue read, it works perfectly fine with the keyboard interrupt.
Unfortunately there is not a single line of description whatsoever :frowning: at least, I cannot find it. Not in the terminal, debugger etc.

1 Like

Did you try the suggestion? It traps the exception KeyboardInterrupt and more. Did it show the same sometimes works - sometimes not behavior?

#except KeyboardInterrupt:
except Exception as e:
    print("Exception: {}".format(str(e)))
    pass

print("nicely quit")
1 Like

Sorry, you are right I should have reacted much nicer and tried away! I was just a little to annoyed by the whole thing. Thanks for all the help and thinking with me!

I did try your sugestion. The behavior is the same, now when I do manage to exit directly it shows the following:

ssh://pi@192.168.0.27:22/usr/bin/python3.7 -u /home/pi/Documents/Python/Testprogram.py
312.34
310.8
^C^C^C310.22
^C^C310.22
310.24
^C^C310.23
^C^C^C^C310.06
^CTraceback (most recent call last):
File “/home/pi/Documents/Python/Testprogram.py”, line 25, in
time.sleep(0.1)
KeyboardInterrupt
^C
Process finished with exit code 130

You see how oft I have to press the stop before it does :frowning: if I press just once and wait, output is exactly the same:

ssh://pi@192.168.0.27:22/usr/bin/python3.7 -u /home/pi/Documents/Python/Testprogram.py
312.39
311.08
310.5
^C310.4
310.52
310.75
310.4
310.71
310.41

Process finished with exit code 143

1 Like

Very interesting @simon.vanhemert
I am a Python neophyte but have a few more ideas for you to try if it doesn’t try your patience.

I am not a PyCharm user; Perhaps PyCharm is marking up your code under the covers with hooks that are interfering with your program catching the keyboard interrupt. Does the program act differently if you run it from a command line instead of from inside PyCharm?

Your “try-except” is around the outer while loop, and the inner “while i<100” appears to be continuing on for a while.

Does the behavior change if you put a tiny sleep after the analog read?

        while i < 100:
            # Read sensor value
            sensor_value = grovepi.analogRead(sensor)
            time.sleep(0.001)
            sensor_value_total += sensor_value
            i += 1

Does the behavior change if you put a try-except and tiny sleep inside the inner while loop?

try:
   while True:
       i = 0
       sensor_value_total = 0

       while i < 100:
           # Read sensor value
           try:
               sensor_value = grovepi.analogRead(sensor)
           except Exception as e:
               print("Exception during analogRead: {}".format(str(e)))
               raise
           time.sleep(0.001)
           sensor_value_total += sensor_value
           i += 1

       # Durchschnittswert ueber die letzten i Messwerte
       sensor_value_average = sensor_value_total / i
       print(sensor_value_average)

       time.sleep(0.1)
except Exception as e:
   print("Outer Exception: {}:".format(str(e)))
   

Does the behavior change if the tiny sleep is inside the inner try-except?

try:
   while True:
       i = 0
       sensor_value_total = 0

       while i < 100:
           # Read sensor value
           try:
                 sensor_value = grovepi.analogRead(sensor)
                 time.sleep(0.001)
           except Exception as e:
                 print("Exception during analogRead: {}".format(str(e)))
                 raise
           
           sensor_value_total += sensor_value
           i += 1

       # Durchschnittswert ueber die letzten i Messwerte
       sensor_value_average = sensor_value_total / i
       print(sensor_value_average)

       time.sleep(0.1)
except Exception as e:
   print("Outer Exception: {}:".format(str(e)))
   

Found this same question on stack: Does this fix issue?

There could be something more sinister at play here. Ever since I started working with Raspberry Pi via ssh over WiFi (several years), I have periodically seen 1-5 second delays in stdin or stdout. No one else has complained of these, and I have given up trying to diagnose them, but the mystery remains. Sometimes I solved it by adding a USB WiFi dongle and ssh-ing in through the IP of the dongle, but sometimes I believe the delay returned even with the dongle. I’m just saying “There Be Dragons In Them Hills.”

1 Like

Thank you for all the good ideas. I have tried all, with no real succes :frowning:

  • I also ran the program from the Raspi directly using the terminal or the Python shell. The result is mostly the same, it exits less quickly in general, but not with a the right error. But as the delay between ^C and stopping is more than 5 seconds, But accessing and editing the Raspi seems to be without delay, I am assuming the connection is good, and Pycharm seems to work well.
  • The tiny sleep inside the loop does improve the behavior, that is, it increases the chance of it working well and quiting with:
    ^CTraceback (most recent call last):
    File “/home/stud/mech/Testprogram.py”, line 19, in
    time.sleep(0.001)
    KeyboardInterrupt

Process finished with exit code 1

However, the chance is still too small, which makes sense if you compare the time the programm spends in the analogue read and in the wait. I could ofcourse increase the wait, but that wouldnt really solve the problem and make my programm very slow.

  • The extra try except does not make a difference, regardless of the position of the extra wait.
1 Like

Another idea based On succumbing to the need for a delay: Only average the number of samples needed to obtain 95% confidence in the mean.

A story - I was designing a speech recognition system for a customer, and needed to test the accuracy of the solution. I imagined a test of 100 people performing 10 recognitions each would give me roughly 0.1% precision on the accuracy. I had a user interface Ph.D available to me and asked about my test design. He asked what I estimated the accuracy to be and what confidence I needed in the test result. He came back with a test design of 5 people to get the requisite 95% confidence.

Perhaps you can compare the answer you get from 3,5,10,and 100 samples, and will discover how many samples you need to get a good value. Statistics has some principles that Iam not familiar with that allow you to calculate from a sample, but I have run this 1,3,5,10,100 test on all my sensors to get an idea ov the number needed to reduce the variance with an average. Whatever the sensor variance is, the average supposedly exhibits sqrt(n) variance.

If the variance of the typical consumer sensor is say 4% and the value being measured is 300, then the sensor can be expected to return a value from 288 to 312. If I am guessing that taking the average of 100 measurements might reduce the variance to +/- 2, 298 to 302. Perhaps taking 10 samples will show values from 296 to 304, if the property being measured does not change during the time of measurement. It seems unlikely that any amount of samples would make the sensor value appear to move smoothly up or down! Be careful of your expectations for your system. (e.g. only output the integer portion )

1 Like

True, It should be possible to make the chance on an error much smaller, possibly down to 5%. The problem however, is that this program is run by students, not me. And if an error occurs and the program does not shut down properly, the connected hardware is likely to break. I may go for another type of sensor, or another way of programming the reading of the sensor.

2 Likes

Sorry for not coming back sooner. I would recommend a slightly longer delay. We usually recommend 0.1 second for safety. If it’s possible. Otherwise 0.05 might work.
Would this help?

2 Likes

Thank you for the suggestion, I am sorry I answered a little grumpy on the last one :slight_smile:
Well, it does work better with a longer waittime, but it chance of it working seems to be directly related to the waittimes. What I mean is, the analogue read takes about 0.007 seconds. If I take a waittime of 0.05 second, that gives me 10 % chance it does not work well and ends with error 143. That is actually the behaviour I see when testing.
The problem in my application is that it is run by Students, and if the programm does not close nicely, a motor will keep running and break. Therefore, even a 1 % chance is unacceptable to me.

2 Likes

@simon.vanhemert

I don’t know how much you can invest into solving this, but it looks to me that the grovepi.py is “eating” the keyboard interrupt in the analogRead() method’s I2C read and write method. Perhaps you want to make a “my_grovepi.py” that raises the KeyboardInterrupt exception.

Something like this might work?:

# Write I2C block to the GrovePi
def write_i2c_block(block, custom_timing = None):
	counter = 0
	reg = block[0]
	data = block[1:]
	while counter < 3:
		try:
			i2c.write_reg_list(reg, data)
			time.sleep(0.002 + additional_waiting)
			return
		except KeyboardInterrupt:
		    raise KeyboardInterrupt
		except:
			counter += 1
			time.sleep(0.003)
			continue

# Read I2C block from the GrovePi
def read_i2c_block(no_bytes = max_recv_size):
	data = data_not_available_cmd
	counter = 0
	while data[0] in [data_not_available_cmd[0], 255] and counter < 3:
		try:
			data = i2c.read_list(reg = None, len = no_bytes)
			time.sleep(0.002 + additional_waiting)
			if counter > 0:
				counter = 0
		except KeyboardInterrupt:
		    raise KeyboardInterrupt
		except:
			counter += 1
			time.sleep(0.003)
			
	return data
2 Likes

Genius! It worked like a charm:
ssh://pi@192.168.0.27:22/usr/bin/python3.7 -u /home/pi/Documents/Python/Testprogram.py
243.01
242.09
^CTraceback (most recent call last):
File “/home/pi/Documents/Python/my_grovepi.py”, line 209, in write_i2c_block
time.sleep(0.002 + additional_waiting)
KeyboardInterrupt

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File “/home/pi/Documents/Python/Testprogram.py”, line 18, in
sensor_value = my_grovepi.analogRead(sensor)
File “/home/pi/Documents/Python/my_grovepi.py”, line 262, in analogRead
write_i2c_block(aRead_cmd + [pin, unused, unused])
File “/home/pi/Documents/Python/my_grovepi.py”, line 212, in write_i2c_block
raise KeyboardInterrupt
KeyboardInterrupt

Process finished with exit code 1

100% succes rate. Super!

Now do you think Dexterindustries would consider this adaption as an improvement of the grovepi code? That would solve the problem for possible others, as well as make my life much easier as I don’t have to change grovepi for 25 devices manually and go to the same effort every time there is an important update. Also, I wouldnt think it adds much to the complexity or efficiency of the code.

2 Likes

DI has been very Supportive. Put your request into support@modrobotics.com. If you are on github, you could actually enter an issue against the code, and a pull request with your suggested changed code. (I know of this, but not how exactly.)

I would think that a change this deep might need some testing to make sure it never leaves the I2C bus in an unrecoverable state.

1 Like

Thanks @cyclicalobsessive for the suggestion!
It’s been integrated into the driver: https://github.com/DexterInd/GrovePi/pull/485

2 Likes