Pseudo-Medical Monitor

From LVL1
Jump to navigation Jump to search

Pseudo-Medical Monitor for Christmas Cheap Gift

In preparation for the expected Christmas Gift availability crisis, I present the Pseudo-Gift of the season. The time honored tradition of knockoff products hitting the market before Christmas is alive and well. What better than a multi-property knockoff device? Star Trek / Arduino / Medical knockoff device gift for everyone. This Pseudo-Medical Tricorder ripoff is the stocking stuffer for 2021.

Operations

  • Thermal Image from AMG8833 Sensor.
  • Ambient and Object Temperature from MLX90614 Sensor.
  • Distance of device from user via VL53L0X Laser TOF Sensor.
  • Pulse Rate from MAX30102 Sensor
  • Fingerprint Scan image capture from Capacitive Sensor with ID809 processing capabilities.

Parts List

  • Arduino Due Generic Clone (alternative: ITEADUINO DUE)

  • Arduino Mega Prototype Shield Generic Clone (alternative: KEYESTUDIO)

  • MLX90614 Contactless Temperature Sensor Generic Clone (alternative: )

  • AMG8833 Thermal Imager Sensor Generic Clone (alternative: TinyCircuits)

  • VL53L0X TOF Laser Distance Sensor Generic Clone (alternative: Onyehn)

  • MAX30102 Pulse and O2 Saturation Sensor Generic Clone (alternative: MH-ET Live)

  • Capacitive Touch Fingerprint Scanner Generic Clone (alternative: DFROBOT)

  • 160x128 LCD TFT SPI 1.8" Module with SD Socket Generic Clone (alternative: Heyaodz111208)

Wiring

Pin Mapping
Due Pin Function MLX90614 AMG8833 VL53L0X MAX30102 FP Scan TFT SD Socket
3 Interrupt IRQ
4 Interrupt INT
5 Interrupt INT
6 Digital I/O GPIO1
7 Digital I/O XSHUT
8 Digital I/O SD_CS
9 Digital I/O RST
10 Digital I/O CS
11 Digital I/O AO
18 TX_1 RX
19 RX_1 TX
20 SDA SDA SDA SDA SDA
21 SCL SCL SCL SCL SCL
SPI MISO SD_MISO
SPI MOSI SDA SD_MOSI
SPI SCK SCK SD_SCK

Code

Special Libraries

#include <Adafruit_GFX.h>

#include <Adafruit_ST7735.h>

#include <Adafruit_VL53L0X.h>

#include <Adafruit_AMG88xx.h>

#include <MAX30105.h>

#include <SparkFunMLX90614.h>

#include <DFRobot_ID809.h>

Library Modification

// In this Library : #include <SparkFunMLX90614.h> 
//Change the following line in the bool IRTherm::I2CReadWord(byte reg, int16_t * dest) routine.
//
//  I2C processing change needed for Arduino Due implementation
//
// Comment Out Line Below
//	_i2cPort->requestFrom(_deviceAddress, (uint8_t) 3, (uint8_t) true); 

// Add Line Below
      _i2cPort->requestFrom(_deviceAddress, (uint8_t) 3, (uint32_t)reg, (uint8_t)1, (uint8_t)true);

Defines - Variables - Routines

Includes

#include <Wire.h>
#include <SPI.h>

#include <Adafruit_GFX.h>    
#include <Adafruit_ST7735.h> 
#include "Adafruit_VL53L0X.h"
#include <Adafruit_AMG88xx.h>
#include <MAX30105.h>
#include <SparkFunMLX90614.h> 
#include <DFRobot_ID809.h>
#include "bmpHeader.h"
#include <SD.h>

#include "TFT_Stuff.h"

#include "HB_Stuff.h"
#include "AMG_Stuff.h"
#include "VL_Stuff.h"
#include "CT_Stuff.h"
#include "FP_Stuff.h"

VL53L0X Sensor

#define START_DISTANCE 200

Adafruit_VL53L0X lox = Adafruit_VL53L0X();

void setup_VL()
{
  lox.begin();
}


int last_range_measurement = -1;
int VL_x_offset = 118;
int VL_y_offset = 0;
int VL_x_width = 10;
int VL_y_height = 74;

