Pseudo-Medical Monitor: Difference between revisions

From LVL1
Jump to navigation Jump to search
Line 99: Line 99:


===EKG-Breathing Unit===
===EKG-Breathing Unit===
[[File:JAC MED MON BioAmp EXG Pill.jpg|BioApm EXG Pill]]


[[File:JAC MED MON NeoPixel ATTINY85.jpg]]
[[File:JAC MED MON NeoPixel ATTINY85.jpg]]

Revision as of 15:40, 16 November 2021

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.

Operation

This device offers several functions to the user. The device is intended to be held by the user for self-analysis.

Modes

Passive Mode

This device provides several passive functions when not placed in specific function modes. The passive mode consists of a thermal image with a actual 8x8 resolution that is interpolated out to 24x24. A distance of device from user is recorded by a time of flight laser range finder. The distance is displayed with a color bar graph. A bar color of RED indicates the distance to user is beyond 50cm. A bar of GREEN indicates a distance of 50cm or less. The final BLUE bar indicates a distance of less than 25cm and invokes the EXG Sensor mode. An Object and Ambient temperature is displayed.

Fingerprint Mode

Placement of a finger on the fingerprint scanner activated this mode. Onscreen instructions direct the user to place finger an scanner. This suspends the Passive Mode. The onscreen instructions tell the user to keep finger on scanner until a fingerprint image is captured and displayed on the screen. When the scanner ring is pulsing RED, a fingerprint is not displayed. A GREEN ring indicates a fingerprint image has been captured and is being displayed. Upon removal of finger from scanner, the device returns to Passive Mode.

Pulse Mode

Placement of a finger on the pulse sensor activates this mode. This suspends the Passive Mode. The sensor reads the pulse and provides a beat indicator as well as an estimate pulse rate. Future upgrades will display the O2 Saturation level (stray diagnosis number under pulse rate). Upon removal of finger from sensor, the device returns to Passive Mode.

EXG Mode

When the device distance is met, or a finger is placed on the distance sensor, the EXG Mode is engaged. This suspends the Passive Mode. The entire screen is cleared and converted to display ~152 readings with automatic scaling to screen. If the distance reading remains in this modes range, additional readings are made and displayed until distance criteria changes. Upon mode change, the entire screen is redrawn and the fingerprint information is reset to not recorded. (Finger is over distance sensor, readings are from the BioAmp EXG Pill in a black box)

Tests

Eye Test

Hearing Test

Grip and Reflex Test

Extended EXG Tests

Design (Updated 11/05/2021)

  • 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.
  • ECG,EMG,EEG,EOG Function depending on where you put the electrodes!

Parts List

  • Arduino Due Generic Clone (alternative: ITEADUINO DUE)

  • Arduino Mega Prototype Shield Generic Clone (alternative: KEYESTUDIO) (EXG connector added after photo)

  • Dual Encoder Control with LED indicators

BioApm EXG Pill

  • 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)

  • Grip and Reflex Sensor
    • Cylinder 5x5x15 cm. 0.25 cm. thickness
    • Copper Tape Hoop
    • Velostat Wrap Sheet
    • MPU6050 Sensor
    • Vibration Motor
    • Vibration Motor Control Circuit
    • RJ45 Socket Breakout Board
    • Momentary Push Button Switch
    • Foam Wrap 0.5 cm.

  • Test Unit for Add-Ons


Assemblies

EKG-Breathing Unit

BioApm EXG Pill

Hand Held Unit

Wiring

Main Shield

Pin Mapping
Due Pin Function MLX90614 AMG8833 VL53L0X MAX30102 FP Scan TFT SD Socket EXG
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
A0 Signal
SPI MISO SD_MISO
SPI MOSI SDA SD_MOSI
SPI SCK SCK SD_SCK

Encoders Panel

Pin Mapping
Device SIGNAL DUE Pin Function
Controls Vin 3.3V Power
Controls SDA 20 SDA-I2C
Controls SCL 21 SCL-I2C
Controls INT 2 GPIO
Controls GND GND Power

MIDI Shield

