forked from Tuckie/max31855
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmax31855.py
170 lines (149 loc) · 6.04 KB
/
max31855.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#!/usr/bin/python
import RPi.GPIO as GPIO
class MAX31855(object):
'''Python driver for [MAX38155 Cold-Junction Compensated Thermocouple-to-Digital Converter](http://www.maximintegrated.com/datasheet/index.mvp/id/7273)
Requires:
- The [GPIO Library](https://code.google.com/p/raspberry-gpio-python/) (Already on most Raspberry Pi OS builds)
- A [Raspberry Pi](http://www.raspberrypi.org/)
'''
def __init__(self, cs_pin, clock_pin, data_pin, units = "c", board = GPIO.BCM):
'''Initialize Soft (Bitbang) SPI bus
Parameters:
- cs_pin: Chip Select (CS) / Slave Select (SS) pin (Any GPIO)
- clock_pin: Clock (SCLK / SCK) pin (Any GPIO)
- data_pin: Data input (SO / MOSI) pin (Any GPIO)
- units: (optional) unit of measurement to return. ("c" (default) | "k" | "f")
- board: (optional) pin numbering method as per RPi.GPIO library (GPIO.BCM (default) | GPIO.BOARD)
'''
self.cs_pin = cs_pin
self.clock_pin = clock_pin
self.data_pin = data_pin
self.units = units
self.data = None
self.board = board
# Initialize needed GPIO
GPIO.setmode(self.board)
GPIO.setup(self.cs_pin, GPIO.OUT)
GPIO.setup(self.clock_pin, GPIO.OUT)
GPIO.setup(self.data_pin, GPIO.IN)
# Pull chip select high to make chip inactive
GPIO.output(self.cs_pin, GPIO.HIGH)
def get(self):
'''Reads SPI bus and returns current value of thermocouple.'''
self.read()
self.checkErrors()
return getattr(self, "to_" + self.units)(self.data_to_tc_temperature())
def get_rj(self):
'''Reads SPI bus and returns current value of reference junction.'''
self.read()
return getattr(self, "to_" + self.units)(self.data_to_rj_temperature())
def read(self):
'''Reads 32 bits of the SPI bus & stores as an integer in self.data.'''
bytesin = 0
# Select the chip
GPIO.output(self.cs_pin, GPIO.LOW)
# Read in 32 bits
for i in range(32):
GPIO.output(self.clock_pin, GPIO.LOW)
bytesin = bytesin << 1
if (GPIO.input(self.data_pin)):
bytesin = bytesin | 1
GPIO.output(self.clock_pin, GPIO.HIGH)
# Unselect the chip
GPIO.output(self.cs_pin, GPIO.HIGH)
# Save data
self.data = bytesin
def checkErrors(self, data_32 = None):
'''Checks error bits to see if there are any SCV, SCG, or OC faults'''
if data_32 is None:
data_32 = self.data
anyErrors = (data_32 & 0x10000) != 0 # Fault bit, D16
noConnection = (data_32 & 1) != 0 # OC bit, D0
shortToGround = (data_32 & 2) != 0 # SCG bit, D1
shortToVCC = (data_32 & 4) != 0 # SCV bit, D2
if anyErrors:
if noConnection:
raise MAX31855Error("No Connection")
elif shortToGround:
raise MAX31855Error("Thermocouple short to ground")
elif shortToVCC:
raise MAX31855Error("Thermocouple short to VCC")
else:
# Perhaps another SPI device is trying to send data?
# Did you remember to initialize all other SPI devices?
raise MAX31855Error("Unknown Error")
def data_to_tc_temperature(self, data_32 = None):
'''Takes an integer and returns a thermocouple temperature in celsius.'''
if data_32 is None:
data_32 = self.data
tc_data = ((data_32 >> 18) & 0x3FFF)
return self.convert_tc_data(tc_data)
def data_to_rj_temperature(self, data_32 = None):
'''Takes an integer and returns a reference junction temperature in celsius.'''
if data_32 is None:
data_32 = self.data
rj_data = ((data_32 >> 4) & 0xFFF)
return self.convert_rj_data(rj_data)
def convert_tc_data(self, tc_data):
'''Convert thermocouple data to a useful number (celsius).'''
if tc_data & 0x2000:
# two's compliment
without_resolution = ~tc_data & 0x1FFF
without_resolution += 1
without_resolution *= -1
else:
without_resolution = tc_data & 0x1FFF
return without_resolution * 0.25
def convert_rj_data(self, rj_data):
'''Convert reference junction data to a useful number (celsius).'''
if rj_data & 0x800:
without_resolution = ~rj_data & 0x7FF
without_resolution += 1
without_resolution *= -1
else:
without_resolution = rj_data & 0x7FF
return without_resolution * 0.0625
def to_c(self, celsius):
'''Celsius passthrough for generic to_* method.'''
return celsius
def to_k(self, celsius):
'''Convert celsius to kelvin.'''
return celsius + 273.15
def to_f(self, celsius):
'''Convert celsius to fahrenheit.'''
return celsius * 9.0/5.0 + 32
def cleanup(self):
'''Selective GPIO cleanup'''
GPIO.setup(self.cs_pin, GPIO.IN)
GPIO.setup(self.clock_pin, GPIO.IN)
class MAX31855Error(Exception):
def __init__(self, value):
self.value = value
def __str__(self):
return repr(self.value)
if __name__ == "__main__":
# Multi-chip example
import time
cs_pins = [4, 17, 18, 24]
clock_pin = 23
data_pin = 22
units = "f"
thermocouples = []
for cs_pin in cs_pins:
thermocouples.append(MAX31855(cs_pin, clock_pin, data_pin, units))
running = True
while(running):
try:
for thermocouple in thermocouples:
rj = thermocouple.get_rj()
try:
tc = thermocouple.get()
except MAX31855Error as e:
tc = "Error: "+ e.value
running = False
print("tc: {} and rj: {}".format(tc, rj))
time.sleep(1)
except KeyboardInterrupt:
running = False
for thermocouple in thermocouples:
thermocouple.cleanup()