void range_display(int range_measurement)
{
  int box_top = VL_y_offset;
  int box_bottom = box_top + VL_y_height;
  
  int box_left = VL_x_offset;
  int box_right = box_left + VL_x_width;
  
  int box_width = VL_x_width;
  int box_height = VL_y_height;
  
  int bar_top = box_top + 1;
  int bar_bottom = box_bottom - 1;

  int bar_left = box_left + 1;
  int bar_right = box_right -1;

  int bar_width = VL_x_width - 2;
  int bar_height = VL_y_height - 2;

  int bar_measure;
  
  if (last_range_measurement == -1)
    {
       // Draw Box
       tft.fillRect(box_left, box_top, box_width, box_height, ST7735_BLACK);     
       tft.drawRect(box_left, box_top, box_width, box_height, ST7735_YELLOW);     
    }
  
  if (last_range_measurement != range_measurement)
    {
      tft.fillRect(bar_left, bar_top, bar_width, bar_height, ST7735_BLACK);
      if (range_measurement > START_DISTANCE)
        tft.fillRect(bar_left, bar_top, bar_width, bar_height, ST7735_RED);
       else
        {
          bar_measure = ((float)range_measurement / (float)START_DISTANCE) * bar_height;
          tft.fillRect(bar_left, bar_bottom - bar_measure, bar_width, bar_measure, ST7735_GREEN);
        }
    }  

  last_range_measurement = range_measurement;
}

void VL_Frame()
{
  int range_measure;
  
  VL53L0X_RangingMeasurementData_t measure;
  lox.rangingTest(&measure, false); // pass in 'true' to get debug data printout!
  if (measure.RangeStatus != 4) 
    {  // phase failures have incorrect data          
      range_measure = measure.RangeMilliMeter;
    }
   else 
    {
      range_measure = 9999;
    }
  range_display(range_measure);  
//  delay(100);  
}

Heart Beat Sensor

Fingerprint Scanner

MLX90614 Sensor Code


IRTherm therm; // Create an IRTherm object to interact with throughout

void setup_CT()
{
  if (therm.begin() == false) { // Initialize thermal IR sensor
    Serial.println("Qwiic IR thermometer did not acknowledge! Freezing!");
    while (1);
  }
//  Serial.println("Qwiic IR Thermometer did acknowledge.");

  therm.setUnit(TEMP_F); // Set the library's units to Farenheit
  // Alternatively, TEMP_F can be replaced with TEMP_C for Celsius or
  // TEMP_K for Kelvin.

}

int last_object_measurement = -1;
int last_ambient_measurement = -1;

int CT_x_offset = 0;
int CT_y_offset = 0;
int CT_x_width = 44;
int CT_y_height = 74;

void CT_Frame()
{
  int box_top = CT_y_offset;
  int box_bottom = box_top + CT_y_height;
  
  int box_left = CT_x_offset;
  int box_right = box_left + CT_x_width;
  
  int box_width = CT_x_width;
  int box_height = CT_y_height;

  int ambient_x_offset = box_left + 1;
  int ambient_y_offset = box_top + 8;
  int object_x_offset = box_left + 1;
  int object_y_offset = ambient_y_offset + 20;

  int ambient_measurement;
  int object_measurement;

  if ((last_ambient_measurement == -1) && (last_object_measurement == -1))
    {
      tft.drawRect(box_left, box_top, box_width, box_height, ST7735_RED);
      tft.setCursor(ambient_x_offset, ambient_y_offset);
      tft.println("MLX90XX");
      tft.setCursor(object_x_offset, object_y_offset);
      tft.println("Object");
      
    }  
  
  therm.read();
  ambient_measurement = therm.ambient();
  object_measurement = therm.object();

  if ( last_ambient_measurement != ambient_measurement)
    {
      tft.setCursor(ambient_x_offset, ambient_y_offset + 8);
      tft.println(String(therm.ambient(), 2));
    }

  if ( last_object_measurement != object_measurement)
    {  
      tft.setCursor(object_x_offset, object_y_offset + 8);
      tft.println(String(therm.object(), 2));
    }

  last_ambient_measurement = ambient_measurement;
  last_object_measurement = object_measurement;      
}

AMG8833 Sensor

//low range of the sensor (this will be blue on the screen)
#define MINTEMP 20

//high range of the sensor (this will be red on the screen)
#define MAXTEMP 28

