Skip to content

Commit

Permalink
Added k-type thermocouple example and test, updated requirements.txt …
Browse files Browse the repository at this point in the history
…to proper package name, PEP-8'd some shit, removed unused imports.
  • Loading branch information
johnrbnsn committed Jun 12, 2019
1 parent 436a672 commit 785eb23
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 87 deletions.
46 changes: 25 additions & 21 deletions Adafruit_MAX31856/max31856.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
'''max31856.py
"""
max31856.py
Class which defines interaction with the MAX31856 sensor.
Copyright (c) 2016 John Robinson
Copyright (c) 2019 John Robinson
Author: John Robinson
Permission is hereby granted, free of charge, to any person obtaining a copy
Expand All @@ -22,14 +23,14 @@
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
'''
"""
import logging
import warnings
import math

import Adafruit_GPIO as Adafruit_GPIO
import Adafruit_GPIO.SPI as SPI


class MAX31856(object):
"""Class to represent an Adafruit MAX31856 thermocouple temperature
measurement board.
Expand Down Expand Up @@ -88,9 +89,9 @@ class MAX31856(object):
MAX31856_S_TYPE = 0x6 # Read S Type Thermocouple
MAX31856_T_TYPE = 0x7 # Read T Type Thermocouple


def __init__(self, tc_type=MAX31856_T_TYPE, avgsel=0x0, software_spi=None, hardware_spi=None, gpio=None):
"""Initialize MAX31856 device with software SPI on the specified CLK,
"""
Initialize MAX31856 device with software SPI on the specified CLK,
CS, and DO pins. Alternatively can specify hardware SPI by sending an
SPI.SpiDev device in the spi parameter.
Expand Down Expand Up @@ -122,7 +123,8 @@ def __init__(self, tc_type=MAX31856_T_TYPE, avgsel=0x0, software_spi=None, hardw
self._spi = SPI.BitBang(gpio, software_spi['clk'], software_spi['di'],
software_spi['do'], software_spi['cs'])
else:
raise ValueError('Must specify either spi for for hardware SPI or clk, cs, and do for softwrare SPI!')
raise ValueError(
'Must specify either spi for for hardware SPI or clk, cs, and do for softwrare SPI!')
self._spi.set_clock_hz(5000000)
# According to Wikipedia (on SPI) and MAX31856 Datasheet:
# SPI mode 1 corresponds with correct timing, CPOL = 0, CPHA = 1
Expand All @@ -137,7 +139,8 @@ def __init__(self, tc_type=MAX31856_T_TYPE, avgsel=0x0, software_spi=None, hardw

@staticmethod
def _cj_temp_from_bytes(msb, lsb):
"""Takes in the msb and lsb from a Cold Junction (CJ) temperature reading and converts it
"""
Takes in the msb and lsb from a Cold Junction (CJ) temperature reading and converts it
into a decimal value.
This function was removed from readInternalTempC() and moved to its own method to allow for
Expand All @@ -164,7 +167,8 @@ def _cj_temp_from_bytes(msb, lsb):

@staticmethod
def _thermocouple_temp_from_bytes(byte0, byte1, byte2):
"""Converts the thermocouple byte values to a decimal value.
"""
Converts the thermocouple byte values to a decimal value.
This function was removed from readInternalTempC() and moved to its own method to allow for
easier testing with standard values.
Expand Down Expand Up @@ -193,7 +197,9 @@ def _thermocouple_temp_from_bytes(byte0, byte1, byte2):
return temp_c

def read_internal_temp_c(self):
"""Return internal temperature value in degrees celsius."""
"""
Return internal temperature value in degrees celsius.
"""
val_low_byte = self._read_register(self.MAX31856_REG_READ_CJTL)
val_high_byte = self._read_register(self.MAX31856_REG_READ_CJTH)

Expand All @@ -202,9 +208,10 @@ def read_internal_temp_c(self):

return temp_c


def read_temp_c(self):
"""Return the thermocouple temperature value in degrees celsius."""
"""
Return the thermocouple temperature value in degrees celsius.
"""
val_low_byte = self._read_register(self.MAX31856_REG_READ_LTCBL)
val_mid_byte = self._read_register(self.MAX31856_REG_READ_LTCBM)
val_high_byte = self._read_register(self.MAX31856_REG_READ_LTCBH)
Expand All @@ -215,7 +222,6 @@ def read_temp_c(self):

return temp_c


def read_fault_register(self):
"""Return bytes containing fault codes and hardware problems.
Expand All @@ -224,9 +230,9 @@ def read_fault_register(self):
reg = self._read_register(self.MAX31856_REG_READ_FAULT)
return reg


def _read_register(self, address):
'''Reads a register at address from the MAX31856
"""
Reads a register at address from the MAX31856
Args:
address (8-bit Hex): Address for read register. Format 0Xh. Constants listed in class
Expand All @@ -238,8 +244,7 @@ def _read_register(self, address):
byte, the dummy byte is only used to keep the SPI clock ticking as we read in the
value. The first returned byte is discarded because no data is transmitted while
specifying the register address.
'''

"""
raw = self._spi.transfer([address, 0x00])
if raw is None or len(raw) != 2:
raise RuntimeError('Did not read expected number of bytes from device!')
Expand All @@ -249,23 +254,22 @@ def _read_register(self, address):
(address & 0xFFFF), (value & 0xFFFF)))
return value


def _write_register(self, address, write_value):
'''Writes to a register at address from the MAX31856
"""
Writes to a register at address from the MAX31856
Args:
address (8-bit Hex): Address for read register. Format 0Xh. Constants listed in class
as MAX31856_REG_WRITE_*
write_value (8-bit Hex): Value to write to the register
'''
"""
self._spi.transfer([address, write_value])
self._logger.debug('Wrote Register: 0x{0:02X}, Value 0x{1:02X}'.format((address & 0xFF),
(write_value & 0xFF)))

# If we've gotten this far without an exception, the transmission must've gone through
return True


# Deprecated Methods
def readTempC(self): #pylint: disable-msg=invalid-name
"""Depreciated due to Python naming convention, use read_temp_c instead
Expand Down
97 changes: 62 additions & 35 deletions Adafruit_MAX31856/test_MAX31856.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
# Copyright (c) 2016 John Robinson
# Author: John Robinson
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
"""
Copyright (c) 2019 John Robinson
Author: John Robinson
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
"""

# Global Imports
import logging
Expand All @@ -28,7 +30,10 @@
# Local Imports
from max31856 import MAX31856 as MAX31856

logging.basicConfig(filename='test_MAX31856.log', level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logging.basicConfig(
filename='test_MAX31856.log',
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
_logger = logging.getLogger(__name__)

class Adafruit_MAX31856(unittest.TestCase):
Expand All @@ -37,12 +42,12 @@ def tearDown(self):
GPIO.cleanup()

#def test_software_spi_initialize(self):
#'''Checks to see if the sensor can initialize on the software SPI interface.
#"""Checks to see if the sensor can initialize on the software SPI interface.

#Will fail if it cannot find the MAX31856 library or any dependencies.
#Test only checks to see that the sensor can be initialized in Software, does not check the
#hardware connection.
#'''
#"""
#_logger.debug('test_software_SPI_initialize()')
## Raspberry Pi software SPI configuration.
#software_spi = {"clk": 25, "cs": 8, "do": 9, "di": 10}
Expand All @@ -54,12 +59,13 @@ def tearDown(self):
#self.assertTrue(False)

def test_hardware_spi_initialize(self):
'''Checks to see if the sensor can initialize on the hardware SPI interface.
"""
Checks to see if the sensor can initialize on the hardware SPI interface.
Will fail if it cannot find the MAX31856 library or any dependencies.
Test only checks to see that the sensor can be initialized in Software, does not check the
hardware connection.
'''
"""
_logger.debug('test_hardware_SPI_initialize()')
# Raspberry Pi hardware SPI configuration.
spi_port = 0
Expand All @@ -72,9 +78,10 @@ def test_hardware_spi_initialize(self):
self.assertTrue(False)

def test_get_register_reading(self):
'''Checks to see if we can read a register from the device. Good test for correct
"""
Checks to see if we can read a register from the device. Good test for correct
connectivity.
'''
"""
_logger.debug('test_get_register_reading()')
# Raspberry Pi hardware SPI configuration.
spi_port = 0
Expand All @@ -92,8 +99,8 @@ def test_get_register_reading(self):
self.assertTrue(False)

#def test_get_temperaure_reading_software_spi(self):
#'''Checks to see if we can read a temperature from the board, using software SPI
#'''
#"""Checks to see if we can read a temperature from the board, using software SPI
#"""
#_logger.debug('test_get_temperature_reading_software_spi')
## Raspberry Pi software SPI configuration.
#software_spi = {"clk": 25, "cs": 8, "do": 9, "di": 10}
Expand All @@ -107,8 +114,9 @@ def test_get_register_reading(self):
#self.assertTrue(False)

def test_get_temperaure_reading(self):
'''Checks to see if we can read a temperature from the board, using Hardware SPI
'''
"""
Checks to see if we can read a temperature from the board, using Hardware SPI
"""
_logger.debug('test_get_temperaure_reading')
# Raspberry Pi hardware SPI configuration.
spi_port = 0
Expand All @@ -123,8 +131,9 @@ def test_get_temperaure_reading(self):
self.assertTrue(False)

def test_get_internal_temperaure_reading(self):
'''Checks to see if we can read a temperature from the board, using Hardware SPI
'''
"""
Checks to see if we can read a temperature from the board, using Hardware SPI
"""
_logger.debug('test_get_internal_temperature_reading()')
# Raspberry Pi hardware SPI configuration.
spi_port = 0
Expand All @@ -138,9 +147,27 @@ def test_get_internal_temperaure_reading(self):
else:
self.assertTrue(False)

def test_get_internal_temperaure_reading_k_type(self):
"""
Checks to see if we can read a temperature from the board, using Hardware SPI, and K type thermocouple
"""
_logger.debug('test_get_internal_temperature_reading()')
# Raspberry Pi hardware SPI configuration.
spi_port = 0
spi_device = 0
sensor = MAX31856(hardware_spi=SPI.SpiDev(spi_port, spi_device), tc_type=MAX31856.MAX31856_K_TYPE)

temp = sensor.read_internal_temp_c()

if temp:
self.assertTrue(True)
else:
self.assertTrue(False)

def test_temperature_byte_conversions(self):
'''Checks the byte conversion for various known temperature byte values.
'''
"""
Checks the byte conversion for various known temperature byte values.
"""
_logger.debug('test_temperature_byte_conversions()')

#-------------------------------------------#
Expand Down
26 changes: 17 additions & 9 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ You can confirm that SPI is enabled by checking to see you get a response simila
Installing Library
------------------

I recommend running this code from within a virtual environment, cd to your desired source directory and clone the repository:
I recommend running this code from within a virtual environment, cd to your desired source directory and clone the
repository:

.. code::
Expand All @@ -43,13 +44,19 @@ I recommend running this code from within a virtual environment, cd to your desi
Running Example/ Tests
----------------------

Examples are in the examples directory, or tests are in the `Adafruit_MAX31856 <https://github.com/johnrbnsn/Adafruit_Python_MAX31856/tree/master/Adafruit_MAX31856>`_ directory. From within this directory, run simpletest.py:
Examples are in the examples directory, or tests are in the
`Adafruit_MAX31856 <https://github.com/johnrbnsn/Adafruit_Python_MAX31856/tree/master/Adafruit_MAX31856>`_
directory. From within this directory, run simpletest.py, or the same example with an alternate thermocouple type (k
for the example, same process for any thermocouple type):

.. code::
(env_py3) $ python simpletest.py
(env_py3) $ python simpletest_k_type.py
Runing Tests, cd to `Adafruit_MAX31856 <https://github.com/johnrbnsn/Adafruit_Python_MAX31856/tree/master/Adafruit_MAX31856>`_ and run:
Runing Tests, cd to
`Adafruit_MAX31856 <https://github.com/johnrbnsn/Adafruit_Python_MAX31856/tree/master/Adafruit_MAX31856>`_
and run:

.. code::
Expand All @@ -58,18 +65,19 @@ Runing Tests, cd to `Adafruit_MAX31856 <https://github.com/johnrbnsn/Adafruit_Py
Debugging
---------

If you are having issues, run the tests located in the `Adafruit\_MAX31856 <https://github.com/johnrbnsn/Adafruit_Python_MAX31856/tree/master/Adafruit_MAX31856>`_ directory by:
If you are having issues, run the tests located in the
`Adafruit\_MAX31856 <https://github.com/johnrbnsn/Adafruit_Python_MAX31856/tree/master/Adafruit_MAX31856>`_
directory by:

.. code::
python test_MAX31856.py -v
The resulting output and the test_MAX31856.log file should help with debugging the issue.

Supporting Developer
--------------------
Please consider supporting me if you found this code helpful: `<http://paypal.me/johnrbnsn>`_

Acknowledgement
---------------
This code was modeled after the `Adafruit MAX31855 repository <https://github.com/adafruit/Adafruit_Python_MAX31855>`_ which works with the prior version of this thermocouple amplifier. There is only an Arduino example available from Adafruit at this time, since I use RasPi, I created this library.
This code was modeled after the
`Adafruit MAX31855 repository <https://github.com/adafruit/Adafruit_Python_MAX31855>`_ which works with
the prior version of this thermocouple amplifier. There is only an Arduino example available from Adafruit at this
time, since I use RasPi, I created this library.
Loading

0 comments on commit 785eb23

Please sign in to comment.