Mit einem Arduino bzw. ESP32 kann man sogar eine Wärmebildkamera basteln. Bei der Variante 1 wird die Temperatur mit dem Sensor MLX90614 Pixel für Pixel bestimmt, indem man den Sensor mittels zweier Servomotoren in viele verschiedene Richtungen orientiert. Bei der Variante 2 mit dem Sensor MLX90640 ist dies nicht mehr nötig, da der Sensor bereits über 32×24 Pixel verfügt. Leider brachte ich den MLX90640 mit einem Arduino nicht zum Laufen. Daher musste ich auf einen ESP32 ausweichen.
Variante 1: Sensor MLX90614
Dieser Sensor verfügt lediglich über 1 Pixel. Daher muss zur Aufnahme eines kompletten Wärmebildes der Sensor Schritt für Schritt in viele Richtungen ausgerichtet werden. Dies bedingt natürlich eine sehr große Messzeit. Bei 1 Sekunde pro Messung und einem Bild mit 40×40 Pixel dauert der Messvorgang immerhin bereits fast 27 Minuten.
Die Temperaturwerte werden am Ende mit dem Grafikprogramm gnuplot eingefärbt, damit man ein anschauliches Wärmebild erhält.
Arduino-Code:
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 |
// ======================================================= // ====== Programm zur Erstellung eines Wärmebilds ======= // ======================================================= #include <Servo.h> // Inkludiert die Servodateien #include <i2cmaster.h> // Inkludiert die i2c-Bus-Dateien Servo servo_horiz; // Definiert den horizontalen servo Servo servo_verti; // Definiert den vertikalen servo int taste; // Tastaturpin für Start des Scans int horiz_start = 90; // horizontaler Startwinkel int verti_start = 20; // vertikaler Startwinkel int schritte = 40; // horizontale bzw. vertikale Schritte // ************************************ // ************** SETUP *************** // ************************************ void setup() { Serial.begin(9600); servo_horiz.attach(9); // Set horizontalen servo to digital pin 9 servo_verti.attach(10); // Set vertikalen servo to digital pin 10 pinMode(13, OUTPUT); i2c_init(); //Initialisiert den i2c-Bus PORTC = (1 << PORTC4) | (1 << PORTC5); //enable pullups } // ******************************************** // ************** HAUPTSCHLEIFE *************** // ******************************************** void loop() { taste = analogRead(A0); // Abfrage, ob die Taste gedrückt wird und der Scan gestartet werden kann servo_horiz.write(horiz_start); servo_verti.write(verti_start); if (taste > 500) { // ********** Messung gestartet ************ digitalWrite(13, HIGH); // set the LED on delay(1000); // wait for a second digitalWrite(13, LOW); // set the LED off for (int i = horiz_start; i >= horiz_start - schritte; i--) { servo_verti.write(verti_start); delay(150); for (int j = verti_start; j <= verti_start + schritte; j++) { servo_horiz.write(i); servo_verti.write(j); delay(250); int dev = 0x5A<<1; int data_low = 0; int data_high = 0; int pec = 0; i2c_start_wait(dev+I2C_WRITE); i2c_write(0x07); // Temperatur vom MLX90614 lesen i2c_rep_start(dev+I2C_READ); data_low = i2c_readAck(); // Read 1 byte and then send back data_high = i2c_readAck(); // Read 1 byte and then send back pec = i2c_readNak(); i2c_stop(); // This converts high and low bytes together and processes temperature, MSB is a error bit and is ignored for temps double tempFactor = 0.02; // 0.02 degrees per LSB (measurement resolution of the MLX90614) double tempData = 0x0000; // zero out the data int frac; // data past the decimal point // This masks off the error bit of the high byte, then moves it left 8 bits and adds the low byte. tempData = (double)(((data_high & 0x007F) << 8) + data_low); tempData = (tempData * tempFactor)-0.01; float celcius = tempData - 273.15; Serial.print(horiz_start - i); Serial.print(" "); Serial.print(j - verti_start); Serial.print(" "); Serial.println(celcius); } } // ********** Messung beendet ************ digitalWrite(13, HIGH); // set the LED on delay(1000); // wait for a second digitalWrite(13, LOW); // set the LED off servo_horiz.write(horiz_start); servo_verti.write(verti_start); } } |
Variante 2: Sensor MLX90640
Dieser Sensor verfügt wie schon erwähnt bereits über 32×24 Pixel. Deshalb können ca. 2 Bilder pro Sekunde erstellt/angezeigt werden. Das Display verfügt über 320×240 Pixel. Angezeigt werden Minimaltemperatur, Maximaltemperatur und Temperatur des mit einem Kreuz versehenen Pixels in der Bildmitte.
Arduino-Code:
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 |
#include <Wire.h> #include "MLX90640_API.h" #include "MLX90640_I2C_Driver.h" #include "SPI.h" #include "Adafruit_GFX.h" #include "Adafruit_ILI9341.h" // For the ESP-WROVER_KIT, these are the default. #define TFT_CS 15 #define TFT_DC 2 #define TFT_MOSI 13 #define TFT_CLK 14 #define TFT_RST 26 #define TFT_MISO 12 #define TFT_LED 27 Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_MOSI, TFT_CLK, TFT_RST, TFT_MISO); const byte MLX90640_address = 0x33; //Default 7-bit unshifted address of the MLX90640 #define TA_SHIFT 8 //Default shift for MLX90640 in open air static float mlx90640To[768]; paramsMLX90640 mlx90640; int xPos, yPos; // Abtastposition int R_colour, G_colour, B_colour; // RGB-Farbwert int i, j; // Zählvariable float T_max, T_min; // maximale bzw. minimale gemessene Temperatur float T_center; // Temperatur in der Bildschirmmitte // *************************************** // **************** SETUP **************** // *************************************** void setup() { Serial.begin(115200); Wire.begin(); Wire.setClock(400000); //Increase I2C clock speed to 400kHz while (!Serial); //Wait for user to open terminal Serial.println("MLX90640 IR Array Example"); if (isConnected() == false) { Serial.println("MLX90640 not detected at default I2C address. Please check wiring. Freezing."); while (1); } 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"); Serial.print(" status = "); Serial.println(status); } //Once params are extracted, we can release eeMLX90640 array MLX90640_I2CWrite(0x33, 0x800D, 6401); // writes the value 1901 (HEX) = 6401 (DEC) in the register at position 0x800D to enable reading out the temperatures!!! // =============================================================================================================================================================== //MLX90640_SetRefreshRate(MLX90640_address, 0x00); //Set rate to 0.25Hz effective - Works //MLX90640_SetRefreshRate(MLX90640_address, 0x01); //Set rate to 0.5Hz effective - Works //MLX90640_SetRefreshRate(MLX90640_address, 0x02); //Set rate to 1Hz effective - Works //MLX90640_SetRefreshRate(MLX90640_address, 0x03); //Set rate to 2Hz effective - Works MLX90640_SetRefreshRate(MLX90640_address, 0x04); //Set rate to 4Hz effective - Works //MLX90640_SetRefreshRate(MLX90640_address, 0x05); //Set rate to 8Hz effective - Works at 800kHz //MLX90640_SetRefreshRate(MLX90640_address, 0x06); //Set rate to 16Hz effective - Works at 800kHz //MLX90640_SetRefreshRate(MLX90640_address, 0x07); //Set rate to 32Hz effective - fails pinMode(TFT_LED, OUTPUT); digitalWrite(TFT_LED, HIGH); tft.begin(); tft.setRotation(1); tft.fillScreen(ILI9341_BLACK); tft.fillRect(0, 0, 319, 13, tft.color565(255, 0, 10)); tft.setCursor(100, 3); tft.setTextSize(1); tft.setTextColor(ILI9341_YELLOW, tft.color565(255, 0, 10)); tft.print("Thermographie - stoppi"); tft.drawLine(250, 210 - 0, 258, 210 - 0, tft.color565(255, 255, 255)); tft.drawLine(250, 210 - 30, 258, 210 - 30, tft.color565(255, 255, 255)); tft.drawLine(250, 210 - 60, 258, 210 - 60, tft.color565(255, 255, 255)); tft.drawLine(250, 210 - 90, 258, 210 - 90, tft.color565(255, 255, 255)); tft.drawLine(250, 210 - 120, 258, 210 - 120, tft.color565(255, 255, 255)); tft.drawLine(250, 210 - 150, 258, 210 - 150, tft.color565(255, 255, 255)); tft.drawLine(250, 210 - 180, 258, 210 - 180, tft.color565(255, 255, 255)); tft.setCursor(80, 220); tft.setTextColor(ILI9341_WHITE, tft.color565(0, 0, 0)); tft.print("T+ = "); // drawing the colour-scale // ======================== for (i = 0; i < 181; i++) { getColour(i); tft.drawLine(240, 210 - i, 250, 210 - i, tft.color565(R_colour, G_colour, B_colour)); } } // ********************************** // ************** LOOP ************** // ********************************** void loop() { 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 float emissivity = 0.95; MLX90640_CalculateTo(mlx90640Frame, &mlx90640, emissivity, tr, mlx90640To); } // determine T_min and T_max and eliminate error pixels // ==================================================== mlx90640To[1*32 + 21] = 0.5 * (mlx90640To[1*32 + 20] + mlx90640To[1*32 + 22]); // eliminate the error-pixels mlx90640To[4*32 + 30] = 0.5 * (mlx90640To[4*32 + 29] + mlx90640To[4*32 + 31]); // eliminate the error-pixels T_min = mlx90640To[0]; T_max = mlx90640To[0]; for (i = 1; i < 768; i++) { if((mlx90640To[i] > -41) && (mlx90640To[i] < 301)) { if(mlx90640To[i] < T_min) { T_min = mlx90640To[i]; } if(mlx90640To[i] > T_max) { T_max = mlx90640To[i]; } } else if(i > 0) // temperature out of range { mlx90640To[i] = mlx90640To[i-1]; } else { mlx90640To[i] = mlx90640To[i+1]; } } // determine T_center // ================== T_center = mlx90640To[11* 32 + 15]; // drawing the picture // =================== for (i = 0 ; i < 24 ; i++) { for (j = 0; j < 32; j++) { mlx90640To[i*32 + j] = 180.0 * (mlx90640To[i*32 + j] - T_min) / (T_max - T_min); getColour(mlx90640To[i*32 + j]); tft.fillRect(217 - j * 7, 35 + i * 7, 7, 7, tft.color565(R_colour, G_colour, B_colour)); } } tft.drawLine(217 - 15*7 + 3.5 - 5, 11*7 + 35 + 3.5, 217 - 15*7 + 3.5 + 5, 11*7 + 35 + 3.5, tft.color565(255, 255, 255)); tft.drawLine(217 - 15*7 + 3.5, 11*7 + 35 + 3.5 - 5, 217 - 15*7 + 3.5, 11*7 + 35 + 3.5 + 5, tft.color565(255, 255, 255)); tft.fillRect(260, 25, 37, 10, tft.color565(0, 0, 0)); tft.fillRect(260, 205, 37, 10, tft.color565(0, 0, 0)); tft.fillRect(115, 220, 37, 10, tft.color565(0, 0, 0)); tft.setTextColor(ILI9341_WHITE, tft.color565(0, 0, 0)); tft.setCursor(265, 25); tft.print(T_max, 1); tft.setCursor(265, 205); tft.print(T_min, 1); tft.setCursor(120, 220); tft.print(T_center, 1); tft.setCursor(300, 25); tft.print("C"); tft.setCursor(300, 205); tft.print("C"); tft.setCursor(155, 220); tft.print("C"); //delay(20); } // =============================== // ===== determine the colour ==== // =============================== void getColour(int j) { if (j >= 0 && j < 30) { R_colour = 0; G_colour = 0; B_colour = 20 + (120.0/30.0) * j; } if (j >= 30 && j < 60) { R_colour = (120.0 / 30) * (j - 30.0); G_colour = 0; B_colour = 140 - (60.0/30.0) * (j - 30.0); } if (j >= 60 && j < 90) { R_colour = 120 + (135.0/30.0) * (j - 60.0); G_colour = 0; B_colour = 80 - (70.0/30.0) * (j - 60.0); } if (j >= 90 && j < 120) { R_colour = 255; G_colour = 0 + (60.0/30.0) * (j - 90.0); B_colour = 10 - (10.0/30.0) * (j - 90.0); } if (j >= 120 && j < 150) { R_colour = 255; G_colour = 60 + (175.0/30.0) * (j - 120.0); B_colour = 0; } if (j >= 150 && j <= 180) { R_colour = 255; G_colour = 235 + (20.0/30.0) * (j - 150.0); B_colour = 0 + 255.0/30.0 * (j - 150.0); } } //Returns true if the MLX90640 is detected on the I2C bus boolean isConnected() { Wire.beginTransmission((uint8_t)MLX90640_address); if (Wire.endTransmission() != 0) return (false); //Sensor did not ACK return (true); } |