Pseudo-Medical Monitor: Difference between revisions
| Line 89: | Line 89: | ||
** Momentary Push Button Switch | ** Momentary Push Button Switch | ||
** Foam Wrap 0.5 cm. | ** Foam Wrap 0.5 cm. | ||
==Assemblies== | |||
===Hand Held Unit=== | |||
[[File:JAC MED MON Hand Unit EXPLODED.jpg]] | |||
[[File:JAC MED MON Hand Unit UNCOVERED.jpg]] | |||
==Wiring== | ==Wiring== | ||
Revision as of 15:07, 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
- BioAmp EXG Pill Excellent Hackaday.io Project by Deepak Khatri
- 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.
Assemblies
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();
}
}




