//the colors we will be using
const uint16_t camColors[] = {0x480F,
                              0x400F, 0x400F, 0x400F, 0x4010, 0x3810, 0x3810, 0x3810, 0x3810, 0x3010, 0x3010,
                              0x3010, 0x2810, 0x2810, 0x2810, 0x2810, 0x2010, 0x2010, 0x2010, 0x1810, 0x1810,
                              0x1811, 0x1811, 0x1011, 0x1011, 0x1011, 0x0811, 0x0811, 0x0811, 0x0011, 0x0011,
                              0x0011, 0x0011, 0x0011, 0x0031, 0x0031, 0x0051, 0x0072, 0x0072, 0x0092, 0x00B2,
                              0x00B2, 0x00D2, 0x00F2, 0x00F2, 0x0112, 0x0132, 0x0152, 0x0152, 0x0172, 0x0192,
                              0x0192, 0x01B2, 0x01D2, 0x01F3, 0x01F3, 0x0213, 0x0233, 0x0253, 0x0253, 0x0273,
                              0x0293, 0x02B3, 0x02D3, 0x02D3, 0x02F3, 0x0313, 0x0333, 0x0333, 0x0353, 0x0373,
                              0x0394, 0x03B4, 0x03D4, 0x03D4, 0x03F4, 0x0414, 0x0434, 0x0454, 0x0474, 0x0474,
                              0x0494, 0x04B4, 0x04D4, 0x04F4, 0x0514, 0x0534, 0x0534, 0x0554, 0x0554, 0x0574,
                              0x0574, 0x0573, 0x0573, 0x0573, 0x0572, 0x0572, 0x0572, 0x0571, 0x0591, 0x0591,
                              0x0590, 0x0590, 0x058F, 0x058F, 0x058F, 0x058E, 0x05AE, 0x05AE, 0x05AD, 0x05AD,
                              0x05AD, 0x05AC, 0x05AC, 0x05AB, 0x05CB, 0x05CB, 0x05CA, 0x05CA, 0x05CA, 0x05C9,
                              0x05C9, 0x05C8, 0x05E8, 0x05E8, 0x05E7, 0x05E7, 0x05E6, 0x05E6, 0x05E6, 0x05E5,
                              0x05E5, 0x0604, 0x0604, 0x0604, 0x0603, 0x0603, 0x0602, 0x0602, 0x0601, 0x0621,
                              0x0621, 0x0620, 0x0620, 0x0620, 0x0620, 0x0E20, 0x0E20, 0x0E40, 0x1640, 0x1640,
                              0x1E40, 0x1E40, 0x2640, 0x2640, 0x2E40, 0x2E60, 0x3660, 0x3660, 0x3E60, 0x3E60,
                              0x3E60, 0x4660, 0x4660, 0x4E60, 0x4E80, 0x5680, 0x5680, 0x5E80, 0x5E80, 0x6680,
                              0x6680, 0x6E80, 0x6EA0, 0x76A0, 0x76A0, 0x7EA0, 0x7EA0, 0x86A0, 0x86A0, 0x8EA0,
                              0x8EC0, 0x96C0, 0x96C0, 0x9EC0, 0x9EC0, 0xA6C0, 0xAEC0, 0xAEC0, 0xB6E0, 0xB6E0,
                              0xBEE0, 0xBEE0, 0xC6E0, 0xC6E0, 0xCEE0, 0xCEE0, 0xD6E0, 0xD700, 0xDF00, 0xDEE0,
                              0xDEC0, 0xDEA0, 0xDE80, 0xDE80, 0xE660, 0xE640, 0xE620, 0xE600, 0xE5E0, 0xE5C0,
                              0xE5A0, 0xE580, 0xE560, 0xE540, 0xE520, 0xE500, 0xE4E0, 0xE4C0, 0xE4A0, 0xE480,
                              0xE460, 0xEC40, 0xEC20, 0xEC00, 0xEBE0, 0xEBC0, 0xEBA0, 0xEB80, 0xEB60, 0xEB40,
                              0xEB20, 0xEB00, 0xEAE0, 0xEAC0, 0xEAA0, 0xEA80, 0xEA60, 0xEA40, 0xF220, 0xF200,
                              0xF1E0, 0xF1C0, 0xF1A0, 0xF180, 0xF160, 0xF140, 0xF100, 0xF0E0, 0xF0C0, 0xF0A0,
                              0xF080, 0xF060, 0xF040, 0xF020, 0xF800,
                             };

