mirror of
				https://github.com/KevinMidboe/python-gpiozero.git
				synced 2025-10-29 17:50:37 +00:00 
			
		
		
		
	Overhaul the pi_info system: Pin factories are now capable of generating pi_info themselves (although currently they all just look up the revision and call pi_info with a specific one). PiGPIOPin will now return pi_info for the remote pi which can be specified by parameter or implicitly by the environment vars. Overvolted Pis should work properly no matter what (some argument over whether the revision 7 or 8 chars in this case; both should work). Added some minor tweaks for the new camera-capable Pi Zero Finally, added a bunch of tests for pins.data
		
			
				
	
	
		
			240 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			240 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| from __future__ import (
 | |
|     unicode_literals,
 | |
|     absolute_import,
 | |
|     print_function,
 | |
|     division,
 | |
|     )
 | |
| str = type('')
 | |
| 
 | |
| import warnings
 | |
| from RPi import GPIO
 | |
| 
 | |
| from . import LocalPin
 | |
| from .data import pi_info
 | |
| from ..exc import (
 | |
|     PinInvalidFunction,
 | |
|     PinSetInput,
 | |
|     PinFixedPull,
 | |
|     PinInvalidPull,
 | |
|     PinInvalidState,
 | |
|     PinInvalidBounce,
 | |
|     PinPWMFixedValue,
 | |
|     PinNonPhysical,
 | |
|     PinNoPins,
 | |
|     )
 | |
| 
 | |
| 
 | |
| class RPiGPIOPin(LocalPin):
 | |
|     """
 | |
|     Uses the `RPi.GPIO`_ library to interface to the Pi's GPIO pins. This is
 | |
|     the default pin implementation if the RPi.GPIO library is installed.
 | |
|     Supports all features including PWM (via software).
 | |
| 
 | |
|     Because this is the default pin implementation you can use it simply by
 | |
|     specifying an integer number for the pin in most operations, e.g.::
 | |
| 
 | |
|         from gpiozero import LED
 | |
| 
 | |
|         led = LED(12)
 | |
| 
 | |
|     However, you can also construct RPi.GPIO pins manually if you wish::
 | |
| 
 | |
|         from gpiozero.pins.rpigpio import RPiGPIOPin
 | |
|         from gpiozero import LED
 | |
| 
 | |
|         led = LED(RPiGPIOPin(12))
 | |
| 
 | |
|     .. _RPi.GPIO: https://pypi.python.org/pypi/RPi.GPIO
 | |
|     """
 | |
| 
 | |
|     _PINS = {}
 | |
| 
 | |
|     GPIO_FUNCTIONS = {
 | |
|         'input':   GPIO.IN,
 | |
|         'output':  GPIO.OUT,
 | |
|         'i2c':     GPIO.I2C,
 | |
|         'spi':     GPIO.SPI,
 | |
|         'pwm':     GPIO.HARD_PWM,
 | |
|         'serial':  GPIO.SERIAL,
 | |
|         'unknown': GPIO.UNKNOWN,
 | |
|         }
 | |
| 
 | |
|     GPIO_PULL_UPS = {
 | |
|         'up':       GPIO.PUD_UP,
 | |
|         'down':     GPIO.PUD_DOWN,
 | |
|         'floating': GPIO.PUD_OFF,
 | |
|         }
 | |
| 
 | |
|     GPIO_EDGES = {
 | |
|         'both':    GPIO.BOTH,
 | |
|         'rising':  GPIO.RISING,
 | |
|         'falling': GPIO.FALLING,
 | |
|         }
 | |
| 
 | |
|     GPIO_FUNCTION_NAMES = {v: k for (k, v) in GPIO_FUNCTIONS.items()}
 | |
|     GPIO_PULL_UP_NAMES = {v: k for (k, v) in GPIO_PULL_UPS.items()}
 | |
|     GPIO_EDGES_NAMES = {v: k for (k, v) in GPIO_EDGES.items()}
 | |
| 
 | |
|     PI_INFO = None
 | |
| 
 | |
|     def __new__(cls, number):
 | |
|         if not cls._PINS:
 | |
|             GPIO.setmode(GPIO.BCM)
 | |
|             GPIO.setwarnings(False)
 | |
|         if cls.PI_INFO is None:
 | |
|             cls.PI_INFO = pi_info()
 | |
|         try:
 | |
|             return cls._PINS[number]
 | |
|         except KeyError:
 | |
|             self = super(RPiGPIOPin, cls).__new__(cls)
 | |
|             try:
 | |
|                 cls.PI_INFO.physical_pin('GPIO%d' % number)
 | |
|             except PinNoPins:
 | |
|                 warnings.warn(
 | |
|                     PinNonPhysical(
 | |
|                         'no physical pins exist for GPIO%d' % number))
 | |
|             self._number = number
 | |
|             self._pull = 'up' if cls.PI_INFO.pulled_up('GPIO%d' % number) else 'floating'
 | |
|             self._pwm = None
 | |
|             self._frequency = None
 | |
|             self._duty_cycle = None
 | |
|             self._bounce = -666
 | |
|             self._when_changed = None
 | |
|             self._edges = GPIO.BOTH
 | |
|             GPIO.setup(self._number, GPIO.IN, self.GPIO_PULL_UPS[self._pull])
 | |
|             cls._PINS[number] = self
 | |
|             return self
 | |
| 
 | |
|     def __repr__(self):
 | |
|         return "GPIO%d" % self._number
 | |
| 
 | |
|     @property
 | |
|     def number(self):
 | |
|         return self._number
 | |
| 
 | |
|     def close(self):
 | |
|         self.frequency = None
 | |
|         self.when_changed = None
 | |
