""" pigpio is a Python module for the Raspberry Pi which allows control of the general purpose input outputs (gpios). There are 54 gpios in total, arranged in two banks. Bank 1 contains gpios 0-31. Bank 2 contains gpios 32-54. Most of the gpios are dedicated to system use. A user should only manipulate gpios in bank 1. For a Rev.1 board only use gpios 0, 1, 4, 7, 8, 9, 10, 11, 14, 15, 17, 18, 21, 22, 23, 24, 25. For a Rev.2 board only use gpios 2, 3, 4, 7, 8, 9, 10, 11, 14, 15, 17, 18, 22, 23, 24, 25, 27, 28, 29, 30, 31. It is safe to read all the gpios. If you try to write a system gpio or change its mode you can crash the Pi or corrupt the data on the SD card. Features The pigpio module's main features are: - provision of PWM on any number of gpios 0-31 simultaneously. - provision of servo pulses on any number of gpios 0-31 simultaneously. - callbacks when any of gpios 0-31 change state. - reading/writing gpios and setting their modes (typically input or output). - reading/writing all of the gpios in a bank (0-31, 32-53) as a single operation. Notes ALL gpios are identified by their Broadcom number. This module uses the services of the C pigpio library. That library must be running on the Pi whose gpios are to be manipulated. The normal way to start the library is as a daemon (during system start). sudo pigpiod Your Python program should wrap the use of the module up in calls to pigpio.start() and pigpio.stop(). Settings A number of settings are determined when the pigpiod daemon is started. - the sample rate (1, 2, 4, 5, 8, or 10us, default 5us). - the set of gpios which may be updated (generally written to). The default set is those listed above for the Rev.1 or Rev.2 boards. - the available PWM frequencies (see set_PWM_frequency()). Exceptions By default a fatal exception is raised if you pass an invalid argument to a pigpio function. If you wish to handle the returned status yourself you should set pigpio.exceptions = False. """ import socket import struct import time import threading import os import atexit VERSION = "1.4" # gpio levels OFF = 0 LOW = 0 CLEAR = 0 ON = 1 HIGH = 1 SET = 1 TIMEOUT = 2 # gpio edges RISING_EDGE = 0 FALLING_EDGE = 1 EITHER_EDGE = 2 # gpio modes INPUT = 0 OUTPUT = 1 ALT0 = 4 ALT1 = 5 ALT2 = 6 ALT3 = 7 ALT4 = 3 ALT5 = 2 # gpio Pull Up Down PUD_OFF = 0 PUD_DOWN = 1 PUD_UP = 2 # script run status PI_SCRIPT_HALTED =0 PI_SCRIPT_RUNNING=1 PI_SCRIPT_WAITING=2 PI_SCRIPT_FAILED =3 # pigpio command numbers _PI_CMD_MODES= 0 _PI_CMD_MODEG= 1 _PI_CMD_PUD= 2 _PI_CMD_READ= 3 _PI_CMD_WRITE= 4 _PI_CMD_PWM= 5 _PI_CMD_PRS= 6 _PI_CMD_PFS= 7 _PI_CMD_SERVO= 8 _PI_CMD_WDOG= 9 _PI_CMD_BR1= 10 _PI_CMD_BR2= 11 _PI_CMD_BC1= 12 _PI_CMD_BC2= 13 _PI_CMD_BS1= 14 _PI_CMD_BS2= 15 _PI_CMD_TICK= 16 _PI_CMD_HWVER=17 _PI_CMD_NO= 18 _PI_CMD_NB= 19 _PI_CMD_NP= 20 _PI_CMD_NC= 21 _PI_CMD_PRG= 22 _PI_CMD_PFG= 23 _PI_CMD_PRRG= 24 _PI_CMD_HELP= 25 _PI_CMD_PIGPV=26 _PI_CMD_WVCLR=27 _PI_CMD_WVAG= 28 _PI_CMD_WVAS= 29 _PI_CMD_WVGO= 30 _PI_CMD_WVGOR=31 _PI_CMD_WVBSY=32 _PI_CMD_WVHLT=33 _PI_CMD_WVSM= 34 _PI_CMD_WVSP= 35 _PI_CMD_WVSC= 36 _PI_CMD_TRIG= 37 _PI_CMD_PROC= 38 _PI_CMD_PROCD=39 _PI_CMD_PROCR=40 _PI_CMD_PROCS=41 _PI_CMD_SLRO= 42 _PI_CMD_SLR= 43 _PI_CMD_SLRC= 44 _PI_CMD_PROCP=45 _PI_CMD_NOIB= 99 # pigpio error numbers _PI_INIT_FAILED =-1 PI_BAD_USER_GPIO =-2 PI_BAD_GPIO =-3 PI_BAD_MODE =-4 PI_BAD_LEVEL =-5 PI_BAD_PUD =-6 PI_BAD_PULSEWIDTH =-7 PI_BAD_DUTYCYCLE =-8 _PI_BAD_TIMER =-9 _PI_BAD_MS =-10 _PI_BAD_TIMETYPE =-11 _PI_BAD_SECONDS =-12 _PI_BAD_MICROS =-13 _PI_TIMER_FAILED =-14 PI_BAD_WDOG_TIMEOUT =-15 _PI_NO_ALERT_FUNC =-16 _PI_BAD_CLK_PERIPH =-17 _PI_BAD_CLK_SOURCE =-18 _PI_BAD_CLK_MICROS =-19 _PI_BAD_BUF_MILLIS =-20 PI_BAD_DUTYRANGE =-21 _PI_BAD_SIGNUM =-22 _PI_BAD_PATHNAME =-23 PI_NO_HANDLE =-24 PI_BAD_HANDLE =-25 _PI_BAD_IF_FLAGS =-26 _PI_BAD_CHANNEL =-27 _PI_BAD_PRIM_CHANNEL=-27 _PI_BAD_SOCKET_PORT =-28 _PI_BAD_FIFO_COMMAND=-29 _PI_BAD_SECO_CHANNEL=-30 _PI_NOT_INITIALISED =-31 _PI_INITIALISED =-32 _PI_BAD_WAVE_MODE =-33 _PI_BAD_CFG_INTERNAL=-34 PI_BAD_WAVE_BAUD =-35 PI_TOO_MANY_PULSES =-36 PI_TOO_MANY_CHARS =-37 PI_NOT_SERIAL_GPIO =-38 _PI_BAD_SERIAL_STRUC=-39 _PI_BAD_SERIAL_BUF =-40 PI_NOT_PERMITTED =-41 PI_SOME_PERMITTED =-42 PI_BAD_WVSC_COMMND =-43 PI_BAD_WVSM_COMMND =-44 PI_BAD_WVSP_COMMND =-45 PI_BAD_PULSELEN =-46 PI_BAD_SCRIPT =-47 PI_BAD_SCRIPT_ID =-48 PI_BAD_SER_OFFSET =-49 PI_GPIO_IN_USE =-50 PI_BAD_SERIAL_COUNT =-51 PI_BAD_PARAM_NUM =-52 PI_DUP_LABEL =-53 PI_TOO_MANY_LABELS =-54 PI_BAD_SCRIPT_CMD =-55 PI_BAD_VAR_NUM =-56 PI_NO_SCRIPT_ROOM =-57 PI_NO_MEMORY =-58 PI_SOCK_READ_FAILED =-59 PI_SOCK_WRIT_FAILED =-60 PI_TOO_MANY_PARAM =-61 PI_NOT_HALTED =-62 # pigpio error text _errors=[ [_PI_INIT_FAILED , "pigpio initialisation failed"], [PI_BAD_USER_GPIO , "gpio not 0-31"], [PI_BAD_GPIO , "gpio not 0-53"], [PI_BAD_MODE , "mode not 0-7"], [PI_BAD_LEVEL , "level not 0-1"], [PI_BAD_PUD , "pud not 0-2"], [PI_BAD_PULSEWIDTH , "pulsewidth not 0 or 500-2500"], [PI_BAD_DUTYCYCLE , "dutycycle not 0-255"], [_PI_BAD_TIMER , "timer not 0-9"], [_PI_BAD_MS , "ms not 10-60000"], [_PI_BAD_TIMETYPE , "timetype not 0-1"], [_PI_BAD_SECONDS , "seconds < 0"], [_PI_BAD_MICROS , "micros not 0-999999"], [_PI_TIMER_FAILED , "gpioSetTimerFunc failed"], [PI_BAD_WDOG_TIMEOUT , "timeout not 0-60000"], [_PI_NO_ALERT_FUNC , "DEPRECATED"], [_PI_BAD_CLK_PERIPH , "clock peripheral not 0-1"], [_PI_BAD_CLK_SOURCE , "clock source not 0-1"], [_PI_BAD_CLK_MICROS , "clock micros not 1, 2, 4, 5, 8, or 10"], [_PI_BAD_BUF_MILLIS , "buf millis not 100-10000"], [PI_BAD_DUTYRANGE , "dutycycle range not 25-40000"], [_PI_BAD_SIGNUM , "signum not 0-63"], [_PI_BAD_PATHNAME , "can't open pathname"], [PI_NO_HANDLE , "no handle available"], [PI_BAD_HANDLE , "unknown notify handle"], [_PI_BAD_IF_FLAGS , "ifFlags > 3"], [_PI_BAD_CHANNEL , "DMA channel not 0-14"], [_PI_BAD_SOCKET_PORT , "socket port not 1024-30000"], [_PI_BAD_FIFO_COMMAND , "unknown fifo command"], [_PI_BAD_SECO_CHANNEL , "DMA secondary channel not 0-6"], [_PI_NOT_INITIALISED , "function called before gpioInitialise"], [_PI_INITIALISED , "function called after gpioInitialise"], [_PI_BAD_WAVE_MODE , "waveform mode not 0-1"], [_PI_BAD_CFG_INTERNAL , "bad parameter in gpioCfgInternals call"], [PI_BAD_WAVE_BAUD , "baud rate not 100-250000"], [PI_TOO_MANY_PULSES , "waveform has too many pulses"], [PI_TOO_MANY_CHARS , "waveform has too many chars"], [PI_NOT_SERIAL_GPIO , "no serial read in progress on gpio"], [PI_NOT_PERMITTED , "no permission to update gpio"], [PI_SOME_PERMITTED , "no permission to update one or more gpios"], [PI_BAD_WVSC_COMMND , "bad WVSC subcommand"], [PI_BAD_WVSM_COMMND , "bad WVSM subcommand"], [PI_BAD_WVSP_COMMND , "bad WVSP subcommand"], [PI_BAD_PULSELEN , "trigger pulse length > 100"], [PI_BAD_SCRIPT , "invalid script"], [PI_BAD_SCRIPT_ID , "unknown script id"], [PI_BAD_SER_OFFSET , "add serial data offset > 30 minute"], [PI_GPIO_IN_USE , "gpio already in use"], [PI_BAD_SERIAL_COUNT , "must read at least a byte at a time"], [PI_BAD_PARAM_NUM , "script parameter must be 0-9"], [PI_DUP_LABEL , "script has duplicate label"], [PI_TOO_MANY_LABELS , "script has too many labels"], [PI_BAD_SCRIPT_CMD , "illegal script command"], [PI_BAD_VAR_NUM , "script variable must be 0-149"], [PI_NO_SCRIPT_ROOM , "no more room for scripts"], [PI_NO_MEMORY , "can't allocate temporary memory"], [PI_SOCK_READ_FAILED , "socket read failed"], [PI_SOCK_WRIT_FAILED , "socket write failed"], [PI_TOO_MANY_PARAM , "too many script parameters (> 10)"], [PI_NOT_HALTED , "script already running or failed"], ] _control = None _notify = None _host = '' _port = 8888 exceptions = True class _pigpioError(Exception): """pigpio module exception""" def __init__(self, value): self.value = value def __str__(self): return repr(self.value) def error(pigpio_error): """Converts a pigpio error number to a text description. pigpio_error: an error number (<0) returned by pigpio. Example ... print(pigpio.error(-5)) level not 0-1 ... """ for e in _errors: if e[0] == pigpio_error: return e[1] return "unknown error ({})".format(pigpio_error) def tickDiff(tStart, tEnd): """Calculate the time difference between two ticks. tStart: the earlier tick. tEnd: the later tick. The function handles wrap around as the tick overflows 32 bits. The returned value is in microseconds. Example ... print(pigpio.tickDiff(4294967272, 12)) 36 ... """ tDiff = tEnd - tStart if tDiff < 0: tDiff += (1 << 32) return tDiff def _u2i(number): """Converts a number from unsigned to signed. number: a 32 bit unsigned number """ mask = (2 ** 32) - 1 if number & (1 << 31): v = number | ~mask else: v = number & mask if v >= 0: return v; else: if exceptions: raise _pigpioError(error(v)) else: return v def _pigpio_command(sock, cmd, p1, p2): """ Executes a pigpio socket command. sock: command socket. cmd: the command to be executed. p1: command parameter 1 (if applicable). p2: command parameter 2 (if applicable). """ if sock is not None: sock.send(struct.pack('IIII', cmd, p1, p2, 0)) x, y, z, res = struct.unpack('IIII', sock.recv(16)) return res else: raise _pigpioError("*** Module not started, call pigpio.start() ***") def _pigpio_command_ext(sock, cmd, p1, p2, extents): """ Executes an extended pigpio socket command. sock: command socket. cmd: the command to be executed. p1: command parameter 1 (if applicable). p2: command parameter 2 (if applicable). extents: additional data blocks """ if sock is not None: sock.send(struct.pack('IIII', cmd, p1, p2, 0)) for ext in extents: sock.sendall(ext) x, y, z, res = struct.unpack('IIII', sock.recv(16)) return res else: raise _pigpioError("*** Module not started, call pigpio.start() ***") class _callback: """An ADT class to hold callback information.""" def __init__(self, gpio, edge, func): """Initialises a callback ADT. gpio: Broadcom gpio number. edge: EITHER_EDGE, RISING_EDGE, or FALLING_EDGE. func: a user function taking three arguments (gpio, level, tick). """ self.gpio = gpio self.edge = edge self.func = func self.bit = 1<= 0: pigpio.notify_begin(h, 1234) ... """ return _u2i(_pigpio_command(_control, _PI_CMD_NO, 0, 0)) def notify_begin(handle, bits): """Start notifications on a previously opened handle. Returns 0 if OK, otherwise PI_BAD_HANDLE. handle: 0-31 (as returned by notify_open()) bits: a mask indicating the gpios to be notified. The notification sends state changes for each gpio whose corresponding bit in bits is set. Example ... h = pigpio.notify_open() if h >= 0: pigpio.notify_begin(h, 1234) ... This will start notifications for gpios 1, 4, 6, 7, 10 (1234 = 0x04D2 = 0b0000010011010010). Notes Each notification occupies 12 bytes in the fifo as follows: H (16 bit) seqno H (16 bit) flags I (32 bit) tick I (32 bit) level """ return _u2i(_pigpio_command(_control, _PI_CMD_NB, handle, bits)) def notify_pause(handle): """Pause notifications on a previously opened handle. Returns 0 if OK, otherwise PI_BAD_HANDLE. handle: 0-31 (as returned by notify_open()) Notifications for the handle are suspended until notify_begin() is called again. Example ... h = pigpio.notify_open() if h >= 0: pigpio.notify_begin(h, 1234) ... pigpio.notify_pause(h) ... pigpio.notify_begin(h, 1234) ... ... """ return _u2i(_pigpio_command(_control, _PI_CMD_NB, handle, 0)) def notify_close(handle): """Stop notifications on a previously opened handle and release the handle for reuse. Returns 0 if OK, otherwise PI_BAD_HANDLE. handle: 0-31 (as returned by notify_open()) Example ... h = pigpio.notify_open() if h >= 0: pigpio.notify_begin(h, 1234) ... pigpio.notify_close(h) ... ... """ return _u2i(_pigpio_command(_control, _PI_CMD_NC, handle, 0)) def set_watchdog(user_gpio, timeout): """Sets a watchdog for a gpio. Returns 0 if OK, otherwise PI_BAD_USER_GPIO or PI_BAD_WDOG_TIMEOUT. user_gpio: 0-31. timeout: 0-60000. The watchdog is nominally in milliseconds. Only one watchdog may be registered per gpio. The watchdog may be cancelled by setting timeout to 0. If no level change has been detected for the gpio for timeout milliseconds any notification for the gpio has a report written to the fifo with the flags set to indicate a watchdog timeout. The callback() class interprets the flags and will call registered callbacks for the gpio with level TIMEOUT. Example #!/usr/bin/python import pigpio import time def cbf(g, L, t): s = "gpio=" + str(g) + " level=" + str(L) + " at " + str(t) print(s) pigpio.start() cb = pigpio.callback(22, pigpio.EITHER_EDGE, cbf) print("callback started, 5 second delay") time.sleep(5) pigpio.set_watchdog(22, 1000) # 1000ms watchdog print("watchdog started, 5 second delay") time.sleep(5) pigpio.set_watchdog(22, 0) # cancel watchdog print("watchdog cancelled, 5 second delay") time.sleep(5) cb.cancel() pigpio.stop() will print lines such as callback started, 5 second delay watchdog started, 5 second delay gpio=22 level=2 at 3547411617 gpio=22 level=2 at 3548411254 gpio=22 level=2 at 3549411927 gpio=22 level=2 at 3550412060 gpio=22 level=2 at 3551411622 watchdog cancelled, 5 second delay """ return _u2i(_pigpio_command(_control, _PI_CMD_WDOG, user_gpio, timeout)) def read_bank_1(): """Read the levels of the bank 1 gpios (gpios 0-31). The returned 32 bit integer has a bit set if the corresponding gpio is logic 1. Gpio n has bit value (1<= 0: param = struct.unpack('IIIIIIIIII', _control.recv(40)) return status, param return status, () def stop_script(script_id): """ Stops a running script. Returns 0 if OK, otherwise PI_BAD_SCRIPT_ID. script_id: script_id of stored script. """ return _u2i(_pigpio_command(_control, _PI_CMD_PROCS, script_id, 0)) def delete_script(script_id): """ Deletes a stored script. Returns 0 if OK, otherwise PI_BAD_SCRIPT_ID. script_id: script_id of stored script. """ return _u2i(_pigpio_command(_control, _PI_CMD_PROCD, script_id, 0)) def serial_read_open(user_gpio, baud): """ This function opens a gpio for reading serial data. Returns 0 if OK, otherwise PI_BAD_USER_GPIO, PI_BAD_WAVE_BAUD, or PI_GPIO_IN_USE. The serial data is held in a cyclic buffer and is read using gpioSerialRead(). It is the caller's responsibility to read data from the cyclic buffer in a timely fashion. """ return _u2i(_pigpio_command(_control, _PI_CMD_SLRO, user_gpio, baud)) def serial_read(user_gpio): """ This function returns data from the serial cyclic buffer. It returns a tuple of status and string. The status will be the length, possibly 0, of the returned string if OK. Otherwise a negative error code will be returned in which case the string will be null. """ bytes = _u2i(_pigpio_command(_control, _PI_CMD_SLR, user_gpio, 10000)) if bytes > 0: buf = "" while len(buf) < bytes: buf += _control.recv(bytes-len(buf)) return bytes, buf return bytes, "" def serial_read_close(user_gpio): """ This function closes a gpio for reading serial data. Returns 0 if OK, otherwise PI_BAD_USER_GPIO, or PI_NOT_SERIAL_GPIO. """ return _u2i(_pigpio_command(_control, _PI_CMD_SLRC, user_gpio, 0)) class callback: """A class to provide gpio level change callbacks.""" def __init__(self, user_gpio, edge=RISING_EDGE, func=None): """Initialise a callback and adds it to the notification thread. user_gpio: 0-31. edge: EITHER_EDGE, RISING_EDGE (default), or FALLING_EDGE. func: user supplied callback function. If a user callback is not specified a default tally callback is provided which simply counts edges. The user supplied callback receives three parameters, the gpio, the level, and the tick. Example 1: user supplied edge and callback #!/usr/bin/python import pigpio import time def cbf(g, L, t): s = "gpio=" + str(g) + " level=" + str(L) + " at " + str(t) print(s) pigpio.start() cb = pigpio.callback(22, pigpio.EITHER_EDGE, cbf) time.sleep(30) cb.cancel() pigpio.stop() will print lines such as gpio=22 level=1 at 548556842 gpio=22 level=0 at 551316679 gpio=22 level=1 at 553411795 gpio=22 level=0 at 555269219 gpio=22 level=1 at 557689701 Example 2: user supplied edge, default (tally) callback #!/usr/bin/python import pigpio import time pigpio.start() pigpio.set_PWM_dutycycle(4, 0) pigpio.set_PWM_frequency(4, 2000) cb = pigpio.callback(4, pigpio.EITHER_EDGE) pigpio.set_PWM_dutycycle(4, 128) # half power tally_1 = cb.tally() time.sleep(50) tally_2 = cb.tally() s = "counted " + str(tally_2 - tally_1) + " edges" print(s) cb.cancel() pigpio.stop() will print a line such as counted 200200 edges Example 3: default edge and (tally) callback #!/usr/bin/python import pigpio import time pigpio.start() pigpio.set_PWM_dutycycle(17, 0) pigpio.set_PWM_frequency(17, 2000) cb = pigpio.callback(17) pigpio.set_PWM_dutycycle(17, 64) # quarter power tally_1 = cb.tally() time.sleep(50) tally_2 = cb.tally() s = "counted " + str(tally_2 - tally_1) + " rising edges" print(s) cb.cancel() pigpio.stop() will print a line such as counted 100101 rising edges """ self.count=0 if func is None: func=self._tally self.callb = _callback(user_gpio, edge, func) _notify.append(self.callb) def cancel(self): """Cancels a callback by removing it from the notification thread.""" _notify.remove(self.callb) def _tally(self, user_gpio, level, tick): """Increment the callback called count. user_gpio: level: tick: """ self.count += 1 def tally(self): """Provides a count of how many times the default tally callback has triggered. The count will be zero if the user has supplied their own callback function. """ return self.count def wait_for_edge(user_gpio, edge=RISING_EDGE, timeout=60.0): """Wait for an edge event on a gpio. The function returns as soon as the edge is detected or after the number of seconds specified by timeout has expired. user_gpio: 0-31. edge: EITHER_EDGE, RISING_EDGE (default), or FALLING_EDGE. timeout: 0.0- (default 60.0). The function returns True if the edge is detected, otherwise False. Example 1: default edge and timeout #!/usr/bin/python import pigpio import time pigpio.start() if pigpio.wait_for_edge(23): print("Rising edge detected") else: print("wait for edge timed out") pigpio.stop() will print Rising edge detected or wait for edge timed out Example 2: user supplied edge and timeout #!/usr/bin/python import pigpio import time pigpio.start() if pigpio.wait_for_edge(23, pigpio.FALLING_EDGE, 5.0): print("Falling edge detected") else: print("wait for falling edge timed out") pigpio.stop() will print Falling edge detected or wait for falling edge timed out """ a = _wait_for_edge(user_gpio, edge, timeout) return a.trigger def start(host = os.getenv("PIGPIO_ADDR", ''), port = os.getenv("PIGPIO_PORT", 8888)): """Start the pigpio module. host: the host name of the Pi on which the pigpio daemon is running. The default is localhost unless overwritten by the PIGPIO_ADDR environment variable. port: the port number on which the pigpio daemon is listening. The default is 8888 unless overwritten by the PIGPIO_PORT environment variable. The pigpiod must have been started with the same port number. The function connects to the pigpio daemon and reserves resources to be used for sending commands and receiving notifications. EXAMPLES: ... pigpio.start() # use defaults pigpio.start('mypi') # specify host, default port pigpio.start('mypi', 7777) # specify host and port ... """ global _control, _notify global _host, _port _host = host _port = int(port) _control = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: _control.connect((_host, _port)) _notify = _callback_thread() except socket.error: if _control is not None: _control = None if _host == '': h = "localhost" else: h = _host errStr = "Can't connect to pigpio on " + str(h) + "(" + str(_port) + ")" print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") print(errStr) print("") print("Did you start the pigpio daemon? E.g. sudo pigpiod") print("") print("Did you specify the correct Pi host/port in the environment") print("variables PIGPIO_ADDR/PIGPIO_PORT?") print("E.g. export PIGPIO_ADDR=soft, export PIGPIO_PORT=8888") print("") print("Did you specify the correct Pi host/port in the") print("pigpio.start() function? E.g. pigpio.start('soft', 8888))") print("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%") return False return True def stop(): """Release pigpio resources. Example ... pigpio.stop() ... """ global _control, _notify if _notify is not None: _notify.stop() _notify = None if _control is not None: _control.close() _control = None atexit.register(stop)