Hardware Setup
The system uses three main sensors:
- MAX30102 - Optical heart rate and blood oxygen sensor
- GSR Sensor - Galvanic Skin Response sensor for stress detection
- BH1750 - Ambient light sensor
These sensors work together to collect physiological data that can indicate mental health status.
// Sensor Objects in the code
MAX30105 particleSensor; // MAX30102 sensor object
BH1750 lightMeter; // BH1750 light sensor
const int GSR_PIN = 34; // GSR sensor pin
The sensors are connected as follows:
- MAX30102 and BH1750 use I2C communication (connected to SDA and SCL pins)
- GSR sensor is connected to analog pin 34
The code initializes these connections in the setup()
function:
void setup() {
Wire.begin(); // Initialize I2C
particleSensor.begin(Wire, I2C_SPEED_FAST); // MAX30102
lightMeter.begin(); // BH1750
// GSR is analog so only needs pin declaration
}
Code Implementation
The MAX30102 sensor detects heart rate by measuring changes in blood volume using optical technology (PPG). The code implements:
- IR LED shines light into the skin
- Photodetector measures reflected light
- Pulse detection algorithm identifies beats
- Beat-to-beat intervals are calculated for HRV
if(irValue > 50000) { // Finger detected
if(checkForBeat(irValue)) {
long delta = millis() - lastBeat;
lastBeat = millis();
beatsPerMinute = 60000.0 / delta;
}
}
Based on Shaffer et al. (2014) HRV measurement standards
The moving average calculation separates the GSR signal into two components:
- Tonic component (slow-changing baseline)
- Phasic component (fast-changing responses)
This is implemented with a 5-sample moving average window:
// Moving average for EDA tonic (Boucsein, 2012)
float edaMovingAverage(float newVal) {
eda_buffer[buffer_index] = newVal;
buffer_index = (buffer_index + 1) % WINDOW_SIZE;
float sum = 0;
for(int i=0; i<WINDOW_SIZE; i++) sum += eda_buffer[i];
return sum/WINDOW_SIZE;
}
HRV is calculated using SDNN (Standard Deviation of NN intervals), which is a well-established metric in psychophysiology research (Castaldo et al., 2019).
The code implements this by:
- Storing the last 16 RR intervals (time between heartbeats)
- Calculating the mean interval
- Computing the standard deviation of these intervals
// Calculate SDNN (Standard Deviation of NN intervals)
float calculateSDNN() {
float mean = 0.0f;
for (int i = 0; i < RR_BUFFER_SIZE; i++) {
mean += rrIntervals[i];
}
mean /= RR_BUFFER_SIZE;
// ... variance calculation ...
return sqrt(variance);
}
HRV is an important indicator of autonomic nervous system activity and stress levels.
Mental Health Analysis
The system uses a neural network (defined in MentalHealthNN.h) that takes sensor inputs and outputs probabilities for four mental states:
- Normal
- Stress
- Anxiety
- Depression
The prediction happens in the detectMentalHealth()
function:
void detectMentalHealth(float edaTonic, float edaPhasic, float bpm, float hrv, float lux,
MentalHealthStatus& status, float* probabilities) {
// Get prediction from neural network
status = mentalHealth.predict(edaTonic, edaPhasic, bpm, hrv, lux);
// Get probabilities for each class
mentalHealth.getPredictionProbabilities(edaTonic, edaPhasic, bpm, hrv, lux, probabilities);
}
The neural network was likely trained on labeled physiological data from individuals with known mental health conditions.
The BH1750 light sensor measures ambient light levels which are important because:
- Light exposure affects circadian rhythms and mood (Golden et al., 2005)
- Low light levels may correlate with depressive symptoms
- Sudden light changes can indicate environmental stressors
The light data is read and included in the mental health analysis:
// Read BH1750 ambient light sensor
float lux = lightMeter.readLightLevel();
// Included in neural network input
detectMentalHealth(..., lux, ...);
Data Upload
The system uploads sensor data to GitHub repositories every 10 seconds using the GitHub API:
- Data is formatted as JSON documents
- Base64 encoded for GitHub API requirements
- Uploaded using HTTP PUT requests with authentication
- SHA hashes are tracked for version control
// Example of GitHub upload function
void uploadToGitHub(DynamicJsonDocument doc, const char* url, String& lastSHA) {
HTTPClient http;
http.begin(url);
http.addHeader("Content-Type", "application/json");
http.addHeader("Authorization", token);
String jsonStr;
serializeJson(doc, jsonStr);
String encodedData = base64::encode(jsonStr);
String payload = "{\"message\":\"Update data\",\"content\":\"" +
encodedData + "\",\"sha\":\"" + lastSHA + "\"}";
int httpCode = http.PUT(payload);
// ... handle response ...
}
Four separate JSON files are maintained for each sensor type and the mental status.