|         GPIO.cleanup(self._number)
 | |
| 
 | |
|     def output_with_state(self, state):
 | |
|         self._pull = 'floating'
 | |
|         GPIO.setup(self._number, GPIO.OUT, initial=state)
 | |
| 
 | |
|     def input_with_pull(self, pull):
 | |
|         if pull != 'up' and self.PI_INFO.pulled_up('GPIO%d' % self._number):
 | |
|             raise PinFixedPull('%r has a physical pull-up resistor' % self)
 | |
|         try:
 | |
|             GPIO.setup(self._number, GPIO.IN, self.GPIO_PULL_UPS[pull])
 | |
|             self._pull = pull
 | |
|         except KeyError:
 | |
|             raise PinInvalidPull('invalid pull "%s" for pin %r' % (pull, self))
 | |
| 
 | |
|     def _get_function(self):
 | |
|         return self.GPIO_FUNCTION_NAMES[GPIO.gpio_function(self._number)]
 | |
| 
 | |
|     def _set_function(self, value):
 | |
|         if value != 'input':
 | |
|             self._pull = 'floating'
 | |
|         if value in ('input', 'output') and value in self.GPIO_FUNCTIONS:
 | |
|             GPIO.setup(self._number, self.GPIO_FUNCTIONS[value], self.GPIO_PULL_UPS[self._pull])
 | |
|         else:
 | |
|             raise PinInvalidFunction('invalid function "%s" for pin %r' % (value, self))
 | |
| 
 | |
|     def _get_state(self):
 | |
|         if self._pwm:
 | |
|             return self._duty_cycle
 | |
|         else:
 | |
|             return GPIO.input(self._number)
 | |
| 
 | |
|     def _set_state(self, value):
 | |
|         if self._pwm:
 | |
|             try:
 | |
|                 self._pwm.ChangeDutyCycle(value * 100)
 | |
|             except ValueError:
 | |
|                 raise PinInvalidState('invalid state "%s" for pin %r' % (value, self))
 | |
|             self._duty_cycle = value
 | |
|         else:
 | |
|             try:
 | |
|                 GPIO.output(self._number, value)
 | |
|             except ValueError:
 | |
|                 raise PinInvalidState('invalid state "%s" for pin %r' % (value, self))
 | |
|             except RuntimeError:
 | |
|                 raise PinSetInput('cannot set state of pin %r' % self)
 | |
| 
 | |
|     def _get_pull(self):
 | |
|         return self._pull
 | |
| 
 | |
|     def _set_pull(self, value):
 | |
|         if self.function != 'input':
 | |
|             raise PinFixedPull('cannot set pull on non-input pin %r' % self)
 | |
|         if value != 'up' and self.PI_INFO.pulled_up('GPIO%d' % self._number):
 | |
|             raise PinFixedPull('%r has a physical pull-up resistor' % self)
 | |
|         try:
 | |
|             GPIO.setup(self._number, GPIO.IN, self.GPIO_PULL_UPS[value])
 | |
|             self._pull = value
 | |
|         except KeyError:
 | |
|             raise PinInvalidPull('invalid pull "%s" for pin %r' % (value, self))
 | |
| 
 | |
|     def _get_frequency(self):
 | |
|         return self._frequency
 | |
| 
 | |
|     def _set_frequency(self, value):
 | |
|         if self._frequency is None and value is not None:
 | |
|             try:
 | |
|                 self._pwm = GPIO.PWM(self._number, value)
 | |
|             except RuntimeError:
 | |
|                 raise PinPWMFixedValue('cannot start PWM on pin %r' % self)
 | |
|             self._pwm.start(0)
 | |
|             self._duty_cycle = 0
 | |
|             self._frequency = value
 | |
|         elif self._frequency is not None and value is not None:
 | |
|             self._pwm.ChangeFrequency(value)
 | |
|             self._frequency = value
 | |
|         elif self._frequency is not None and value is None:
 | |
|             self._pwm.stop()
 | |
|             self._pwm = None
 | |
|             self._duty_cycle = None
 | |
|             self._frequency = None
 | |
| 
 | |
|     def _get_bounce(self):
 | |
|         return None if self._bounce == -666 else (self._bounce / 1000)
 | |
| 
 | |
|     def _set_bounce(self, value):
 | |
|         if value is not None and value < 0:
 | |
|             raise PinInvalidBounce('bounce must be 0 or greater')
 | |
|         f = self.when_changed
 | |
|         self.when_changed = None
 | |
|         try:
 | |
|             self._bounce = -666 if value is None else int(value * 1000)
 | |
|         finally:
 | |
|             self.when_changed = f
 | |
| 
 | |
|     def _get_edges(self):
 | |
|         return self.GPIO_EDGES_NAMES[self._edges]
 | |
| 
 | |
|     def _set_edges(self, value):
 | |
|         f = self.when_changed
 | |
|         self.when_changed = None
 | |
|         try:
 | |
|             self._edges = self.GPIO_EDGES[value]
 | |
|         finally:
 | |
|             self.when_changed = f
 | |
| 
 | |
|     def _get_when_changed(self):
 | |
|         return self._when_changed
 | |
| 
 | |
|     def _set_when_changed(self, value):
 | |
|         if self._when_changed is None and value is not None:
 | |
|             self._when_changed = value
 | |
|             GPIO.add_event_detect(
 | |
|                 self._number, self._edges,
 | |
|                 callback=lambda channel: self._when_changed(),
 | |
|                 bouncetime=self._bounce)
 | |
|         elif self._when_changed is not None and value is None:
 | |
|             GPIO.remove_event_detect(self._number)
 | |
|             self._when_changed = None
 | |
|         else:
 | |
|             self._when_changed = value
 | |
| 
 |