-
Notifications
You must be signed in to change notification settings - Fork 21
/
TC_ESP32_MLX90640.ino
305 lines (240 loc) · 7.91 KB
/
TC_ESP32_MLX90640.ino
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
/**
* Based on:
* - https://github.com/wilhelmzeuschner/arduino_thermal_camera_with_sd_and_img_processing.
* - https://github.com/sparkfun/SparkFun_MLX90640_Arduino_Example
*
* Hardware:
* - ESP32: https://www.espressif.com/en/products/hardware/esp32-devkitc/overview
* - Sensor: https://shop.pimoroni.com/products/mlx90640-thermal-camera-breakout
* - Display: https://www.amazon.de/gp/product/B07DPMV34R/, https://www.pjrc.com/store/display_ili9341.html
*/
#include <TFT_eSPI.h>
#include <SD.h>
#include <SPI.h>
#include <Wire.h>
#include "MLX90640_API.h"
#include "MLX90640_I2C_Driver.h"
#define EMMISIVITY 0.95
#define INTERPOLATE false
#define C_BLUE Display.color565(0,0,255)
#define C_RED Display.color565(255,0,0)
#define C_GREEN Display.color565(0,255,0)
#define C_WHITE Display.color565(255,255,255)
#define C_BLACK Display.color565(0,0,0)
#define C_LTGREY Display.color565(200,200,200)
#define C_DKGREY Display.color565(80,80,80)
#define C_GREY Display.color565(127,127,127)
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
const byte MLX90640_address = 0x33; //Default 7-bit unshifted address of the MLX90640
#define TA_SHIFT 8 //Default shift for MLX90640 in open air
paramsMLX90640 mlx90640;
TFT_eSPI Display = TFT_eSPI();
// Added for measure Temp
boolean measure = true;
float centerTemp;
unsigned long tempTime = millis();
unsigned long tempTime2 = 0;
// start with some initial colors
float minTemp = 20.0;
float maxTemp = 40.0;
// variables for interpolated colors
byte red, green, blue;
// variables for row/column interpolation
float intPoint, val, a, b, c, d, ii;
int x, y, i, j;
// array for the 32 x 24 measured tempValues
static float tempValues[32*24];
// Output size
#define O_WIDTH 224
#define O_HEIGHT 168
#define O_RATIO O_WIDTH/32
float **interpolated = NULL;
uint16_t *imageData = NULL;
void setup() {
Serial.begin(115200);
Serial.println("Hello.");
// Connect thermal sensor.
Wire.begin();
Wire.setClock(400000); // Increase I2C clock speed to 400kHz
Wire.beginTransmission((uint8_t)MLX90640_address);
if (Wire.endTransmission() != 0) {
Serial.println("MLX90640 not detected at default I2C address. Please check wiring.");
}
else {
Serial.println("MLX90640 online!");
}
// Get device parameters - We only have to do this once
int status;
uint16_t eeMLX90640[832];
status = MLX90640_DumpEE(MLX90640_address, eeMLX90640);
if (status != 0) Serial.println("Failed to load system parameters");
status = MLX90640_ExtractParameters(eeMLX90640, &mlx90640);
if (status != 0) Serial.println("Parameter extraction failed");
// Set refresh rate
MLX90640_SetRefreshRate(MLX90640_address, 0x05); // Set rate to 8Hz effective - Works at 800kHz
// Once EEPROM has been read at 400kHz we can increase
Wire.setClock(800000);
// Set up Display.
pinMode(TFT_DC, OUTPUT);
SPI.begin();
SPI.setFrequency(80000000L);
Display.begin();
//Display.setRotation(3);
Display.fillScreen(C_BLACK);
// Prepare interpolated array
interpolated = (float **)malloc(O_HEIGHT * sizeof(float *));
for (int i=0; i<O_HEIGHT; i++) {
interpolated[i] = (float *)malloc(O_WIDTH * sizeof(float));
}
// Prepare imageData array
imageData = (uint16_t *)malloc(O_WIDTH * O_HEIGHT * sizeof(uint16_t));
// get the cutoff points for the color interpolation routines
// note this function called when the temp scale is changed
setAbcd();
drawLegend();
}
void loop() {
tempTime = millis();
readTempValues();
setTempScale();
drawPicture();
drawMeasurement();
}
// Read pixel data from MLX90640.
void readTempValues() {
for (byte x = 0 ; x < 2 ; x++) // Read both subpages
{
uint16_t mlx90640Frame[834];
int status = MLX90640_GetFrameData(MLX90640_address, mlx90640Frame);
if (status < 0)
{
Serial.print("GetFrame Error: ");
Serial.println(status);
}
float vdd = MLX90640_GetVdd(mlx90640Frame, &mlx90640);
float Ta = MLX90640_GetTa(mlx90640Frame, &mlx90640);
float tr = Ta - TA_SHIFT; //Reflected temperature based on the sensor ambient temperature
MLX90640_CalculateTo(mlx90640Frame, &mlx90640, EMMISIVITY, tr, tempValues);
}
}
int row;
float temp, temp2;
void interpolate() {
for (row=0; row<24; row++) {
for (x=0; x<O_WIDTH; x++) {
temp = tempValues[(31 - (x/7)) + (row*32) + 1];
temp2 = tempValues[(31 - (x/7)) + (row*32)];
interpolated[row*7][x] = lerp(temp, temp2, x%7/7.0);
}
}
for (x=0; x<O_WIDTH; x++) {
for (y=0; y<O_HEIGHT; y++) {
temp = interpolated[y-y%7][x];
temp2 = interpolated[min((y-y%7)+7, O_HEIGHT-7)][x];
interpolated[y][x] = lerp(temp, temp2, 1);//y%7/7.0);
}
}
}
// Linear interpolation
float lerp(float v0, float v1, float t) {
return v0 + t * (v1 - v0);
}
void drawPicture() {
if (INTERPOLATE) {
interpolate();
for (y=0; y<O_HEIGHT; y++) {
for (x=0; x<O_WIDTH; x++) {
imageData[(y*O_WIDTH) + x] = getColor(interpolated[y][x]);
}
}
Display.pushImage(8, 8, O_WIDTH, O_HEIGHT, imageData);
}
else {
for (y=0; y<24; y++) {
for (x=0; x<32; x++) {
Display.fillRect(8 + x*7, 8 + y*7, 7, 7, getColor(tempValues[(31-x) + (y*32)]));
}
}
}
}
// Get color for temp value.
uint16_t getColor(float val) {
/*
pass in value and figure out R G B
several published ways to do this I basically graphed R G B and developed simple linear equations
again a 5-6-5 color display will not need accurate temp to R G B color calculation
equations based on
http://web-tech.ga-usa.com/2012/05/creating-a-custom-hot-to-cold-temperature-color-gradient-for-use-with-rrdtool/index.html
*/
red = constrain(255.0 / (c - b) * val - ((b * 255.0) / (c - b)), 0, 255);
if ((val > minTemp) & (val < a)) {
green = constrain(255.0 / (a - minTemp) * val - (255.0 * minTemp) / (a - minTemp), 0, 255);
}
else if ((val >= a) & (val <= c)) {
green = 255;
}
else if (val > c) {
green = constrain(255.0 / (c - d) * val - (d * 255.0) / (c - d), 0, 255);
}
else if ((val > d) | (val < a)) {
green = 0;
}
if (val <= b) {
blue = constrain(255.0 / (a - b) * val - (255.0 * b) / (a - b), 0, 255);
}
else if ((val > b) & (val <= d)) {
blue = 0;
}
else if (val > d) {
blue = constrain(240.0 / (maxTemp - d) * val - (d * 240.0) / (maxTemp - d), 0, 240);
}
// use the displays color mapping function to get 5-6-5 color palet (R=5 bits, G=6 bits, B-5 bits)
return Display.color565(red, green, blue);
}
void setTempScale() {
minTemp = 255;
maxTemp = 0;
for (i = 0; i < 768; i++) {
minTemp = min(minTemp, tempValues[i]);
maxTemp = max(maxTemp, tempValues[i]);
}
setAbcd();
drawLegend();
}
// Function to get the cutoff points in the temp vs RGB graph.
void setAbcd() {
a = minTemp + (maxTemp - minTemp) * 0.2121;
b = minTemp + (maxTemp - minTemp) * 0.3182;
c = minTemp + (maxTemp - minTemp) * 0.4242;
d = minTemp + (maxTemp - minTemp) * 0.8182;
}
// Draw a legend.
void drawLegend() {
float inc = (maxTemp - minTemp) / 224.0;
j = 0;
for (ii = minTemp; ii < maxTemp; ii += inc) {
Display.drawFastVLine(8+ + j++, 292, 20, getColor(ii));
}
Display.setTextFont(2);
Display.setTextSize(1);
Display.setCursor(8, 272);
Display.setTextColor(TFT_WHITE, TFT_BLACK);
Display.print(String(minTemp).substring(0, 5));
Display.setCursor(192, 272);
Display.setTextColor(TFT_WHITE, TFT_BLACK);
Display.print(String(maxTemp).substring(0, 5));
Display.setTextFont(NULL);
}
// Draw a circle + measured value.
void drawMeasurement() {
// Mark center measurement
Display.drawCircle(120, 8+84, 3, TFT_WHITE);
// Measure and print center temperature
centerTemp = (tempValues[383 - 16] + tempValues[383 - 15] + tempValues[384 + 15] + tempValues[384 + 16]) / 4;
Display.setCursor(86, 214);
Display.setTextColor(TFT_WHITE, TFT_BLACK);
Display.setTextFont(2);
Display.setTextSize(2);
Display.print(String(centerTemp).substring(0, 5) + " °C");
}