-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathmax31855.h
365 lines (312 loc) · 10.2 KB
/
max31855.h
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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
#ifndef max31855_h
#define max31855_h
#define DEBUG
// NODEMCU hspi
// HW scl D5 14
// HW mosi/sda D7 13
// HW miso D6 12
// HW cs D8 15
// #define MAXDO 5 // HWMISO
// #define MAXCS 15 // 2
// #define MAXCLK 14 // HWSLK
// #define MAXMISO 13
#define USE_max6675
#ifdef USE_max6675
#include "max6675.h"
MAX6675 tc(MAX_SCK,MAX_CS,MAX_SDO);
#else
// #include "MAX31855.h" // by Rob Tillaart Library Manager (NO HSPI!!)
#include "Adafruit_MAX31855.h" // Seperate calls for spiread for each function, no raw bit cache!
Adafruit_MAX31855 tc(MAX_SCK,MAX_CS,MAX_SDO);
#endif
#include <ktypelinear.h>
// #include <quickstats.h> // @todo test quickstats, add child class for container
// #include <Statistics.h> // https://github.com/provideyourown/statistics
#include <Average.h>
#include <number_format.h>
// Adafruit_MAX31855 tc(14,15,12);
#ifdef TC2
Adafruit_MAX31855 tcB(1);
#endif
bool updateLock = false; // lockout updating temps
// Averaging
// options
bool useInternal = false; // use internal temp as TC
int avgSamples = 10; // 1 to disable averaging
float tempOffset = 0; // allow temp skew
int tempSampleRate = 1000; // how often to sample temperature when idle
// averaging vars when not using stats averaging
unsigned long nextTempRead = 0; // NI averaging timing
unsigned long nextTempAvgRead = 0; // NI averaging timing
// clean up averaging, when using stats lib some below are not used
int avgReadCount = 0; // running avg counter
float maxT; // highest temp in window (tempSampleRate)
float minT; // lowest temp in window (tempSampleRate)
#define USESTATS
// options
bool useAveraging = true; // use stats lib averaging
int max31855_numsamples = 10; // use avgSamples?
// Statistics stats(max31855_numsamples); // init stats for avg (samples)
// Statistics tcdev(16); // init stats for avg (samples)
bool useModeAveraging = true; // use mode for averages to reduce spurious noise, noise is usually negative value temps
// @todo change library, only supports ints atm
Average<float> stats(max31855_numsamples);
Average<float> tcdev(max31855_numsamples*2);
// inline Average::addData(T entry) {push(T entry);}
// Runtime reflow variables
float currentTemp = 0; // current real temp w offset applied
float currentTempB = 0; // alt thermocouple
float currentTempAvg = 0; // current avg temp (avgSamples) mean
float internalTemp = 0; // cold junction
float currentTempCorr = 0; // linearized k type
float lastTemp = -1; // used in pid.h
int TCinterval = 0; // temp reading interval
// TC WARN
// options
bool tcWarn = false; // stop and show error if a tc issue is detected
uint16_t numfailwarn = 10; // how many failures in window
uint16_t numfailwindow = 10000; // ms to compare
uint8_t lastTCStatus = 0; // last error status
uint16_t TCnumErrors = 0; // counter for failures
// max31855 error states
#define STATUS_OK 0x00
#define STATUS_OPEN_CIRCUIT 0x01
#define STATUS_SHORT_TO_GND 0x02
#define STATUS_SHORT_TO_VCC 0x04
#define STATUS_NOREAD 0x80
// Adafruit_MAX31855 tc(MAXCLK, MAXCS, MAXDO);
// Initialise the MAX31855 IC for thermocouple tempterature reading
// MAX31855 tc(MAXCLK, MAXCS, MAXDO);
void averages(){
int minat = 0;
int maxat = 0;
Serial.print("Mean: "); Serial.println(stats.mean());
Serial.print("Mode: "); Serial.println(stats.mode());
Serial.print("Max: "); Serial.println(stats.maximum(&maxat));
Serial.print(" at: "); Serial.println(maxat);
Serial.print("Min: "); Serial.println(stats.minimum(&minat));
Serial.print(" at: "); Serial.println(minat);
Serial.print("StdDev: "); Serial.println(stats.stddev());
// Serial.print("predict: "); Serial.println(stats.predict(x)); // linear regression ?
// Serial.print("leastSquares: "); Serial.println(stats.leastSquares(m,b,r));
}
// Really not needed for reflow temps, fairly linear in region, makes almost no difference
float correctKType(){
float tcmv = (currentTemp - internalTemp) * 0.041276;
currentTempCorr = correctedCelsius(tcmv,internalTemp);
}
float readInternal(){
#ifdef USE_max6675
return 0;
#else
return tc.readInternal();
#endif
}
uint8_t readError(){
#ifdef USE_max6675
return 0;
#else
return tc.readError();
#endif
}
// @todo better average library, lerp etc
void ReadCurrentTempAvg()
{
int status = readError();
float internal = readInternal();
if(useInternal) currentTempAvg += internal + tempOffset;
else currentTempAvg += tc.readCelsius() + tempOffset;
avgReadCount++;
#ifdef DEBUG
// Serial.print(" avgtot: ");
// Serial.print( currentTempAvg );
// Serial.print(" avg count: ");
// Serial.println( avgReadCount );
// Serial.print(" avg: ");
// Serial.println( currentTempAvg/avgReadCount );
#endif
}
// Read the temp probe
void ReadCurrentTemp()
{
lastTCStatus = readError();
#ifdef DEBUG
// Serial.print("tc status: ");
// Serial.println( status );
#endif
if(useInternal){
// use internal cj as real temp, useful for testing without tc or monitoring pcb etc
internalTemp = readInternal();
currentTemp = internalTemp + tempOffset;
}
else {
// sanity check
// @todo replace with sanitycheck
// also check for dev not changing over long periods, could be bad tc, or runaway is heat is being applied
double readC = tc.readCelsius();
if(isnan(readC) || readC < 0 || readC > 700){
// Serial.println("[ERROR] TC OUT OF RANGE, skipping " + (String)readC);
TCnumErrors++;
return;
}
// offset
currentTemp = readC + tempOffset;
// linearized NI
// correctKType();
}
// average
if(useAveraging){
stats.push(currentTemp);
if(useModeAveraging){
currentTempAvg = stats.mode();
tcdev.push(stats.mode());
}
else {
currentTempAvg = stats.mean();
tcdev.push(currentTempAvg); // might lag, better to use currentTemp but will include more noise
}
// Serial.print("[TEMP] tc ");
// Serial.println(currentTemp);
// Serial.print("[TEMP] tc avg ");
// Serial.println(currentTempAvg);
}
else currentTempAvg = currentTemp;
}
// @todo use actual window, using a periodic reset now
void resetError(){
TCnumErrors = 0;
}
uint16_t getTCErrorCount(){
return TCnumErrors;
}
bool getTcHasError(){
return lastTCStatus != STATUS_OK;
}
String getTcStatus(){
uint8_t tcStatus = lastTCStatus;
if(tcStatus == STATUS_OK) return "OK";
else if(tcStatus == STATUS_OPEN_CIRCUIT) return "Open";
else if(tcStatus == STATUS_SHORT_TO_GND) return "GND Short";
else if(tcStatus == STATUS_SHORT_TO_VCC) return "VCC Short";
else if(tcStatus == STATUS_NOREAD) return "Read Failed";
else return "ERROR";
}
void updateDevVars(){
if(currentTempAvg>maxT) maxT = currentTempAvg; // update min max
if(currentTempAvg<minT) minT = currentTempAvg;
}
void updateAltTemps(){
internalTemp = readInternal();
lastTCStatus = readError();
}
void setTCInterval(int intv){
TCinterval = intv;
}
// MAIN UPDATER SCHDULER
// @todo consolidate into single cleaner functions
void updateTemps(){
// if(updateLock) return;
// digitalWrite(0,LOW); // fixes DC left HIGH and data clocked to max ic causing screen artifacts.
delay(TCinterval); // stabilizes temperatures ????
internalTemp = readInternal();
ReadCurrentTemp();
if(!useAveraging)updateDevVars();
// Serial.println(currentTemp);
}
void resetDev(){
maxT = minT = currentTempAvg;
TCnumErrors = 0; // for now do this here
}
float readTCDev(){
float dev = (maxT-minT);
updateTemps();
return dev;
}
// @todo add windowing, dev is currently tied to averageCount and samplerate
// track time stamp calls or period and track dev over time window
// eg. rate of change per second, or extrapolate
// add timer and average, then avg dev over 500ms and double
// y = m * log(x) + b
float getTCDev(){
float dev;
if(useAveraging){
dev = tcdev.stddev(); // @note will return nan, if 0
if(isnan(dev)){
dev = 0;
// Serial.println("[ERROR] stats dev NAN");
}
}
else dev = (maxT-minT);
return dev;
}
bool TCSanityCheck(){
bool ret = true;
if(TCnumErrors > numfailwarn){
Serial.println("[ERROR] TC has read errors");
ret = false;
}
if(getTcHasError()){
Serial.print("[ERROR] TC Error - ");
Serial.println(getTcStatus());
ret = false;
}
if((int)currentTemp > 300 || (int)currentTemp < 0) {
Serial.println("[ERROR] TC OUT OF RANGE");
ret = false;
}
return true;
}
float readFahrenheit(){
return (currentTempAvg*1.8)+32;
}
void printTC(){
updateTemps();
int tempf = readFahrenheit();
Serial.print("[TC] TC1: " + String(( currentTempAvg ))+"ºC " + tempf + "ºF");
tempf = (((internalTemp)*1.8)+32);
Serial.print(" CJ: " + String( round_f( internalTemp ) )+"ºC " + tempf + "ºF");
Serial.print(" Dev: " + String( round_f( getTCDev() ) )+"ºC");
Serial.print(" Raw: ");
Serial.print(currentTemp);
// Serial.print(" Dev: ");
// Serial.print(stats.stdDeviation());
// Serial.print(" TCLIN: ");
// Serial.print(currentTempCorr);
// Serial.print(" NTC: ");
// Serial.print((String)(float(ntc_temp)/10));
Serial.println("");
TCSanityCheck();
}
// color code temperature
/**
int hotTemp = 80; // C burn temperature for HOT indication, 0=disable
int coolTemp = 50; // C burn temperature for HOT indication, 0=disable
int shutDownTemp = 210; // degrees C
if(currentTemp > hotTemp) tft.setTextColor( RED, BLACK );
else if(currentTemp < coolTemp) tft.setTextColor( GREEN, BLACK );
else tft.setTextColor( YELLOW, BLACK );
**/
void initTC(){
// Start up the MAX31855
bool res;
#ifdef USE_max6675
res = true;
#else
res = tc.begin();
#endif
// if(!res) add check now, see commits
delay(200);
readError();
// check for sanity
Serial.println("[TC] MAX31855 Thermocouple Begin...");
if(!TCSanityCheck()) Serial.println("[ERROR] Status: "+ getTcStatus());
#ifdef DEBUG
printTC();
// Serial.println("[TC] "+(String)round(readInternal()));
// Serial.println("[TC] "+(String)round(tc.readCelsius()));
// Serial.println("[TC] "+(String)round(readFahrenheit()));
#endif
updateTemps();
resetDev();
}
#endif