THERMAL_MLX90640.ino 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  1. /*
  2. Description: Read the THERMAL Unit (MLX90640 IR array) temperature pixels and display it on the screen.
  3. */
  4. #include <M5Stack.h>
  5. #include <Wire.h>
  6. #include "MLX90640_API.h"
  7. #include "MLX90640_I2C_Driver.h"
  8. const byte MLX90640_address = 0x33; //Default 7-bit unshifted address of the MLX90640
  9. #define TA_SHIFT 8 //Default shift for MLX90640 in open air
  10. #define COLS 32
  11. #define ROWS 24
  12. #define COLS_2 (COLS * 2)
  13. #define ROWS_2 (ROWS * 2)
  14. float pixelsArraySize = COLS * ROWS;
  15. float pixels[COLS * ROWS];
  16. float pixels_2[COLS_2 * ROWS_2];
  17. float reversePixels[COLS * ROWS];
  18. byte speed_setting = 2 ; // High is 1 , Low is 2
  19. bool reverseScreen = false;
  20. //bool reverseScreen = true;
  21. #define INTERPOLATED_COLS 32
  22. #define INTERPOLATED_ROWS 32
  23. static float mlx90640To[COLS * ROWS];
  24. paramsMLX90640 mlx90640;
  25. float signedMag12ToFloat(uint16_t val);
  26. //low range of the sensor (this will be blue on the screen)
  27. int MINTEMP = 24; // For color mapping
  28. int min_v = 24; //Value of current min temp
  29. int min_cam_v = -40; // Spec in datasheet
  30. //high range of the sensor (this will be red on the screen)
  31. int MAXTEMP = 35; // For color mapping
  32. int max_v = 35; //Value of current max temp
  33. int max_cam_v = 300; // Spec in datasheet
  34. int resetMaxTemp = 45;
  35. //the colors we will be using
  36. const uint16_t camColors[] = {0x480F,
  37. 0x400F, 0x400F, 0x400F, 0x4010, 0x3810, 0x3810, 0x3810, 0x3810, 0x3010, 0x3010,
  38. 0x3010, 0x2810, 0x2810, 0x2810, 0x2810, 0x2010, 0x2010, 0x2010, 0x1810, 0x1810,
  39. 0x1811, 0x1811, 0x1011, 0x1011, 0x1011, 0x0811, 0x0811, 0x0811, 0x0011, 0x0011,
  40. 0x0011, 0x0011, 0x0011, 0x0031, 0x0031, 0x0051, 0x0072, 0x0072, 0x0092, 0x00B2,
  41. 0x00B2, 0x00D2, 0x00F2, 0x00F2, 0x0112, 0x0132, 0x0152, 0x0152, 0x0172, 0x0192,
  42. 0x0192, 0x01B2, 0x01D2, 0x01F3, 0x01F3, 0x0213, 0x0233, 0x0253, 0x0253, 0x0273,
  43. 0x0293, 0x02B3, 0x02D3, 0x02D3, 0x02F3, 0x0313, 0x0333, 0x0333, 0x0353, 0x0373,
  44. 0x0394, 0x03B4, 0x03D4, 0x03D4, 0x03F4, 0x0414, 0x0434, 0x0454, 0x0474, 0x0474,
  45. 0x0494, 0x04B4, 0x04D4, 0x04F4, 0x0514, 0x0534, 0x0534, 0x0554, 0x0554, 0x0574,
  46. 0x0574, 0x0573, 0x0573, 0x0573, 0x0572, 0x0572, 0x0572, 0x0571, 0x0591, 0x0591,
  47. 0x0590, 0x0590, 0x058F, 0x058F, 0x058F, 0x058E, 0x05AE, 0x05AE, 0x05AD, 0x05AD,
  48. 0x05AD, 0x05AC, 0x05AC, 0x05AB, 0x05CB, 0x05CB, 0x05CA, 0x05CA, 0x05CA, 0x05C9,
  49. 0x05C9, 0x05C8, 0x05E8, 0x05E8, 0x05E7, 0x05E7, 0x05E6, 0x05E6, 0x05E6, 0x05E5,
  50. 0x05E5, 0x0604, 0x0604, 0x0604, 0x0603, 0x0603, 0x0602, 0x0602, 0x0601, 0x0621,
  51. 0x0621, 0x0620, 0x0620, 0x0620, 0x0620, 0x0E20, 0x0E20, 0x0E40, 0x1640, 0x1640,
  52. 0x1E40, 0x1E40, 0x2640, 0x2640, 0x2E40, 0x2E60, 0x3660, 0x3660, 0x3E60, 0x3E60,
  53. 0x3E60, 0x4660, 0x4660, 0x4E60, 0x4E80, 0x5680, 0x5680, 0x5E80, 0x5E80, 0x6680,
  54. 0x6680, 0x6E80, 0x6EA0, 0x76A0, 0x76A0, 0x7EA0, 0x7EA0, 0x86A0, 0x86A0, 0x8EA0,
  55. 0x8EC0, 0x96C0, 0x96C0, 0x9EC0, 0x9EC0, 0xA6C0, 0xAEC0, 0xAEC0, 0xB6E0, 0xB6E0,
  56. 0xBEE0, 0xBEE0, 0xC6E0, 0xC6E0, 0xCEE0, 0xCEE0, 0xD6E0, 0xD700, 0xDF00, 0xDEE0,
  57. 0xDEC0, 0xDEA0, 0xDE80, 0xDE80, 0xE660, 0xE640, 0xE620, 0xE600, 0xE5E0, 0xE5C0,
  58. 0xE5A0, 0xE580, 0xE560, 0xE540, 0xE520, 0xE500, 0xE4E0, 0xE4C0, 0xE4A0, 0xE480,
  59. 0xE460, 0xEC40, 0xEC20, 0xEC00, 0xEBE0, 0xEBC0, 0xEBA0, 0xEB80, 0xEB60, 0xEB40,
  60. 0xEB20, 0xEB00, 0xEAE0, 0xEAC0, 0xEAA0, 0xEA80, 0xEA60, 0xEA40, 0xF220, 0xF200,
  61. 0xF1E0, 0xF1C0, 0xF1A0, 0xF180, 0xF160, 0xF140, 0xF100, 0xF0E0, 0xF0C0, 0xF0A0,
  62. 0xF080, 0xF060, 0xF040, 0xF020, 0xF800,
  63. };
  64. float get_point(float *p, uint8_t rows, uint8_t cols, int8_t x, int8_t y);
  65. void set_point(float *p, uint8_t rows, uint8_t cols, int8_t x, int8_t y, float f);
  66. void get_adjacents_1d(float *src, float *dest, uint8_t rows, uint8_t cols, int8_t x, int8_t y);
  67. void get_adjacents_2d(float *src, float *dest, uint8_t rows, uint8_t cols, int8_t x, int8_t y);
  68. float cubicInterpolate(float p[], float x);
  69. float bicubicInterpolate(float p[], float x, float y);
  70. void interpolate_image(float *src, uint8_t src_rows, uint8_t src_cols, float *dest, uint8_t dest_rows, uint8_t dest_cols);
  71. long loopTime, startTime, endTime, fps;
  72. void setup()
  73. {
  74. M5.begin();
  75. M5.Power.begin();
  76. Wire.begin();
  77. Wire.setClock(450000); //Increase I2C clock speed to 400kHz
  78. Serial.begin(115200);
  79. M5.Lcd.begin();
  80. M5.Lcd.setRotation(1);
  81. M5.Lcd.fillScreen(TFT_BLACK);
  82. M5.Lcd.setTextColor(YELLOW, BLACK);
  83. while (!Serial); //Wait for user to open terminal
  84. Serial.println("M5Stack MLX90640 IR Camera");
  85. M5.Lcd.setTextSize(2);
  86. //Get device parameters - We only have to do this once
  87. int status;
  88. uint16_t eeMLX90640[832];//32 * 24 = 768
  89. status = MLX90640_DumpEE(MLX90640_address, eeMLX90640);
  90. if (status != 0)
  91. Serial.println("Failed to load system parameters");
  92. status = MLX90640_ExtractParameters(eeMLX90640, &mlx90640);
  93. if (status != 0)
  94. Serial.println("Parameter extraction failed");
  95. int SetRefreshRate;
  96. //Setting MLX90640 device at slave address 0x33 to work with 16Hz refresh rate:
  97. // 0x00 – 0.5Hz
  98. // 0x01 – 1Hz
  99. // 0x02 – 2Hz
  100. // 0x03 – 4Hz
  101. // 0x04 – 8Hz // OK
  102. // 0x05 – 16Hz // OK
  103. // 0x06 – 32Hz // Fail
  104. // 0x07 – 64Hz
  105. SetRefreshRate = MLX90640_SetRefreshRate (0x33, 0x05);
  106. //Once params are extracted, we can release eeMLX90640 array
  107. //Display bottom side colorList and info
  108. M5.Lcd.fillScreen(TFT_BLACK);
  109. int icolor = 0;
  110. for (int icol = 0; icol <= 248; icol++)
  111. {
  112. //彩色条
  113. M5.Lcd.drawRect(36, 208, icol, 284 , camColors[icolor]);
  114. icolor++;
  115. }
  116. infodisplay();
  117. }
  118. void loop()
  119. {
  120. loopTime = millis();
  121. startTime = loopTime;
  122. ///////////////////////////////
  123. // Set Min Value - LongPress //
  124. ///////////////////////////////
  125. if (M5.BtnA.pressedFor(1000)) {
  126. if (MINTEMP <= 5 )
  127. {
  128. MINTEMP = MAXTEMP - 5;
  129. }
  130. else
  131. {
  132. MINTEMP = MINTEMP - 5;
  133. }
  134. infodisplay();
  135. }
  136. ///////////////////////////////
  137. // Set Min Value - SortPress //
  138. ///////////////////////////////
  139. if (M5.BtnA.wasPressed()) {
  140. if (MINTEMP <= 0)
  141. {
  142. MINTEMP = MAXTEMP - 1;
  143. }
  144. else
  145. {
  146. MINTEMP--;
  147. }
  148. infodisplay();
  149. }
  150. /////////////////////
  151. // Reset settings //
  152. /////////////////////
  153. if (M5.BtnB.wasPressed()) {
  154. MINTEMP = min_v - 1;
  155. MAXTEMP = max_v + 1;
  156. infodisplay();
  157. }
  158. ////////////////
  159. // Power Off //
  160. ////////////////
  161. if (M5.BtnB.pressedFor(1000)) {
  162. M5.Lcd.fillScreen(TFT_BLACK);
  163. M5.Lcd.setTextColor(YELLOW, BLACK);
  164. M5.Lcd.drawCentreString("Power Off...", 160, 80, 4);
  165. delay(1000);
  166. M5.powerOFF();
  167. }
  168. ///////////////////////////////
  169. // Set Max Value - LongPress //
  170. ///////////////////////////////
  171. if (M5.BtnC.pressedFor(1000)) {
  172. if (MAXTEMP >= max_cam_v)
  173. {
  174. MAXTEMP = MINTEMP + 1;
  175. }
  176. else
  177. {
  178. MAXTEMP = MAXTEMP + 5;
  179. }
  180. infodisplay();
  181. }
  182. ///////////////////////////////
  183. // Set Max Value - SortPress //
  184. ///////////////////////////////
  185. if (M5.BtnC.wasPressed()) {
  186. if (MAXTEMP >= max_cam_v )
  187. {
  188. MAXTEMP = MINTEMP + 1;
  189. }
  190. else
  191. {
  192. MAXTEMP++;
  193. }
  194. infodisplay();
  195. }
  196. M5.update();
  197. for (byte x = 0 ; x < speed_setting ; x++) // x < 2 Read both subpages
  198. {
  199. uint16_t mlx90640Frame[834];
  200. int status = MLX90640_GetFrameData(MLX90640_address, mlx90640Frame);
  201. if (status < 0)
  202. {
  203. Serial.print("GetFrame Error: ");
  204. Serial.println(status);
  205. }
  206. float vdd = MLX90640_GetVdd(mlx90640Frame, &mlx90640);
  207. float Ta = MLX90640_GetTa(mlx90640Frame, &mlx90640);
  208. float tr = Ta - TA_SHIFT; //Reflected temperature based on the sensor ambient temperature
  209. float emissivity = 0.95;
  210. MLX90640_CalculateTo(mlx90640Frame, &mlx90640, emissivity, tr, pixels); //save pixels temp to array (pixels)
  211. int mode_ = MLX90640_GetCurMode(MLX90640_address);
  212. //amendment
  213. MLX90640_BadPixelsCorrection((&mlx90640)->brokenPixels, pixels, mode_, &mlx90640);
  214. //MLX90640_BadPixelsCorrection((&mlx90640)->outlierPixels, pixels, mode_, &mlx90640);
  215. }
  216. //Reverse image (order of Integer array)
  217. if (reverseScreen == 1)
  218. {
  219. for (int x = 0 ; x < pixelsArraySize ; x++)
  220. {
  221. if (x % COLS == 0) //32 values wide
  222. {
  223. for (int j = 0 + x, k = (COLS-1) + x; j < COLS + x ; j++, k--)
  224. {
  225. reversePixels[j] = pixels[k];
  226. // Serial.print(x);Serial.print(" = Rev "); Serial.print(j);Serial.print(" , Nor ");Serial.println(k);
  227. }
  228. }
  229. }
  230. }
  231. float dest_2d[INTERPOLATED_ROWS * INTERPOLATED_COLS];
  232. int ROWS_i,COLS_j;
  233. if (reverseScreen == 1)
  234. {
  235. // ** reversePixels
  236. interpolate_image(reversePixels, ROWS, COLS, dest_2d, INTERPOLATED_ROWS, INTERPOLATED_COLS);
  237. }
  238. else
  239. {
  240. interpolate_image(pixels, ROWS, COLS, dest_2d, INTERPOLATED_ROWS, INTERPOLATED_COLS);
  241. // 32 * 24 = 768
  242. // 63 * 48 = 3072
  243. //pixels_2
  244. for(int y = 0;y < ROWS;y++)
  245. {
  246. for(int x = 0;x < COLS;x++)
  247. {
  248. // 原始数据
  249. pixels_2[(((y * 2) * (COLS*2)) + (x * 2))] = pixels[y*COLS+x];
  250. if(x != 31)
  251. pixels_2[(((y * 2) * (COLS*2)) + (x * 2)+1)] = ( pixels_2[(((y * 2) * (COLS*2)) + (x * 2))] + pixels_2[(((y * 2) * (COLS*2)) + (x * 2)+2)]) / 2;
  252. else
  253. pixels_2[(((y * 2) * (COLS*2)) + (x * 2)+1)] = ( pixels_2[(((y * 2) * (COLS*2)) + (x * 2))] );
  254. //Serial.print(pixels_2[(((y * 2) * (COLS*2)) + (x * 2))]);
  255. //Serial.print(pixels[y*COLS+x]);
  256. //Serial.print(" ");
  257. }
  258. //Serial.println("\r\n");
  259. }
  260. /*
  261. //-------------------------
  262. // 计算x间隔插入数据
  263. for(int y = 0;y < ROWS;y++)//24
  264. {
  265. for(int x = 0;x < COLS;x++)//32
  266. {
  267. if(x != 31)
  268. pixels_2[(((y * 2) * (COLS*2)) + (x * 2)+1)] = ( pixels_2[(((y * 2) * (COLS*2)) + (x * 2))] + pixels_2[(((y * 2) * (COLS*2)) + (x * 2)+2)]) / 2;
  269. else
  270. pixels_2[(((y * 2) * (COLS*2)) + (x * 2)+1)] = ( pixels_2[(((y * 2) * (COLS*2)) + (x * 2))] );
  271. }
  272. }
  273. */
  274. ///*
  275. // 计算y间隔插入数据
  276. for(int y = 0;y < ROWS;y++)//24
  277. {
  278. for(int x = 0;x < COLS_2;x++)//64
  279. {
  280. if(y != 23)
  281. pixels_2[(((y * 2) + 1) * (COLS_2)) + x] = ( pixels_2[(((y * 2) * COLS_2) + x)] + pixels_2[((((y * 2) + 2) * COLS_2) + x)] ) / 2;
  282. else
  283. pixels_2[(((y * 2) + 1) * (COLS_2)) + x] = ( pixels_2[(((y * 2) * COLS_2) + x)] + pixels_2[(((y * 2) * COLS_2) + x)] ) / 2;
  284. }
  285. }
  286. //*/
  287. /*
  288. //打印数据
  289. for(int y = 0;y < ROWS_2;y++)
  290. {
  291. for(int x = 0;x < COLS_2;x++)
  292. {
  293. Serial.print(pixels_2[y * COLS_2 + x]);
  294. Serial.print(" ");
  295. }
  296. Serial.println("\r\n");
  297. }
  298. //-------------------------
  299. // */
  300. }
  301. uint16_t boxsize = min(M5.Lcd.width() / INTERPOLATED_ROWS, M5.Lcd.height() / INTERPOLATED_COLS);
  302. uint16_t boxWidth = M5.Lcd.width() / INTERPOLATED_ROWS;
  303. //uint16_t boxWidth = 192 / INTERPOLATED_ROWS;
  304. uint16_t boxHeight = (M5.Lcd.height() - 31) / INTERPOLATED_COLS; // 31 for bottom info
  305. //drawpixels(pixels, 24, INTERPOLATED_COLS, 8, 8, false);
  306. //drawpixels(pixels_2, 48, 64, 5, 5, false);
  307. drawpixels(dest_2d, INTERPOLATED_ROWS, INTERPOLATED_COLS, boxWidth, boxHeight, false);
  308. max_v = MINTEMP;
  309. min_v = MAXTEMP;
  310. int spot_v = pixels[360];
  311. spot_v = pixels[768/2];
  312. //while(1);
  313. for ( int itemp = 0; itemp < sizeof(pixels) / sizeof(pixels[0]); itemp++ )
  314. {
  315. if ( pixels[itemp] > max_v )
  316. {
  317. max_v = pixels[itemp];
  318. }
  319. if ( pixels[itemp] < min_v )
  320. {
  321. min_v = pixels[itemp];
  322. }
  323. }
  324. M5.Lcd.setTextSize(2);
  325. M5.Lcd.fillRect(164, 220, 75, 18, TFT_BLACK); // clear max temp text
  326. M5.Lcd.fillRect(60, 220, 200, 18, TFT_BLACK); // clear spot temp text
  327. int icolor = 0;
  328. //for (int icol = 0; icol <= 248; icol++)
  329. //{
  330. // M5.Lcd.drawRect(36, 208, icol, 284 , camColors[icolor]);
  331. // icolor++;
  332. //}
  333. M5.Lcd.setCursor(60, 222); // update min & max temp
  334. M5.Lcd.setTextColor(TFT_WHITE);
  335. if (max_v > max_cam_v | max_v < min_cam_v ) {
  336. M5.Lcd.setTextColor(TFT_RED);
  337. M5.Lcd.printf("Error", 1);
  338. }
  339. else
  340. {
  341. M5.Lcd.print("Min:");
  342. M5.Lcd.print(min_v, 1);
  343. M5.Lcd.print("C ");
  344. M5.Lcd.print("Max:");
  345. M5.Lcd.print(max_v, 1);
  346. M5.Lcd.print("C");
  347. M5.Lcd.setCursor(180, 94); // update spot temp text
  348. M5.Lcd.print(spot_v, 1);
  349. M5.Lcd.printf("C");
  350. //M5.Lcd.drawCircle(160, 100, 6, TFT_WHITE); // update center spot icon
  351. //M5.Lcd.drawLine(160, 90, 160, 110, TFT_WHITE); // vertical line
  352. //M5.Lcd.drawLine(150, 100, 170, 100, TFT_WHITE); // horizontal line
  353. M5.Lcd.drawCircle(160, 120, 6, TFT_WHITE); // update center spot icon
  354. M5.Lcd.drawLine(160, 110, 160, 130, TFT_WHITE); // vertical line
  355. M5.Lcd.drawLine(150, 120, 170, 120, TFT_WHITE); // horizontal line
  356. }
  357. loopTime = millis();
  358. endTime = loopTime;
  359. fps = 1000 / (endTime - startTime);
  360. //M5.Lcd.fillRect(310, 209, 10, 12, TFT_BLACK); //Clear fps text area
  361. M5.Lcd.fillRect(300, 209, 20, 12, TFT_BLACK); //Clear fps text area
  362. M5.Lcd.setTextSize(1);
  363. M5.Lcd.setCursor(284, 210);
  364. M5.Lcd.print("fps:" + String( fps ));
  365. M5.Lcd.setTextSize(1);
  366. }
  367. /***infodisplay()*****/
  368. void infodisplay(void) {
  369. M5.Lcd.fillRect(0, 198, 320, 4, TFT_WHITE);
  370. M5.Lcd.setTextColor(TFT_WHITE);
  371. M5.Lcd.fillRect(284, 223, 320, 240, TFT_BLACK); //Clear MaxTemp area
  372. M5.Lcd.setTextSize(2);
  373. M5.Lcd.setCursor(284, 222); //move to bottom right
  374. M5.Lcd.print(MAXTEMP , 1); // update MAXTEMP
  375. M5.Lcd.print("C");
  376. M5.Lcd.setCursor(0, 222); // update MINTEMP text
  377. M5.Lcd.fillRect(0, 222, 36, 16, TFT_BLACK);
  378. M5.Lcd.print(MINTEMP , 1);
  379. M5.Lcd.print("C");
  380. M5.Lcd.setCursor(106, 224);
  381. }
  382. void drawpixels(float *p, uint8_t rows, uint8_t cols, uint8_t boxWidth, uint8_t boxHeight, boolean showVal) {
  383. int colorTemp;
  384. for (int y = 0; y < rows; y++)
  385. {
  386. for (int x = 0; x < cols; x++)
  387. {
  388. float val = get_point(p, rows, cols, x, y);
  389. if (val >= MAXTEMP)
  390. colorTemp = MAXTEMP;
  391. else if (val <= MINTEMP)
  392. colorTemp = MINTEMP;
  393. else colorTemp = val;
  394. uint8_t colorIndex = map(colorTemp, MINTEMP, MAXTEMP, 0, 255);
  395. colorIndex = constrain(colorIndex, 0, 255);// 0 ~ 255
  396. //draw the pixels!
  397. uint16_t color;
  398. color = val * 2;
  399. M5.Lcd.fillRect(boxWidth * x, boxHeight * y, boxWidth, boxHeight, camColors[colorIndex]);
  400. }
  401. }
  402. }
  403. //Returns true if the MLX90640 is detected on the I2C bus
  404. boolean isConnected()
  405. {
  406. Wire.beginTransmission((uint8_t)MLX90640_address);
  407. if (Wire.endTransmission() != 0)
  408. return (false); //Sensor did not ACK
  409. return (true);
  410. }