Adafruit_AMG88xx amg;
unsigned long delayTime;

#define AMG_COLS 8
#define AMG_ROWS 8
float pixels[AMG_COLS * AMG_ROWS];

#define INTERPOLATED_COLS 24
#define INTERPOLATED_ROWS 24

float get_point(float *p, uint8_t rows, uint8_t cols, int8_t x, int8_t y);
void set_point(float *p, uint8_t rows, uint8_t cols, int8_t x, int8_t y, float f);
void get_adjacents_1d(float *src, float *dest, uint8_t rows, uint8_t cols, int8_t x, int8_t y);
void get_adjacents_2d(float *src, float *dest, uint8_t rows, uint8_t cols, int8_t x, int8_t y);
float cubicInterpolate(float p[], float x);
float bicubicInterpolate(float p[], float x, float y);
void interpolate_image(float *src, uint8_t src_rows, uint8_t src_cols,
                       float *dest, uint8_t dest_rows, uint8_t dest_cols);

void setup_AMG()
{

  if (!amg.begin()) 
    {
      Serial.println("Could not find a valid AMG88xx sensor, check wiring!");
      while (1) { delay(1); }
    }

  //  Serial.println("-- Thermal Camera Test --");

}

void drawpixels(int x_offset, int y_offset, float *p, uint8_t rows, uint8_t cols, uint8_t boxWidth, uint8_t boxHeight) 
{
  int colorTemp;
//  int x_offset = 4, y_offset = 0;
  
  for (int y = 0; y < rows; y++) 
  {
    for (int x = 0; x < cols; x++) 
    {
      float val = get_point(p, rows, cols, x, y);
      if (val >= MAXTEMP) colorTemp = MAXTEMP;
      else if (val <= MINTEMP) colorTemp = MINTEMP;
      else colorTemp = val;

      uint8_t colorIndex = map(colorTemp, MINTEMP, MAXTEMP, 0, 255);
      colorIndex = constrain(colorIndex, 0, 255);
      //draw the pixels!
      uint16_t color;
      color = val * 2;
      tft.fillRect(x_offset + (boxWidth * x), y_offset + (((rows-1)  - y) * boxHeight), boxWidth, boxHeight, camColors[colorIndex]);
    }
  }
}

int AMG_x_offset = 44;
int AMG_y_offset = 0;
int AMG_x_width = 74;
int AMG_y_height = 74;

void AMG_Frame()
{

  int AMG_box_top = AMG_y_offset;
  int AMG_box_left = AMG_x_offset;
  int AMG_box_bottom = AMG_box_top + AMG_y_height;
  int AMG_box_right = AMG_box_left + AMG_x_width;
  int AMG_box_width = AMG_x_width;
  int AMG_box_height = AMG_y_height;
  int AMG_frame_left = AMG_box_left + 1;
  int AMG_frame_top = AMG_box_top + 1;
  
  int box_size = 3;
  
  //read all the pixels
  amg.readPixels(pixels);
  float dest_2d[INTERPOLATED_ROWS * INTERPOLATED_COLS];
  int32_t t = millis();
  interpolate_image(pixels, AMG_ROWS, AMG_COLS, dest_2d, INTERPOLATED_ROWS, INTERPOLATED_COLS);
//  Serial.print("Interpolation took "); Serial.print(millis() - t); Serial.println(" ms");
//  uint16_t boxsize = min(tft.width() / INTERPOLATED_COLS, tft.height() / INTERPOLATED_COLS);
  tft.drawRect(AMG_box_left, AMG_box_top, AMG_box_width, AMG_box_height, ST7735_ORANGE);
  drawpixels(AMG_frame_left,AMG_frame_top,dest_2d, INTERPOLATED_ROWS, INTERPOLATED_COLS, box_size, box_size);  
}

Core

void setup()
{
  Wire.begin();
  Serial.begin(115200);

  Serial.println("SETUP - Begin");
  Serial.println("TFT");
  setup_TFT();
  Serial.println("CT");
  setup_CT();
  Serial.println("AMG");
  setup_AMG();
  Serial.println("VL");
  setup_VL();  
  Serial.println("HB");
  setup_HB();
  Serial.println("FP");
  setup_FP();
  Serial.println("SETUP - End");
}


void loop()
{
  VL_Frame();
  AMG_Frame();
  CT_Frame();
  FP_Frame();
  HB_Frame();
}