Pin Mapping
Device SIGNAL DUE Pin Function
VS1103 Vin 3.3V Power
VS1103 GND GND Power
VS1103 TX 17 RX-Serial2
VS1103 RX 16 TX-Serial2
VS1103 GPIO0 30 GPIO
VS1103 GPIO1 32 GPIO
VS1103 RST 28 GPIO
VS1103 DREQ 24 GPIO
VS1103 BSYNC 26 GPIO
VS1103 CS 22 GPIO
VS1103 SO SPI-HDR MISO
VS1103 SI SPI-HDR MOSI
VS1103 SCLK SPI-HDR SCK

Ports Shield

Pin Mapping
Port # Line # DUE Pin Function
0 1 3.3V Power
0 2 GND Power
0 3 70 I2C-SDA
0 4 71 I2C-SCL
0 5 53 GPIO0
0 6 51 GPIO1
0 7 49 GPIO2
0 8 65/A11 Analog0


Pin Mapping
Port # Line # DUE Pin Function
1 1 3.3V Power
1 2 GND Power
1 3 70 I2C-SDA
1 4 71 I2C-SCL
1 5 27 GPIO0
1 6 29 GPIO1
1 7 31 GPIO2
1 8 60/A6 Analog0


Pin Mapping
Port # Line # DUE Pin Function
2 1 3.3V Power
2 2 GND Power
2 3 70 I2C-SDA
2 4 71 I2C-SCL
2 5 64/A10 Analog0
2 6 63/A9 Analog1
2 7 62/A8 Analog2
2 8 61/A7 Analog3


Pin Mapping
Port # Line # DUE Pin Function
3 1 3.3V Power
3 2 GND Power
3 3 70 I2C-SDA
3 4 71 I2C-SCL
3 5 59/A5 Analog0
3 6 58/A4 Analog1
3 7 57/A3 Analog2
3 8 56/A2 Analog3

Schematics

Control Panel Circuit

EXG BIO AMP Circuit used as EKG

Respiratory Sensor

Vibrating Motor Circuit

Code (Updated 10/24/2021)

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

The following code is a mixing and matching of the example code provided by the referenced Special Libraries.

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"
#include "EXG_Stuff.h"

 

VL53L0X Sensor (VL_Stuff.h)

#define START_DISTANCE 500
#define EXG_DISTANCE 150

Adafruit_VL53L0X lox = Adafruit_VL53L0X();

bool EXG_Mode = false;

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 setup_VL()
{
  lox.begin();
}

void VL_Reset()
{
  last_range_measurement = -1;
}

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
        {
          if (range_measurement < EXG_DISTANCE)
            {
              bar_measure = ((float)range_measurement / (float)START_DISTANCE) * bar_height;
              tft.fillRect(bar_left, bar_bottom - bar_measure, bar_width, bar_measure, ST7735_BLUE);              
            }
           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;
}

int VL_Reading()
{
  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;
    }

  if (range_measure < EXG_DISTANCE)
    EXG_Mode = true;
   else
    EXG_Mode = false;

  return(range_measure);  
}

void VL_Frame()
{
  int range_measure;
  
  range_measure = VL_Reading();
  
  range_display(range_measure);  

//  delay(100);  
}

Heart Beat Sensor (HB_Stuff.h)


