PKfHpyparts/__init__.pyPKfHpyparts/systems/__init__.pyPKfH" )pyparts/systems/temperature_controller.pyimport time from pyparts.logic import pid_controller class TemperatureController(object): """A PID based temperature controller. TemperatureController uses a PID controller to regulate temperature. A temperature sensor is used to read the current temperature and a PWM based heater is used to increase the temperature when needed. Once the desired temperature is reached the heater turns off until the temperature falls below the desired temperature. Attributes: _temp_sensor: TemperatureSensor. A temperature sensor to read temperature values from. _heater_pin: PwmOutput. A PWM output that controls a heating element. _pid_worker: PIDController.Worker. Worker thread for maintaining a temperature. """ # Error value at which PWM output will be set to 100% MAX_ERROR_DEGREES_C = 10.0 def __init__(self, temp_sensor, heater_pin, kp, ki, kd): """Creates a TemperatureController. Args: temp_sensor: TemperatureSensor. A temperature sensor to read temperature. heater_pin: PwmOutput. A PWM output that controls a heating element. kp: Integer. PID controller constant term. ki: Integer. PID controller integrator term. kd: Integer. PID controller differentiator term. """ self._temp_sensor = temp_sensor self._heater_pin = heater_pin self._pid_worker = pid_controller.PIDController.Worker( kp, ki, kd, self._pid_input_func, self._pid_output_func) self._is_enabled = False def _pid_input_func(self): """Input function to the PID controller.""" return self._temp_sensor.temp_c def _pid_output_func(self, val): """Output function to handle PID controller error values. Args: val: Float. The output value from the PID controller. """ # If the sensor is too hot, turn off the heater if val <= 0: self._heater_pin.set_duty_cycle(0) return if val > self.MAX_ERROR_DEGREES_C: self._heater_pin.set_duty_cycle(100) return self._heater_pin.set_duty_cycle( (float(val) / self.MAX_ERROR_DEGREES_C) * 100) time.sleep(1) def set_temp_c(self, temp_c): """Set the desired temerature value. Args: temp_c: Integer. The temperature to target with the controller. """ self._pid_worker.desired_value = temp @property def temp_setting(self): """Get the current temperature set point.""" return self._pid_worker.desired_value def enable(self): """Enable the temperature sensor and begin controlling the temperature.""" if not self._is_enabled: self._pid_worker.start() self._is_enabled = True def disable(self): """Stops the temperature sensor and stops controlling the temperature.""" if self._is_enabled: self._pid_worker.stop() self._is_enabled = False @property def is_enabled(self): """Checks whether the temperature controller has been enabled or not. Returns: Boolean. True if the controller has been started. """ return self._is_enabled PKfHpyparts/parts/__init__.pyPKfH|A?.!!pyparts/parts/base_part.pyclass BasePart(object): pass PKfH pyparts/parts/switch/__init__.pyPKfHpyparts/parts/switch/button.pyfrom pyparts.parts import base_part class Button(base_part.BasePart): def __init__(self, pin): self._pin = pin self._enabled = False def set_on_down(self, callback, debounce_time_ms): if self._pin.pull_up_down == self._pin.PUD_UP: type = self._pin.INTERRUPT_FALLING else: type = self._pin.INTERRUPT_RISING self._pin.add_interrupt(type, callback, debounce_time_ms) def set_on_up(self, callback, debounce_time_ms): if self._pin.pull_up_down == self._pin.PUD_UP: type = self._pin.INTERRUPT_RISING else: type = self._pin.INTERRUPT_FALLING self._pin.add_interrupt(type, callback, debounce_time_ms) def set_on_both(self, callback, debounce_time_ms): self._pin.add_interrupt(self._pin.INTERRUPT_BOTH, callback, debounce_time_ms) def set_on_hold(self, callback, min_hold_time): raise NotImplementedError def set_on_press(self, callback, max_down_time): raise NotImplementedError def set_on_double_press(self, callback, max_down_time, max_up_time): raise NotImplementedError def remove_callbacks(self): self._pin.remove_interrupt() PKfHpyparts/parts/led/__init__.pyPKfH9|pyparts/parts/led/rgb_led.pyimport time from pyparts.parts import base_part class RGBLed(base_part.BasePart): def __init__(self, red_pwm, blue_pwm, green_pwm): self._red_pwm = red_pwm self._green_pwm = green_pwm self._blue_pwm = blue_pwm def enable(self): self._red_pwm.enable() self._green_pwm.enable() self._blue_pwm.enable() def disable(self): self._red_pwm.disable() self._green_pwm.disable() self._blue_pwm.disable() @property def is_enabled(self): return self._red_pwm.is_enabled @property def pwm_frequency_hz(self): return self._red_pwm.frequency_hz def set_pwm_frequency_hz(self, frequency_hz): self._red_pwm.set_frequency_hz(frequency_hz) self._green_pwm.set_frequency_hz(frequency_hz) self._blue_pwm.set_frequency_hz(frequency_hz) @property def rgb(self): return self.red, self.green, self.blue def set_rgb(self, red_duty_cycle, green_duty_cycle, blue_duty_cycle): self.set_red(red_duty_cycle) self.set_green(green_duty_cycle) self.set_blue(blue_duty_cycle) @property def red(self): return self._red_pwm.duty_cycle def set_red(self, duty_cycle): self._red_pwm.set_duty_cycle(duty_cycle) @property def green(self): return self._green_pwm.duty_cycle def set_green(self, duty_cycle): self._green_pwm.set_duty_cycle(duty_cycle) @property def blue(self): return self._blue_pwm.duty_cycle def set_blue(self, duty_cycle): self._blue_pwm.set_duty_cycle(duty_cycle) def fade(self, red, green, blue, delay_ms=500, step=5): for i in range(0, delay_ms, step): f = float(i) / delay_ms self.set_red(self.red + (red - self.red) * f) self.set_green(self.green + (green - self.green) * f) self.set_blue(self.blue + (blue - self.blue) * f) time.sleep(delay_ms / 1000) PKfH!pyparts/parts/display/__init__.pyPKfH(pyparts/parts/display/screen/__init__.pyPKfH k )pyparts/parts/display/screen/nokia5110.pyimport time from pyparts.parts import base_part _LCD_WIDTH = 84 _LCD_HEIGHT = 48 _NUMBER_OF_LINES = 6 _PIXELS_PER_LINE = _LCD_HEIGHT / _NUMBER_OF_LINES _POWER_DOWN = 0x04 _ENTRY_MODE = 0x02 _FUNCTION_SET = 0x20 _EXTENDED_INSTRUCTION = 0x01 _DISPLAY_CONTROL = 0x08 _DISPLAY_BLANK = 0x0 _DISPLAY_NORMAL = 0x4 _DISPLAY_ALL_ON = 0x1 _DISPLAY_INVERTED = 0x5 _SET_X_ADDR = 0x80 _SET_Y_ADDR = 0x40 _SET_VOP = 0x80 _SET_BIAS = 0x10 _SET_TEMP = 0x04 class Nokia5110(base_part.BasePart): def __init__(self, spi, dc, rst, led): """Creates a Nokia5110 display. Args: spi: SPI peripheral object to communicate with the display over. dc: Digital output. Used to toggle between data and command modes. rst: Digital output. Reset pin. led: LED object. Controls the backlight. """ self._spi = spi self._spi.open() self._spi.set_mode(0) self._spi.set_clock_frequency(4000000) self._dc = dc self._rst = rst self._led = led self._enabled = False def __del__(self): self._spi.close() super(Nokia5110, self).__del__() def send_command(self, command): self._dc.set_low() self._spi.write([command]) def send_extended_command(self, command): # Set extended command mode self.send_command(_FUNCTION_SET | _EXTENDED_INSTRUCTION) self.send_command(command) # Set normal display mode. self.send_command(_FUNCTION_SET) self.send_command(_DISPLAY_CONTROL | _DISPLAY_NORMAL) def send_data(self, data): self._dc.set_high() self._spi.write(data) def enable(self, contrast=50, bias=4): self.reset() self.set_bias(bias) self.set_contrast(contrast) self._enabled = True @property def is_enabled(self): return self._enabled def reset(self): self._rst.set_low() time.sleep(0.1) self._rst.set_high() def set_cursor(self, x, y): self.send_command(_SET_X_ADDR | x) self.send_command(_SET_Y_ADDR | y) def reset_cursor(self): self.set_cursor(0, 0) def display_image(self, image): if image.mode != '1': raise ValueError('Image must be in 1bit mode.') buffer = [] pix = image.load() for row in range(_NUMBER_OF_LINES): for x in range(_LCD_WIDTH): bits = 0 for bit in range(8): bits = bits << 1 bits |= 1 if pix[(x, row * _PIXELS_PER_LINE + 7 - bit)] == 0 else 0 buffer.append(bits) self.clear() self.send_data(buffer) def clear(self): self.reset_cursor() self.send_data([0] * (_LCD_WIDTH * _LCD_HEIGHT / 8)) def set_contrast(self, contrast): contrast = max(0, min(contrast, 0x7f)) self.send_extended_command(_SET_VOP | contrast) def set_bias(self, bias): self.send_extended_command(_SET_BIAS | bias) def set_backlight(self, duty_cycle): self._led.set_duty_cycle(duty_cycle) @property def height(self): return _LCD_HEIGHT @property def width(self): return _LCD_WIDTH @property def lines(self): return _NUMBER_OF_LINES PKfH!pyparts/parts/encoder/__init__.pyPKfHkžzz'pyparts/parts/encoder/rotary_encoder.pyimport math import time import threading from pyparts.parts import base_part class RotaryEncoder(base_part.BasePart): def __init__(self, a_pin, b_pin): self._a = a_pin self._b = b_pin self._last_state = self.get_state() self._last_delta = 0 def get_state(self): a = 1 if self._a.is_high else 0 b = 1 if self._b.is_high else 0 return (a ^ b) | b << 1 def get_delta(self): delta = 0 state = self.get_state() if state != self._last_state: delta = (state - self._last_state) % 4 if delta == 3: delta = -1 elif delta == 2: # Assume two steps in the same direction as the previous step. delta = int(math.copysign(delta, self._last_delta)) self._last_state = state self._last_delta = delta return delta class Worker(threading.Thread): def __init__(self, a_pin, b_pin): super(RotaryEncoder.Worker, self).__init__() self._lock = threading.Lock() self._encoder = RotaryEncoder(a_pin, b_pin) self._delta = 0 self._stop = False def run(self): while not self._stop: delta = self._encoder.get_delta() with self._lock: self._delta += delta time.sleep(0.001) def stop(self): self._stop = True def get_delta(self): with self._lock: delta = self._delta self._delta = 0 return delta PKfH pyparts/parts/sensor/__init__.pyPKfH,pyparts/parts/sensor/temperature/__init__.pyPKfHSS,pyparts/parts/sensor/temperature/max31855.pyfrom pyparts.parts.sensor.temperature import base_temperature_sensor _INTERNAL_TEMP_MASK = 0xfff0 _INTERNAL_TEMP_SHIFT = 4 _INTERNAL_TEMP_MSB_MASK = 0x800 _INTERNAL_DEGREES_C_PER_BIT = 0.0625 _THERMOCOUPLE_TEMP_MASK = 0xfffe0000 _THERMOCOUPLE_TEMP_SHIFT = 18 _THERMOCOUPLE_TEMP_MSB_MASK = 0x2000 _THERMOCOUPLE_DEGREES_C_PER_BIT = 0.25 _FAULT_BIT_MASK = 0x8000 class MAX31855(base_temperature_sensor.BaseTemperatureSensor): def __init__(self, spi_bus): super(MAX31855, self).__init__() self._spi_bus = spi_bus self._spi_bus.open() self._spi_bus.set_mode(0) def __del__(self): self._spi_bus.close() super(MAX31855, self).__del__() def _get_temp_c(self): value = self._read() value &= _THERMOCOUPLE_TEMP_MASK value >>= _THERMOCOUPLE_TEMP_SHIFT if value & _THERMOCOUPLE_TEMP_MSB_MASK: value -= 16384 return value * _THERMOCOUPLE_DEGREES_C_PER_BIT @property def internal_temp_c(self): value = self._read() value &= _INTERNAL_TEMP_MASK value >>= _INTERNAL_TEMP_SHIFT if value & _INTERNAL_TEMP_MSB_MASK: value -= 4096 return value * _INTERNAL_DEGREES_C_PER_BIT @property def internal_temp_f(self): return self._to_f(self.get_internal_temp_c()) def _read(self): values = self._spi_bus.read(4) if not values or len(values) != 4: raise RuntimeError('Unable to read MAX31855 data.') packed_value = (values[0] << 24 | values[1] << 16 | values[2] << 8 | values[3]) if packed_value & _FAULT_BIT_MASK: raise RuntimeError('MAX31855 error. Fault bit set.') return packed_value PKfHL3;pyparts/parts/sensor/temperature/base_temperature_sensor.pyimport abc from pyparts.parts import base_part class BaseTemperatureSensor(base_part.BasePart): __metaclass__ = abc.ABCMeta @abc.abstractmethod def _get_temp_c(self): raise NotImplementedError @property def temp_c(self): return self._get_temp_c() @property def temp_f(self): return self._to_f(self.temp_c) def _to_f(self, temp): return (temp * 9 / 5) + 32 PKfH  )pyparts/platforms/raspberrypi_platform.pyimport RPi.GPIO as gpio from pyparts.platforms import base_platform from pyparts.platforms.gpio import raspberrypi_gpio as rpi_gpio from pyparts.platforms.pwm import raspberrypi_pwm as rpi_pwm from pyparts.platforms.spi import raspberrypi_spi as rpi_spi # Create local copies of the numbering schemes for conveinence. BCM = gpio.BCM BOARD = gpio.BOARD class RaspberryPiPlatform(base_platform.BasePlatform): """Raspberry Pi implementation of a platform. RaspberryPiPlatform provides peripheral devices that can be used by parts to interact with the Raspberry Pi computer. Available peripherals: * DigitalInput * DigitalOutput * PWMOutput * HardwareSPIBus Attributes: _pin_numbering: BCM or BOARD. The current pin numbering scheme. """ def __init__(self, pin_numbering=gpio.BOARD): """Creates a Raspberry Pi platform. Args: pin_numbering: BCM or BOARD. Specifies the pin numbering scheme to use. (default=BOARD) Raises: ValueError: The pin numbering scheme was not one of (BCM, BOARD). """ super(RaspberryPiPlatform, self).__init__() if pin_numbering not in (BCM, BOARD): raise ValueError('Pin numbering must be one of: BCM, BOARD. Got %s' % str(pin_numbering)) gpio.setmode(pin_numbering) self._pin_numbering = pin_numbering def __del__(self): """Destructor. Cleans up GPIO pins.""" gpio.cleanup() @property def pin_numbering(self): """Gets the current pin numbering scheme. Returns: The current pin numbering scheme. One of BCM or BOARD. """ return self._pin_numbering def get_digital_input(self, pin): """Creates a digital input pin on a Raspberry Pi. Args: pin: Integer. Pin number to create the pin on. Returns: A RaspberryPiDigitalInput object for the pin. """ return rpi_gpio.RaspberryPiDigitalInput(pin) def get_digital_output(self, pin): """Creates a digital output pin on a Raspberry Pi. Args: pin: Integer. Pin number to create the pin on. Returns: A RaspberryPiDigitalOutput object for the pin. """ return rpi_gpio.RaspberryPiDigitalOutput(pin) def get_pwm_output(self, pin): """Creates a PWM outut pin on a Raspberry Pi. Args: pin: Integer. Pin number to create the pin on. Returns: A RaspberryPiPWMOutput object for the pin. """ output = rpi_gpio.RaspberryPiDigitalOutput(pin) return rpi_pwm.RaspberryPiPWMOutput(output) def get_hardware_spi_bus(self, port, device): """Creates a hardware based SPI bus on a Raspberry Pi. The Raspberry Pi has an available hardware SPI interface at /dev/spidevX.Y where X and Y are the port and device number respectively. Args: port: Integer. The SPI port number to use. device: Integer. The SPI device number to use. Returns: A RaspberryPiHardwareSPIBus object for the port/device. """ return rpi_spi.RaspberryPiHardwareSPIBus(port, device) def get_software_spi_bus(self, sclk_pin, mosi_pin, miso_pin, ss_pin): """Not implemented.""" raise NotImplementedError def get_i2c_bus(self): """Not implemented.""" raise NotImplementedError PKfHpyparts/platforms/__init__.pyPKfH h"pyparts/platforms/base_platform.pyimport abc class BasePlatform(object): """An abstract class for creating platforms. BasePlatform is what all platforms are created from. Platforms are expected to override the methods of BasePlatform to provide different kinds of peripheral devices like GPIO pins, PWM pins, and communication busses. """ __metaclass__ = abc.ABCMeta @abc.abstractmethod def get_digital_input(self, pin): raise NotImplementedError @abc.abstractmethod def get_digital_output(self, pin): raise NotImplementedError @abc.abstractmethod def get_pwm_output(self, pin): raise NotImplementedError @abc.abstractmethod def get_hardware_spi_bus(self, port, device): raise NotImplementedError @abc.abstractmethod def get_software_spi_bus(self, sclk_pin, mosi_pin, miso_pin, ss_pin): raise NotImplementedError @abc.abstractmethod def get_i2c_bus(self): raise NotImplementedError PKfH!pyparts/platforms/spi/__init__.pyPKfHRR(pyparts/platforms/spi/raspberrypi_spi.pyimport spidev from pyparts.platforms.spi import base_spi class RaspberryPiHardwareSPIBus(base_spi.BaseHardwareSPIBus): """Raspberry Pi implementation of a hardware SPI bus. Attributes: _spi_device: The SPI device used for reading and writing. """ def __init__(self, port, device): """Creates a RaspberryPiHardwareSPIBus. Args: port: Integer. The port to use for the SPI bus. device: Integer. The device to use for the SPI bus. """ super(RaspberryPiHardwareSPIBus, self).__init__(port, device) self._spi_device = spidev.SpiDev() def _open(self): """Opens the SPI bus.""" self._spi_device.open(self._port, self._device) def _close(self): """Closes the SPI bus.""" self._spi_device.close() def _set_clock_frequency_hz(self, frequency_hz): """Sets the clock freqency used by the SPI bus. Args: freqency_hz: Float. The frequency to set the SPI bus clock to. """ self._spi_device.max_speed_hz = frequency_hz def _set_mode(self, mode): """Sets the SPI bus mode. Args: mode: Integer between 0 and 3. The mode to set the SPI bus to. """ self._spi_device.mode = mode def _set_bit_order(self, order): """Sets the SPI bus bit order. Args: order: MSB_FIRST or LSB_FIRST. The bit order to set the SPI bus to. """ self._spi_device.lsbfirst = order == self.LSB_FIRST def write(self, data): """Writes data to the SPI bus. Args: data: Bytearray. Data to write over the SPI bus. """ self._spi_device.writebytes(data) def read(self, length): """Reads at most length bytes from the SPI bus. Args: length: Integer. The maximum number of bytes to read from the SPI bus. Returns: A bytearray of the bytes read from the bus. """ return bytearray(self._spi_device.readbytes(length)) PKfH !pyparts/platforms/spi/base_spi.pyimport abc class BaseSPIBus(object): """A class for creating SPI bus peripherals. BaseSPIBus implements methods to interact with an SPI peripheral. Platforms are expected to subclass BaseSPIBus and provide platform specific implementations of _open, _close, _set_clock_frequency_hz, _set_mode, _set_bit_order, write, and read. Attributes: _is_open: Boolean. Whether or not the SPI bus is open. _clock_frequency_hz: Float. The SPI bus clock frequency. _mode: Integer between 0 and 3. The SPI bus mode. _bit_order: MSB_FIRST or LSB_FIRST. The bit order of the SPI bus. """ __metaclass__ = abc.ABCMeta MSB_FIRST = 0 LSB_FIRST = 1 def __init__(self): """Creates an SPI bus.""" self._is_open = False self._clock_frequency_hz = 0.0 self._mode = 0 self._bit_order = self.MSB_FIRST @abc.abstractmethod def _open(self): """Opens the SPI bus. This method should be implemented by the platform. """ raise NotImplementedError @abc.abstractmethod def _close(self): """Closes the SPI bus. This method should be implemented by the platform. """ raise NotImplementedError @abc.abstractmethod def _set_clock_frequency_hz(self, frequency_hz): """Sets the SPI bus clock frequency. This method should be implemented by the platform. Args: frequency_hz: Integer. Frequency to set the clock to. """ raise NotImplementedError @abc.abstractmethod def _set_mode(self, mode): """Sets the SPI bus mode.. This method should be implemented by the platform. Args: mode: Integer between 0 and 3. The mode to set the SPI bus to. """ raise NotImplementedError @abc.abstractmethod def _set_bit_order(self, order): """Sets the SPI bus bit order. This method should be implemented by the platform. Args: order: MSB_FIRST or LSB_FIRST. The bit order to use for the SPI bus. """ raise NotImplementedError def open(self): """Opens the SPI bus.""" if not self._is_open: self._open() self._is_open = True def close(self): """Closes the SPI bus.""" if self._is_open: self._close() self._is_open = False @property def is_open(self): """Checks if the SPI bus is open or not. Returns: True if the SPI bus is open, False otherwise. """ return self._is_open @property def clock_frequency_hz(self): """Returns the current SPI bus clock frequency. Returns: The current SPI bus clock frequency as a float. """ return self._clock_frequency_hz def set_clock_frequency_hz(self, frequency_hz): """Sets the clock freqency used by the SPI bus. Args: freqency_hz: Float. The frequency to set the SPI bus clock to. Raises: ValueError: Thrown if the frequency is less than 0. RuntimeError: Thrown if the bus isn't open. """ if frequency_hz < 0: raise ValueError('Frequency must be greater than 0. Got %g' % (frequency_hz)) if not self._is_open: raise RuntimeError('SPI device must be opened before setting frequency.') self._set_clock_frequency_hz(frequency_hz) self._clock_frequency_hz = frequency_hz @property def mode(self): """Returns the current SPI bus mode. Returns: The current SPI bus mode as an integer. """ return self._mode def set_mode(self, mode): """Sets the SPI bus mode. Args: mode: Integer between 0 and 3. The mode to set the SPI bus to. Raises: ValueError: Thrown if the mode is not between 0 and 3. RuntimeError: Thrown if the bus isn't open. """ if mode < 0 or mode > 3: raise ValueError('Mode must be between 0 and 3. Got %d' % mode) if not self._is_open: raise RuntimeError('SPI device must be opened before setting mode.') self._set_mode(mode) self._mode = mode @property def bit_order(self): return self._bit_order def set_bit_order(self, order): """Sets the SPI bus bit order. Args: order: MSB_FIRST or LSB_FIRST. The bit order to set the SPI bus to. Raises: ValueError: Thrown if the order is not one of MSB_FIRST or LSB_FIRST. RuntimeError: Thrown if the bus isn't open. """ if order not in (self.MSB_FIRST, self.LSB_FIRST): raise ValueError('Bit order must be MSB_FIRST or LSB_FIRST') if not self._is_open: raise RuntimeError('SPI device must be opened before setting bit order.') self._set_bit_order(order) self._bit_order = order @abc.abstractmethod def write(self, data): """Writes data to the SPI bus. This method should be implemented by the platform. Args: data: Bytearray. Data to write over the SPI bus. """ raise NotImplementedError @abc.abstractmethod def read(self, length): """Reads at most length bytes from the SPI bus. This method should be implemented by the platform. Args: length: Integer. The maximum number of bytes to read from the SPI bus. """ raise NotImplementedError class BaseHardwareSPIBus(BaseSPIBus): """A class for creating SPI buses using hardware peripherals. BaseHardwareSPIBus implements methods to interact with hardware implementations of SPI buses. Attributes: _port: The SPI device port number. _device: The SPI device used for the bus. """ def __init__(self, port, device): """Creates a BaseHardwareSPIBus. Args: port: Integer. The port to use for the SPI bus. device: Integer. The device to use for the SPI bus. """ super(BaseHardwareSPIBus, self).__init__() self._port = port self._device = device @property def port(self): """Gets the SPI device port. Returns: The current port used by the SPI bus. """ return self._port @property def device(self): """Gets the current SPI bus device. Returns: The current SPI bus device. """ return self._device PKfH"pyparts/platforms/gpio/__init__.pyPKfHمee#pyparts/platforms/gpio/base_gpio.pyimport abc # Pin values HIGH = True LOW = False class GPIOError(Exception): """Error type for exceptions while writing to GPIO pins.""" pass class BaseGPIO(object): """A class for creating GPIO type peripherals. BaseGPIO implements basic GPIO functionality such as reading pin values and setting pins high or low. Platforms are expected to subclass BaseGPIO and provide platform specific implementations of _write and _read. Platforms will likely also have to override the constructor to do platform specific initialization tasks like setting the pin to INPUT or OUTPUT. Attributes: _pin: The GPIO pin being used. For example a pin number. _mode: The mode the pin is in. For example INPUT or OUTPUT. """ __metaclass__ = abc.ABCMeta # Pin modes OUTPUT = 0 INPUT = 1 BIDIRECTIONAL = 2 # Internal resistor configurations PUD_UP = 1 PUD_DOWN = 2 def __init__(self, pin, mode, pull_up_down): """Creates a GPIO pin. Args: pin: Integer. The pin to create the GPIO on. mode: INPUT, OUTPUT, or BIDIRECTIONAL. The type of pin mode to use. pull_up_down: PUD_UP or PUD_DOWN. Enable pull up or pull down resistors. """ self._pin = pin self._mode = mode self._pull_up_down = pull_up_down @abc.abstractmethod def _write(self, value): """Writes a value to the pin. This method should be implemented by the platform. Args: value: The value to write to the pin. """ raise NotImplementedError @abc.abstractmethod def _read(self): """Reads the current value of a pin. This method should be implemented by the platform. Returns: The GPIO pin's current value as HIGH or LOW. """ raise NotImplementedError @property def pin_number(self): """Gets the pin number of the GPIO. Returns: The GPIO's pin number. """ return self._pin @property def mode(self): """Gets the mode the GPIO pin is in. Returns: The mode being used by the GPIO pin. """ return self._mode @property def pull_up_down(self): """Gets the state of the GPIO pull up or pull down resistors. Returns: The state of the GPIO pull up or pull down resistors. """ return self._pull_up_down def set_high(self): """Writes a value of HIGH to the GPIO pin. Raises: GPIOError: Trying to write to a pin in INPUT mode will throw an exception. """ if self._mode == self.INPUT: raise GPIOError('Failed to write pin %d high. Pin %d is an input.' % (self._pin, self._pin)) self._write(HIGH) @property def is_high(self): """Checks if the GPIO pin has a value of HIGH. Returns: True if the pin is in a HIGH state, False otherwise. """ return self._read() == HIGH def set_low(self): """Writes a value of LOW to the GPIO pin. Raises: GPIOError: Trying to write to a pin in INPUT mode will throw an exception. """ if self._mode == self.INPUT: raise GPIOError('Failed to write pin %d low. Pin %d is an input.' % (self._pin, self._pin)) self._write(LOW) @property def is_low(self): """Checks if the GPIO pin has a value of LOW. Returns: True if the GPIO pin is in a LOW state, False otherwise. """ return self._read() == LOW class BaseDigitalInput(BaseGPIO): """A class for creating digital input type peripherals. BaseDigitalInput adds interrupt functionality to BaseGPIO. """ # Platforms should implement these INTERRUPT_FALLING = None INTERRUPT_RISING = None INTERRUPT_BOTH = None @abc.abstractmethod def add_interrupt(self, type, callback=None, debounce_time_ms=0): """Adds an interrupt to the digital input pin. Args: type: FALLING, RISING, or BOTH. Edge to trigger the interrupt on. callback: Function. The function to call when the interrupt fires. (default=None) debounce_time_ms: Integer. Debounce time to put on the interrupt. (default=0) """ raise NotImplementedError @abc.abstractmethod def wait_for_edge(self, type): """Blocks until the edge is detected. Args: type: RISING, FALLING, or BOTH. Edge to detect before unblocking. """ raise NotImplementedError def remove_interrupt(self): """Removes all interrupts from the digital input pin.""" raise NotImplementedError PKfH F F *pyparts/platforms/gpio/raspberrypi_gpio.pyimport RPi.GPIO as rpi_gpio from pyparts.platforms.gpio import base_gpio class RaspberryPiGPIO(base_gpio.BaseGPIO): """Raspberry Pi implementation of a GPIO peripheral.""" def __init__(self, pin, mode, pull_up_down=base_gpio.BaseGPIO.PUD_UP): """Creates a GPIO pin for a Raspberry Pi. Args: pin: Integer. The pin number to create the GPIO on. mode: INPUT or OUTPUT. The pin mode to put the GPIO in. pull_up_down: PUD_UP or PUD_DOWN. Enable pull up or pull down resistors. (default=PUD_UP) """ super(RaspberryPiGPIO, self).__init__(pin, mode, pull_up_down) if self._mode == self.INPUT: pin_type = rpi_gpio.IN else: pin_type = rpi_gpio.OUT if pull_up_down == self.PUD_UP: pud = rpi_gpio.PUD_UP else: pud = rpi_gpio.PUD_DOWN rpi_gpio.setup(self._pin, pin_type, pull_up_down=pud) def _write(self, value): """Writes a value to the pin. Args: value: HIGH or LOW. The value to write to the pin. """ rpi_gpio.output(self._pin, value) def _read(self): """Reads the current value from the pin. Returns: The GPIO pin's current value as HIGH or LOW. """ return rpi_gpio.input(self._pin) class RaspberryPiDigitalInput(base_gpio.BaseDigitalInput, RaspberryPiGPIO): """Raspberry Pi implementation of a DigitalInput.""" INTERRUPT_FALLING = rpi_gpio.FALLING INTERRUPT_RISING = rpi_gpio.RISING INTERRUPT_BOTH = rpi_gpio.BOTH def __init__(self, pin): """Creates a DigitalInput pin for a Raspberry Pi. Args: pin: Integer. The pin to create the DigitalInput on. """ super(RaspberryPiDigitalInput, self).__init__(pin, self.INPUT) def add_interrupt(self, type, callback=None, debounce_time_ms=0): """Creates an interrupt on the digital input pin. Args: type: FALLING, RISING, or BOTH. Edge type to trigger the interrupt on. callback. Function. The function to call when the interrupt fires. (default=None) debounce_time_ms: Integer. Debounce time to add to the interrupt. (default=0) """ rpi_gpio.add_event_detect(self._pin, type, callback, debounce_time_ms) def wait_for_edge(self, type): """Block until an edge is detected. Args: type: FALLING, RISING, or BOTH. Edge type to detect before unblocking. """ rpi_gpio.wait_for_edge(self._pin, type) def remove_interrupt(self): """Removes all interrupts from the pin.""" rpi_gpio.remove_event_detection(self._pin) class RaspberryPiDigitalOutput(RaspberryPiGPIO): """Raspberry Pi implementation of a DigitalOutput.""" def __init__(self, pin): """Creates a DigitalOutput pin for a Raspberry Pi. Args: pin: Integer. The pin to create the DigitalOutput on. """ super(RaspberryPiDigitalOutput, self).__init__(pin, base_gpio.BaseGPIO.OUTPUT) PKfH!pyparts/platforms/pwm/__init__.pyPKfH@!pyparts/platforms/pwm/base_pwm.pyimport abc class BasePWM(object): """A class for creating PWM type peripherals. BasePWM implements methods to interact with a PWM interface. Platforms are expected to subclass BasePWM and provide platform specific implementations of _enable, _disable, _set_duty_cycle, and _set_frequency_hz. Attributes: _enabled: Boolean. Whether or not the PWM is enabled. _output_pin: DigitalOutput. The DigitalOutput GPIO pin being used for PWM. _duty_cycle. Float from 0.0 to 100.0. The current duty cycle as a percent. _frequency_hz: Float. The current PWM frequency in Hertz. """ __metaclass__ = abc.ABCMeta def __init__(self, output_pin): """Creates a PWM output. Args: output_pin: A DigitalOutput to use for PWM output. """ self._enabled = False self._output_pin = output_pin self._duty_cycle = 0.0 self._frequency_hz = 0.0 @abc.abstractmethod def _enable(self): """Enables the PWM output. This method should be implemented by the platform. """ raise NotImplementedError @abc.abstractmethod def _disable(self): """Disables the PWM output. This method should be implemented by the platform. """ raise NotImplementedError @abc.abstractmethod def _set_duty_cycle(self, duty_cycle): """Sets the duty cycle of the PWM output. This method should be implemented by the platform. Args: duty_cycle: Float from 0.0 to 100.0. Duty cycle to set PWM output to. """ raise NotImplementedError @abc.abstractmethod def _set_frequency_hz(self, freqency_hz): """Sets the PWM frequency used for PWM output. This method should be implemented by the platform. Args: frequency_hz: Float. The frequency to set the PWM output to. """ raise NotImplementedError def enable(self): """Enables the PWM output.""" if not self._enabled: self._enable() self._enabled = True def disable(self): """Disables the PWM output.""" if self._enabled: self._disable() self._enabled = False @property def is_enabled(self): """Checks if the PWM output is enabled. Returns: True if the PWM output is enabled, Flase otherwise. """ return self._enabled @property def duty_cycle(self): """Gets the current value for the PWM output's duty cycle. Returns: The current duty cycle as a float. """ return self._duty_cycle def set_duty_cycle(self, duty_cycle): """Sets the duty cycle of the PWM output. Args: duty_cycle: Float from 0.0 to 100.0. Duty cycle to set the PWM output to. Raises: ValueError: Thrown if duty_cycle is not between 0.0 and 100.0 """ if duty_cycle < 0 or duty_cycle > 100: raise ValueError('Duty cycle must be between 0 and 100. Got: %d' % duty_cycle) self._set_duty_cycle(duty_cycle) self._duty_cycle = duty_cycle @property def frequency_hz(self): """Gets the current frequency used by the PWM output. Returns: The current frequency in Hertz as a float. """ return self._frequency_hz def set_frequency_hz(self, frequency_hz): """Sets the PWM frequency used for PWM output. Args: frequency_hz: Float. The frequency to set the PWM output to. Raises: ValueError: Thrown if the frequency is negative. """ if frequency_hz < 0: raise ValueError('Frequency must be greater than 0. Got: %d' % frequency_hz) self._set_frequency_hz(frequency_hz) self._frequency_hz = frequency_hz @property def pin_number(self): """Gets the pin number of the PWM output. Returns: The pin number as an integer. """ return self._output_pin.pin_number PKfHdJ&(pyparts/platforms/pwm/raspberrypi_pwm.pyimport RPi.GPIO as gpio from pyparts.platforms.pwm import base_pwm class RaspberryPiPWMOutput(base_pwm.BasePWM): """Raspberry Pi implementation of a PWM peripheral. Attributes: _pwm_pin: DigitalOutput. The pin being used for PWM. """ def __init__(self, output_pin, frequency_hz=2000): """Creates a PWM output on a DigitalOutput pin. Args: output_pin: DigitalOutput. A DigitalOutput pin to use for PWM output. frequency_hz: Float. PWM frequency to use. (default=2000) """ super(RaspberryPiPWMOutput, self).__init__(output_pin) self._pwm_pin = gpio.PWM(self._output_pin.pin_number, frequency_hz) def _enable(self): """Enables the PWM output.""" self._pwm_pin.start(0) def _disable(self): """Disables the PWM output.""" self._pwm_pin.stop() def _set_duty_cycle(self, duty_cycle): """Sets the duty cycle for the PWM output. Args: duty_cycle: Float from 0.0 to 100.0. Duty cycle to set the PWM output to. """ self._pwm_pin.ChangeDutyCycle(duty_cycle) def _set_frequency_hz(self, frequency_hz): """Sets the frequency for the PWM output. Args: frequency_hz: Float. The frequency to set the PWM output to in Hertz. """ self._pwm_pin.ChangeFrequency(frequency_hz) PKfHpyparts/logic/__init__.pyPKfHqkc::pyparts/logic/pid_controller.pyimport time import threading class PIDController(object): """A PID controller for controlling output based on desired value. Attributes: _kp: Integer. The constant term. _ki: Integer. The integrator term. _kd: Integer. The differential term. _prev_error: Float. The error value from the previous calculation. _cp: Integer. Temp storage for constant term. _ci: Integer. Accumulator for integrator error value. _cd: Integer. Accumulator for differntial error value. _prev_time: Integer. The time of the previous calculation. """ def __init__(self, kp, ki, kd): """Creates a PIDController. Args: kp: Integer. The constant term. ki: Integer. The integrator term. kd: Integer. The differential term. """ self._kp = kp self._ki = ki self._kd = kd self._prev_error = 0 self._cp = 0 self._ci = 0 self._cd = 0 self._prev_time = time.time() def get_output(self, error): """Does a PID calculation and returns the new output value. Args: error: Float. The current error value. Returns: Float. The output of the PID controller for the current error. """ self._current_time = time.time() dt = self._current_time - self._prev_time de = error - self._prev_error self._cp = self._kp * error self._ci += error * dt self._cd = 0 if dt > 0: self._cd = de / dt self._prev_time = self._current_time self._prev_error = error return self._cp + (self._ki * self._ci) + (self._kd * self._cd) class Worker(threading.Thread): """A worker for a PIDController to allow background processing. PIDController.Worker can be used to run a PID controller loop in a background thread. The worker calls the input function to get the current error value. It then computes the output value using the PID controller and calls the output function with that value. Attributes: _controller: PIDController. The PIDController used to calculate output values. _input_func: Function. Function called to determine error. _output_func: Function. Function called with the output of the PID. _set_point: Float. The desired value. _stop: Boolean. Set to true to disable the controller. """ def __init__(self, kp, kd, ki, input_func, output_func): """Creates a PIDController.Worker. Args: kp: Integer. The constant term. ki: Integer. The integrator term. kd: Integer. The differential term. input_func: Function. Function called to calculate error. output_func: Function. Function called with the output of the PID. """ super(PIDController.Worker, self).__init__() self._controller = PIDController(kp, ki, kd) self._input_func = input_func self._output_func = output_func self._set_point = 0 self._stop = False @property def desired_value(self): """Gets the current desired value. Returns: Float. The current desired value. """ return self._set_point def set_desired_value(self, value): """Sets the desired output value. Args: value: Float. The value to try and achieve. """ self._set_point = value def stop(self): """Stops the controller.""" self._stop = True def run(self): """Loop for calculating error, running the PID, and handling output.""" while not self._stop: current_val = self._input_func() error = self._set_point - current_val self._output_func(self._controller.get_output(error)) PKfH^- .python_pyparts-1.0.0.dist-info/DESCRIPTION.rstUNKNOWN PKfH$ß,python_pyparts-1.0.0.dist-info/metadata.json{"classifiers": ["Development Status :: 3 - Alpha", "Intended Audience :: Developers", "Natural Language :: English", "License :: OSI Approved :: MIT License", "Programming Language :: Python", "Topic :: Software Development :: Libraries :: Python Modules"], "extensions": {"python.details": {"contacts": [{"email": "sean@seanwatson.io", "name": "Sean Watson", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://gitlab.com/seanwatson/pyparts"}}}, "extras": [], "generator": "bdist_wheel (0.26.0)", "keywords": ["raspberrypi", "beagleboard", "pine64", "mbed", "embedded", "hardware", "gpio", "spi", "pwm", "electronics"], "license": "MIT", "metadata_version": "2.0", "name": "python-pyparts", "run_requires": [{"requires": ["RPi.GPIO", "spidev"]}], "summary": "A cross platform library for embedded hardware development using single board computers.", "version": "1.0.0"}PKfHi,python_pyparts-1.0.0.dist-info/top_level.txtpyparts PKfHndnn$python_pyparts-1.0.0.dist-info/WHEELWheel-Version: 1.0 Generator: bdist_wheel (0.26.0) Root-Is-Purelib: true Tag: py2-none-any Tag: py3-none-any PKfHa5'python_pyparts-1.0.0.dist-info/METADATAMetadata-Version: 2.0 Name: python-pyparts Version: 1.0.0 Summary: A cross platform library for embedded hardware development using single board computers. Home-page: https://gitlab.com/seanwatson/pyparts Author: Sean Watson Author-email: sean@seanwatson.io License: MIT Keywords: raspberrypi beagleboard pine64 mbed embedded hardware gpio spi pwm electronics Platform: UNKNOWN Classifier: Development Status :: 3 - Alpha Classifier: Intended Audience :: Developers Classifier: Natural Language :: English Classifier: License :: OSI Approved :: MIT License Classifier: Programming Language :: Python Classifier: Topic :: Software Development :: Libraries :: Python Modules Requires-Dist: RPi.GPIO Requires-Dist: spidev UNKNOWN PKfH%tt t %python_pyparts-1.0.0.dist-info/RECORDpyparts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 pyparts/logic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 pyparts/logic/pid_controller.py,sha256=4zKe7ArpY5Eh0XfJZopYTMmzh3qODqn7Y_mCEtmxAwE,3642 pyparts/parts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 pyparts/parts/base_part.py,sha256=fYppLo-lRHHtiKF_A7r1GSXhGPaqGgHhoqSNZW7_QNI,33 pyparts/parts/display/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 pyparts/parts/display/screen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 pyparts/parts/display/screen/nokia5110.py,sha256=jHoA4YMBdIxNp4QubPv9uwLSEUq93s6OyYplInKBJd4,3019 pyparts/parts/encoder/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 pyparts/parts/encoder/rotary_encoder.py,sha256=Ts-ssq61tMlyZcHWtRp8jWTPKnKZzrA0datgKt-z1To,1402 pyparts/parts/led/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 pyparts/parts/led/rgb_led.py,sha256=ama4CcJmIbzATu4IaXs9QS1LICYGnAaCxVMFIuM1P-Y,1823 pyparts/parts/sensor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 pyparts/parts/sensor/temperature/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 pyparts/parts/sensor/temperature/base_temperature_sensor.py,sha256=v1KWoeWElTiqO4ponEAMa-HBfxidi9RIkGgrUxLr-IM,395 pyparts/parts/sensor/temperature/max31855.py,sha256=lHKoGozPE8m79k-SrpfbVf_gvwnvhO8rTxWlPpeT7r0,1619 pyparts/parts/switch/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 pyparts/parts/switch/button.py,sha256=djHJT3MSqLyc30Pu_HPd4_I-KZ8kU16EO9bvm7KREvw,1155 pyparts/platforms/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 pyparts/platforms/base_platform.py,sha256=aLfy807-OSKHDwGrItDNwivrVQnNSr4DxkgM2SatG08,917 pyparts/platforms/raspberrypi_platform.py,sha256=9Ur0zW6mPV1UzUauCdt4BdB644EPf0dSPB3ztWOgGaQ,3242 pyparts/platforms/gpio/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 pyparts/platforms/gpio/base_gpio.py,sha256=vYMeQgTClpT0nyle-sSnFWxbU3uZK2V-HJjaHVXWWRs,4453 pyparts/platforms/gpio/raspberrypi_gpio.py,sha256=U0t_vpkPyNX2epXC035pFDNtDQ1WX3Kn92xvn0NzICo,2886 pyparts/platforms/pwm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 pyparts/platforms/pwm/base_pwm.py,sha256=eOxCVzo-jJebHjmJBn_RAOrb6pjWKGa-GU49XQuqOFo,3767 pyparts/platforms/pwm/raspberrypi_pwm.py,sha256=qef3SXhOKIioZvmhGwXMGrhK0EoIi9EGelRKdaeExug,1285 pyparts/platforms/spi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 pyparts/platforms/spi/base_spi.py,sha256=cND7rHV6gLgHtu9SyN3M_WoPMBwQmtCpGq91TPtdt2I,6030 pyparts/platforms/spi/raspberrypi_spi.py,sha256=O38sH5ECYwynXM61q1zEIcPi3WlZPu8h6I-lQyczVME,1874 pyparts/systems/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 pyparts/systems/temperature_controller.py,sha256=Zd85zPms8BK1AtZoRNpBDb70GtI_DjSO6RIW5yBo52M,3035 python_pyparts-1.0.0.dist-info/DESCRIPTION.rst,sha256=OCTuuN6LcWulhHS3d5rfjdsQtW22n7HENFRh6jC6ego,10 python_pyparts-1.0.0.dist-info/METADATA,sha256=cUgMDRoXlwZm8-NGw8Q3Ityq1ZSmaUfTqS6Kol7vmtU,730 python_pyparts-1.0.0.dist-info/RECORD,, python_pyparts-1.0.0.dist-info/WHEEL,sha256=GrqQvamwgBV4nLoJe0vhYRSWzWsx7xjlt74FT0SWYfE,110 python_pyparts-1.0.0.dist-info/metadata.json,sha256=BS0fK25MV17JdHRxUTRaXHTgnl0BEwVtrZV-Jt8zAco,927 python_pyparts-1.0.0.dist-info/top_level.txt,sha256=RmGeBXBwl9NtN3OajiJD4ddn69FCGF3hQiRnlqSlURU,8 PKfHpyparts/__init__.pyPKfH1pyparts/systems/__init__.pyPKfH" )jpyparts/systems/temperature_controller.pyPKfH pyparts/parts/__init__.pyPKfH|A?.!! pyparts/parts/base_part.pyPKfH  pyparts/parts/switch/__init__.pyPKfHZ pyparts/parts/switch/button.pyPKfHpyparts/parts/led/__init__.pyPKfH9|Tpyparts/parts/led/rgb_led.pyPKfH!pyparts/parts/display/__init__.pyPKfH(pyparts/parts/display/screen/__init__.pyPKfH k )2pyparts/parts/display/screen/nokia5110.pyPKfH!D&pyparts/parts/encoder/__init__.pyPKfHkžzz'&pyparts/parts/encoder/rotary_encoder.pyPKfH B,pyparts/parts/sensor/__init__.pyPKfH,,pyparts/parts/sensor/temperature/__init__.pyPKfHSS,,pyparts/parts/sensor/temperature/max31855.pyPKfHL3;g3pyparts/parts/sensor/temperature/base_temperature_sensor.pyPKfH  )K5pyparts/platforms/raspberrypi_platform.pyPKfH