diff --git a/.gitignore b/.gitignore index e2e9708..53b63f4 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ thumbs.db storage/profiles config.py +.idea/* diff --git a/README.md b/README.md index 038b167..d9497dd 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,10 @@ If you want to deploy the code on a PI for production: This **only applies to non-Raspbian installations**, since Raspbian ships RPi.GPIO with the default installation. +If you also want to use the in-kernel SPI drivers with a MAX31855 sensor: + + $ sudo pip install Adafruit-MAX31855 + ### Clone repo $ git clone https://github.com/apollo-ng/picoReflow.git diff --git a/config.py.EXAMPLE b/config.py.EXAMPLE index 2dbf4c0..f002627 100644 --- a/config.py.EXAMPLE +++ b/config.py.EXAMPLE @@ -35,15 +35,25 @@ heater_invert = 0 # switches the polarity of the heater control ### Inputs gpio_door = 18 -### Thermocouple Adapter selection (MAX31855 or MAX6675) +### Thermocouple Adapter selection: +# max31855 - bitbang SPI interface +# max31855spi - kernel SPI interface +# max6675 - bitbang SPI interface max31855 = 1 max6675 = 0 +max31855spi = 0 # if you use this one, you MUST reassign the default GPIO pins -### Thermocouple I2C Connection +### Thermocouple Connection (using bitbang interfaces) gpio_sensor_cs = 27 gpio_sensor_clock = 22 gpio_sensor_data = 17 +### Thermocouple SPI Connection (using adafrut drivers + kernel SPI interface) +spi_sensor_chip_id = 0 + +### amount of time, in seconds, to wait between reads of the thermocouple +sensor_time_wait = .5 + ######################################################################## # diff --git a/lib/max31855spi.py b/lib/max31855spi.py new file mode 100644 index 0000000..b3eb362 --- /dev/null +++ b/lib/max31855spi.py @@ -0,0 +1,36 @@ +#!/usr/bin/python +import logging + +from Adafruit_MAX31855 import MAX31855 + +class MAX31855SPI(object): + '''Python driver for [MAX38155 Cold-Junction Compensated Thermocouple-to-Digital Converter](http://www.maximintegrated.com/datasheet/index.mvp/id/7273) + Requires: + - adafruit's MAX31855 SPI-only device library + + ''' + def __init__(self, spi_dev): + self.max31855 = MAX31855.MAX31855(spi=spi_dev) + self.log = logging.getLogger(__name__) + + def get(self): + '''Reads SPI bus and returns current value of thermocouple.''' + state = self.max31855.readState() + self.log.debug("status %s" % state) + if state['openCircuit']: + raise MAX31855Error('Not Connected') + elif state['shortGND']: + raise MAX31855Error('Short to Ground') + elif state['shortVCC']: + raise MAX31855Error('Short to VCC') + elif state['fault']: + raise MAX31855Error('Unknown Error') + return self.max31855.readLinearizedTempC() + + +class MAX31855SPIError(Exception): + def __init__(self, value): + self.value = value + + def __str__(self): + return repr(self.value) diff --git a/lib/oven.py b/lib/oven.py index 74059cb..fadd42d 100644 --- a/lib/oven.py +++ b/lib/oven.py @@ -10,18 +10,31 @@ import config log = logging.getLogger(__name__) try: - if (config.max31855 == config.max6675): - log.error("choose (only) one converter IC") - exit() + if config.max31855 + config.max6675 + config.max31855spi > 1: + log.error("choose (only) one converter IC") + exit() if config.max31855: - from max31855 import MAX31855, MAX31855Error - log.info("import MAX31855") + from max31855 import MAX31855, MAX31855Error + log.info("import MAX31855") + if config.max31855spi: + import Adafruit_GPIO.SPI as SPI + from max31855spi import MAX31855SPI, MAX31855SPIError + log.info("import MAX31855SPI") + spi_reserved_gpio = [7, 8, 9, 10, 11] + if config.gpio_air in spi_reserved_gpio: + raise Exception("gpio_air pin %s collides with SPI pins %s" % (config.gpio_air, spi_reserved_gpio)) + if config.gpio_cool in spi_reserved_gpio: + raise Exception("gpio_cool pin %s collides with SPI pins %s" % (config.gpio_cool, spi_reserved_gpio)) + if config.gpio_door in spi_reserved_gpio: + raise Exception("gpio_door pin %s collides with SPI pins %s" % (config.gpio_door, spi_reserved_gpio)) + if config.gpio_heat in spi_reserved_gpio: + raise Exception("gpio_heat pin %s collides with SPI pins %s" % (config.gpio_heat, spi_reserved_gpio)) if config.max6675: - from max6675 import MAX6675, MAX6675Error - log.info("import MAX6675") + from max6675 import MAX6675, MAX6675Error + log.info("import MAX6675") sensor_available = True except ImportError: - log.warning("Could not initialize temperature sensor, using dummy values!") + log.exception("Could not initialize temperature sensor, using dummy values!") sensor_available = False try: @@ -44,7 +57,7 @@ class Oven (threading.Thread): STATE_IDLE = "IDLE" STATE_RUNNING = "RUNNING" - def __init__(self, simulate=False, time_step=0.5): + def __init__(self, simulate=False, time_step=config.sensor_time_wait): threading.Thread.__init__(self) self.daemon = True self.simulate = simulate @@ -147,14 +160,14 @@ class Oven (threading.Thread): self.heat = 1.0 if gpio_available: if config.heater_invert: - GPIO.output(config.gpio_heat, GPIO.LOW) + GPIO.output(config.gpio_heat, GPIO.LOW) else: GPIO.output(config.gpio_heat, GPIO.HIGH) else: self.heat = 0.0 if gpio_available: if config.heater_invert: - GPIO.output(config.gpio_heat, GPIO.HIGH) + GPIO.output(config.gpio_heat, GPIO.HIGH) else: GPIO.output(config.gpio_heat, GPIO.LOW) @@ -211,22 +224,29 @@ class TempSensorReal(TempSensor): def __init__(self, time_step): TempSensor.__init__(self, time_step) if config.max6675: - log.info("init MAX6675") - self.thermocouple = MAX6675(config.gpio_sensor_cs, + log.info("init MAX6675") + self.thermocouple = MAX6675(config.gpio_sensor_cs, config.gpio_sensor_clock, config.gpio_sensor_data, config.temp_scale) if config.max31855: - log.info("init MAX31855") - self.thermocouple = MAX31855(config.gpio_sensor_cs, + log.info("init MAX31855") + self.thermocouple = MAX31855(config.gpio_sensor_cs, config.gpio_sensor_clock, config.gpio_sensor_data, config.temp_scale) + if config.max31855spi: + log.info("init MAX31855-spi") + self.thermocouple = MAX31855SPI(spi_dev=SPI.SpiDev(port=0, device=config.spi_sensor_chip_id)) + def run(self): while True: - self.temperature = self.thermocouple.get() + try: + self.temperature = self.thermocouple.get() + except Exception: + log.exception("problem reading temp") time.sleep(self.time_step)