static const unsigned char PROGMEM still_heart_logo_bmp[] =
{ 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x0F, 0x00, 0x00, 0x67, 0x18, 0xC0, 0x00, 0xC1, 0xB0, 0x60,
0x01, 0x80, 0xE0, 0x20, 0x01, 0x00, 0xC0, 0x30, 0x01, 0x00, 0x40, 0x10, 0x01, 0x00, 0x40, 0x10, 
0x01, 0x04, 0x00, 0x10, 0x01, 0x04, 0x00, 0x10, 0x01, 0x0C, 0x00, 0x30, 0x00, 0x88, 0x80, 0x20, 
0x00, 0x8B, 0x80, 0x40, 0x0F, 0xF3, 0x70, 0x80, 0x00, 0x13, 0x01, 0x80, 0x00, 0x19, 0x03, 0x00, 
0x00, 0x0C, 0x06, 0x00, 0x00, 0x06, 0x0C, 0x00, 0x00, 0x03, 0x18, 0x00, 0x00, 0x01, 0xB0, 0x00, 
0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

static const unsigned char PROGMEM beat_heart_logo_bmp[] =
{ 
0x01, 0xF0, 0x0F, 0x80, 0x06, 0x1C, 0x38, 0x60, 0x18, 0x06, 0x60, 0x18, 0x10, 0x01, 0x80, 0x08, 
0x20, 0x01, 0x80, 0x04, 0x40, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x02, 0xC0, 0x00, 0x08, 0x03,
0x80, 0x00, 0x08, 0x01, 0x80, 0x00, 0x18, 0x01, 0x80, 0x00, 0x1C, 0x01, 0x80, 0x00, 0x14, 0x00,
0x80, 0x00, 0x14, 0x00, 0x80, 0x00, 0x14, 0x00, 0x40, 0x10, 0x12, 0x00, 0x40, 0x10, 0x12, 0x00,
0x7E, 0x1F, 0x23, 0xFE, 0x03, 0x31, 0xA0, 0x04, 0x01, 0xA0, 0xA0, 0x0C, 0x00, 0xA0, 0xA0, 0x08,
0x00, 0x60, 0xE0, 0x10, 0x00, 0x20, 0x60, 0x20, 0x06, 0x00, 0x40, 0x60, 0x03, 0x00, 0x40, 0xC0,
0x01, 0x80, 0x01, 0x80, 0x00, 0xC0, 0x03, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0x30, 0x0C, 0x00,
0x00, 0x08, 0x10, 0x00, 0x00, 0x06, 0x60, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x01, 0x80, 0x00  
};

MAX30105 particleSensor;

#define HB_INT 4

int HB_x_offset = 0;
int HB_y_offset = 78;
int HB_x_width = 46;
int HB_y_height = 82;

int HB_initialize_screen = -1;

void HB_Reset()
{
  HB_initialize_screen = -1;  
}

void setup_HB()
{
  // Initialize sensor
  if (!particleSensor.begin()) //Use default I2C port, 400kHz speed
  {
    Serial.println("MAX30105 was not found. Please check wiring/power. ");
    while (1);
  }
//  Serial.println("Place your index finger on the sensor with steady pressure.");

  particleSensor.setup(); //Configure sensor with default settings
  particleSensor.setPulseAmplitudeRed(0x0A); //Turn Red LED to low to indicate sensor is running
  particleSensor.setPulseAmplitudeGreen(0); //Turn off Green LED

}


void HB_Frame()
{
  int box_top = HB_y_offset;
  int box_bottom = box_top + HB_y_height;
  
  int box_left = HB_x_offset;
  int box_right = box_left + HB_x_width;
  
  int box_width = HB_x_width;
  int box_height = HB_y_height;

  const byte RATE_SIZE = 6; //Increase this for more averaging. 4 is good.
  byte rates[RATE_SIZE]; //Array of heart rates
  long pulse_ir[RATE_SIZE];
  long pulse_red[RATE_SIZE];
  
  byte rateSpot = 0;
  byte O2Spot = 0;
  long O2AVG = 0;
  long lastBeat = 0; //Time at which the last beat occurred
  float beatsPerMinute;
  int beatAvg = -1, lastAvg = 0;
  long irAVG = 0;
  long redAVG = 0;

  bool beatDetect = false;
  int latentBeatMax = 2;
  int latentBeat = 0;

  long irValue = 0;
  long redValue = 0;
  long maxValue = 0;   
  long lastValue = 0;
  long turnValue = 0;
  long lastMax = 0;

  int tft_HB_Screen = 0;
  int last_HB_Screen = -1;
  
  unsigned long start_time;
  unsigned long delta = 0;
  int HB_detect_threshold = 7000;

  // Clear rate calculating array
  for(byte x = 0; x < RATE_SIZE ; ++x)
    rates[x] = 0;

  if (HB_initialize_screen == -1)
    {
      tft.fillRect(box_left, box_top, box_width, box_height, ST7735_BLACK);
      tft.drawRect(box_left, box_top, box_width, box_height, ST7735_BLUE);
      tft.setTextColor(ST7735_WHITE, ST7735_BLACK);             
      tft.setCursor(box_left + 3,box_top + 4);                
      tft.println("Place "); 
      tft.setCursor(box_left + 3,box_top + 16);
      tft.println("your ");            
      tft.setCursor(box_left + 3,box_top + 28);
      tft.println("finger ");            
      tft.setCursor(box_left + 3,box_top + 40);
      tft.println("on ");                        
      tft.setCursor(box_left + 3,box_top + 52);
      tft.println("sensor ");                                    
      HB_initialize_screen = 0;
      for (byte x = 0 ; x < RATE_SIZE ; x++)
        rates[x] = 0;
    }

  irValue = particleSensor.getIR();
  redValue = particleSensor.getRed();
  
  while(irValue > HB_detect_threshold)
    {
      irValue = particleSensor.getIR();    //Reading the IR value it will permit us to know if there's a finger on the sensor or not
      redValue = particleSensor.getRed();
      
//      Serial.print(irValue);
//      Serial.print(",");
//      Serial.print(redValue);
//      Serial.print(",");
//      Serial.print(maxValue);
//      Serial.print(",");
//      Serial.print(turnValue);
//      Serial.print(",");
//      Serial.print(lastMax);
      Serial.println();
           
      if (irValue > 7000)
        {  
          //finger is detected
          tft_HB_Screen = 1;
          if (irValue > maxValue)
            maxValue = irValue;

//        if ((irValue < maxValue - 200) && (irValue > lastValue + 20)) // alternate detect beat method
          if (irValue < maxValue - 200)
            {
              beatDetect = true;
              lastMax = maxValue;
              maxValue = irValue;
              turnValue = lastValue;
              latentBeat = latentBeatMax;
            }

          lastValue = irValue;   
          
          if (beatDetect == true)
            {
              beatDetect = false;

              //We sensed a beat!
              delta = millis() - lastBeat;                   //Measure duration between two beats
              lastBeat = millis();

              beatsPerMinute = 60 / (delta / 1000.0);           //Calculating the BPM
              lastAvg = beatAvg;             

              pulse_ir[O2Spot++] = irValue;
              pulse_red[O2Spot] = redValue;
/*              
              if (O2Spot == RATE_SIZE)
                {
                  for (byte x = 0; x < RATE_SIZE; ++x)
                    {
                      irAVG = irAVG + pulse_ir[x];
                      redAVG = redAVG + pulse_red[x];
                    }
                  O2AVG = ((float)irAVG / (float) redAVG) * 100;
                  tft.setCursor(box_left + 4,box_top + 56);                
                  tft.println(O2AVG);                         
                  O2Spot = 0;
                }
*/              
              if ((beatsPerMinute < 255.0) && (beatsPerMinute > 20.0))               //To calculate the average we strore some values (4) then do some math to calculate the average
                {
                  rates[rateSpot++] = (byte)beatsPerMinute; //Store this reading in the array
                  rateSpot %= RATE_SIZE; //Wrap variable

                  //Take average of readings

                  beatAvg = 0;
                  for (byte x = 0 ; x < RATE_SIZE ; x++)
                    beatAvg += rates[x];
                  beatAvg /= RATE_SIZE;
                } 
            }
        }


      if (irValue < 7000)
        {       //If no finger is detected it inform the user and put the average BPM to 0 or it will be stored for the next measure
          tft_HB_Screen = 0;
          beatAvg=0;
          lastAvg = 0;
          for(byte x = 0; x < RATE_SIZE ; ++x)
            rates[x] = 0;
        }
     
      switch(tft_HB_Screen)
        {
          case 0:            
            if (last_HB_Screen != 0)
              tft.fillRect(box_left + 1, box_top + 1, box_width - 2, box_height - 2, ST7735_BLACK);              
            tft.setTextColor(ST7735_WHITE, ST7735_BLACK);             
            tft.setCursor(box_left + 3,box_top + 4);                
            tft.println("Place "); 
            tft.setCursor(box_left + 3,box_top + 16);
            tft.println("your ");            
            tft.setCursor(box_left + 3,box_top + 28);
            tft.println("finger ");            
            tft.setCursor(box_left + 3,box_top + 40);
            tft.println("on ");                        
            tft.setCursor(box_left + 3,box_top + 52);
            tft.println("sensor ");                                    
            break;
          case 1:         
            if (last_HB_Screen != 1)
              tft.fillRect(box_left + 1,box_top + 1,32,33,ST7735_BLACK);
            tft.setTextColor(ST7735_WHITE, ST7735_BLACK); 
            if (latentBeat == latentBeatMax)  
              {
                tft.fillRect(box_left + 1,box_top + 1, box_width - 2, 33, ST7735_BLACK);
                tft.drawBitmap(box_left + 2,box_top + 2, beat_heart_logo_bmp, 32, 32, ST7735_RED);    //Draw the second picture (bigger heart)              
              }
            if (latentBeat == 0)
              {
                tft.fillRect(box_left + 1,box_top + 1, box_width - 2, 33, ST7735_BLACK);
                tft.drawBitmap(box_left + 2, box_top + 2, still_heart_logo_bmp, 32, 32, ST7735_WHITE);       //Draw the first bmp picture (little heart)
                latentBeat = -1;
              }                                          
            if (latentBeat > 0)
              -- latentBeat;              
            if (lastAvg != beatAvg)
              {
                tft.fillRect(box_left + 1,box_top + 34 ,box_width - 2,box_height - 35, ST7735_BLACK);
                tft.setCursor(box_left + 4,box_top + 34);                
                tft.println("BPM");             
                tft.setCursor(box_left + 4,box_top + 44);                
                tft.println(beatAvg);           
                lastAvg = beatAvg;
              }
            break;
          case 2:
            tft.fillRect(box_left + 1,box_top + 2, 32, 32, ST7735_BLACK);
            tft.drawBitmap(box_left + 2,box_top + 2, beat_heart_logo_bmp, 32, 32, ST7735_RED);    //Draw the second picture (bigger heart)              

            tft.fillRect(box_left + 1,box_top + 34 ,box_width - 2,box_height - 35, ST7735_BLACK);
            tft.setCursor(box_left + 4,box_top + 34);                
            tft.println("BPM");             
            tft.setCursor(box_left + 4,box_top + 44);                
            tft.println(beatAvg);                       
            break;  
          case 3:
            break;    
        }
     last_HB_Screen = tft_HB_Screen;    
     delay(100); 
    }
}

 

Fingerprint Scanner (FP_Stuff.h)

#define FPSerial Serial1
#define FP_INIT 3

DFRobot_ID809 fingerprint;
String desc;
File myFile;

//#define QUARTER
#ifdef  QUARTER
uint8_t data[6400];   //Quarter image
#else
uint8_t data[25600];  //Full image
#endif

int FP_x_offset = 46;
int FP_y_offset = 78;
int FP_x_width = 82;
int FP_y_height = 82;

bool fingerprint_captured = false;
bool FP_first_pass = true;

void setup_FP()
{
  pinMode(FP_INIT,INPUT);
  /*Init FPSerial*/
  FPSerial.begin(115200);
  /*Take FPSerial as communication port of fingerprint module */
  fingerprint.begin(FPSerial);
  
  /*Wait for Serial to open*/
  /*Test whether device can communicate properly with mainboard 
    Return true or false
    */
  while(fingerprint.isConnected() == false)
  {
    Serial.println("Communication with device failed, please check connection");
    /*Get error code information */
    desc = fingerprint.getErrorDescription();
    Serial.println(desc);
    delay(1000);
  }
}


void FP_Reset()
{
  fingerprint_captured = false;
  FP_first_pass = true;  
}

void FP_Frame()
{
  int box_top = FP_y_offset;
  int box_bottom = box_top + FP_y_height;
  
  int box_left = FP_x_offset;
  int box_right = box_left + FP_x_width;
  
  int box_width = FP_x_width;
  int box_height = FP_y_height;

  int p_x, p_y, p_i, p_data, p_temp;
  
  if (FP_first_pass)
    {
      tft.drawRect(box_left, box_top, box_width, box_height, ST7735_MAGENTA);
      tft.fillRect(box_left + 1, box_top + 1, box_width - 2, box_height - 2, ST7735_BLACK);
      fingerprint.ctrlLED(/*LEDMode = */fingerprint.eBreathing, /*LEDColor = */fingerprint.eLEDRed, /*blinkCount = */0);
      tft.setCursor(box_left + 4, box_top + 4);
      tft.println("Place Finger");
      tft.setCursor(box_left + 4, box_top + 16);
      tft.println("on scanner.");      
      FP_first_pass = false;
    }

  if ((fingerprint.detectFinger()) && (!fingerprint_captured))
    {
      tft.setCursor(box_left + 4, box_top + 4);
      tft.println("Keep Finger ");
      tft.setCursor(box_left + 4, box_top + 16);
      tft.println("on scanner.");      
      
      fingerprint.getFingerImage(data);
      fingerprint.ctrlLED(/*LEDMode = */fingerprint.eKeepsOn, /*LEDColor = */fingerprint.eLEDGreen, /*blinkCount = */0);
      for (p_x=0; p_x < 80; ++p_x)
        for (p_y=0; p_y < 80; ++p_y)
          {
            p_temp = (p_x * 2);
            p_temp = p_temp + (p_y * 160);
            p_data = data[p_temp]/4; // image element

            p_temp = p_temp + 1;
            p_data = p_data + data[p_temp]/4; // image element to the right

            p_temp = (p_x * 2);
            p_temp = p_temp + ((p_y + 1) * 160);
            p_data = p_data + data[p_temp]/4;  // image element below

            p_temp = (p_x + 1) * 2;
            p_temp = p_temp + ((p_y + 1) * 160);
            p_data = p_data + data[p_temp]/4;   // image element to the right, one element down
                 
            p_data = p_data/4;
            tft.drawPixel(box_left+1+p_x,box_top+1+p_y,p_data<<5); 
          }        
    }
    
}

 

MLX90614 Sensor Code (CT_Stuff.h)

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

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 setup_CT()
{
  if (therm.begin() == false) { // Initialize thermal IR sensor
    Serial.println("IR thermometer Failed");
    while (1);
  }

  therm.setUnit(TEMP_F); // Set the library's units to Farenheit

}


void CT_Reset()
{
  last_object_measurement = -1;
  last_ambient_measurement = -1;  
}

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 (AMG_Stuff.h)

//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 AMG_Reset()
{
  
}

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);  
}

 

