Exiting a user program back to the shell.
One can use SIGINT (via ctrl-c) exactly as Sunfounder do. But if one wants to use an e.g button, voice command, joystick etc, then the GPIO will remain reserved.
This route is a a bit more convoluted. It requires the use of a parent wrapper to start and terminate the user program.
Here is a short dummy program, which will intentionally leave the GPIO busy, in order to demonstrate the use of the wrapper. (ignore the safe_shutdown for now, (call it mycode.py)
from pidog import Pidog
import os
def safe_shutdown(dog, exit_code=0):
print("Stopping PiDog motion...")
try:
dog.body_stop() # stop all servos
dog.wait_legs_done() # wait for actions to finish
except Exception:
pass
print("Exiting Python process...")
os._exit(exit_code)
if __name__ == "__main__":
my_dog=Pidog()
#############################################################
#Dummy code for demo purposes, replace wih own code
count = 0
VoiceCommand = "None"
while (VoiceCommand != "Exit"):
print("Iteration ",count,VoiceCommand)
count += 1
#Force Exit command on 5th iteration for demo only
if (count == 5):
VoiceCommand = "Exit"
#End dummy lines to remove
#############################################################
print("Closing pidog")
safe_shutdown(my_dog, 0)
If you do run this example then GPIO can be recovered as shown in above post
But, if one wishes to exit the user program cleanly, with GPIO available for subsequent runs, such as above example, then use the following launch wrapper, (call it run_pidog.py)
import pexpect
import sys
import time
#
# Usage: python3 run_pidog.py mycode.py
#
if len(sys.argv) < 2:
print("Usage: python3 wrap2.py mycode.py")
sys.exit(1)
code_to_run = sys.argv[1]
# Launch your code inside a pseudo-terminal
child = pexpect.spawn(f"python3 {code_to_run}", encoding='utf-8')
# Print its output live
child.logfile_read = sys.stdout
# Wait for the user code to end naturally
child.expect(pexpect.EOF)
# Give it a moment .....
time.sleep(0.2)
# Now send a Ctrl-C/SIGINT through the PTY
child.sendcontrol('c')
# Wait for child to fully terminate
child.close()
print("\n[Wrapper] Script finished and cleanup done ")
run this as ..
python run_pidog.py mycode.py
Note, if you use other resources such as the camera, these will need to be added to the shutdown function.
This works because the wrapper owns the real lifecycle, it decides when the session is over, injects SIGINT when appropriate, then waits for child to exit and, If needed, escalates (SIGTERM, SIGKILL)
Hope this is of some value to someone!