BioAmp EXG (EXG_Stuff.h)

#define EXG_SAMPLE_SIZE 155
#define EXG_PIN A0

int EXG_x_offset = 0;
int EXG_y_offset = 0;
int EXG_x_width = 128;
int EXG_y_height = 160;

void EXG_Frame()
{  
  
  int box_top = EXG_y_offset;
  int box_bottom = box_top + EXG_y_height;
  
  int box_left = EXG_x_offset;
  int box_right = box_left + EXG_x_width;
  
  int box_width = EXG_x_width;
  int box_height = EXG_y_height;

  tft.fillRect(box_left, box_top, box_width, box_height, ST7735_WHITE);
  tft.drawRect(box_left+1, box_top+1, box_width-3, box_height-3, ST7735_BLACK);
  
}

void EXG_Display_Data(int * EXG_data)
{
  int box_top = EXG_y_offset;
  int box_bottom = box_top + EXG_y_height;
  
  int box_left = EXG_x_offset;
  int box_right = box_left + EXG_x_width;
  
  int box_width = EXG_x_width;
  int box_height = EXG_y_height;
  
  int max_value = -5000;
  int min_value = 5000;
  int data_point1,data_point2;
  
  tft.fillRect(box_left+2, box_top+2, box_width-5, box_height-5, ST7735_WHITE);
  
  for(int x=0;x<EXG_SAMPLE_SIZE; ++x)
    {
      if (EXG_data[x] > max_value)
        max_value = EXG_data[x];
      if (EXG_data[x] < min_value)
        min_value = EXG_data[x];        
    }
  for(int x=0;x<EXG_SAMPLE_SIZE-1; ++x)
  {
    data_point1 = map(EXG_data[x],min_value,max_value,2,124);
    data_point2 = map(EXG_data[x+1],min_value,max_value,2,124);
    tft.drawLine(data_point1,x+2,data_point2,x+3,ST7735_RED);
    Serial.println(data_point1);
  }
    
}

void EXG_Collect()
{
  int EXG_Delay = 30;  
  int EXG_data[EXG_SAMPLE_SIZE];
  
  for(int x=0;x<EXG_SAMPLE_SIZE; ++x)
    {
      EXG_data[x] = analogRead(EXG_PIN);
      delay(EXG_Delay);
    }

  EXG_Display_Data(EXG_data);
  
}


void EXG_Demo()
{
  EXG_Frame();
  while(1)
    {
      EXG_Collect();      
    }
}
 

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();

  if (EXG_Mode)
    {
      EXG_Frame();
      while(EXG_Mode)
        {
          EXG_Collect();
          VL_Reading();
        }
      TFT_Reset();
      VL_Reset();
      AMG_Reset();
      CT_Reset();
      FP_Reset();  
      HB_Reset();
    }  
}
 

Video

YouTube Demo Video

Return to Index

The JAC Project Index