# Project export: Haven

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: TreeHacks 2025
- Tagline: Solving anxiety the stylish way. Haven is a fashion-tech garment that integrates ML-driven anxiety detection with real-time physical intervention, providing instant, discreet relief.
- Devpost: https://devpost.com/software/haven-6nfwr9
- GitHub: https://github.com/fukaikeda/treehacks2025
- Video: https://www.youtube.com/embed/1hi2ZNOsaTs?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 3 GitHub contributor(s) — fukaikeda (7 commits), clarizzaa (4 commits), CharChang888 (1 commits)

## Devpost submission (written by the team)

### Overview

TLDR: 66% of college students report high anxiety—it’s common but hard to manage in busy lives. Things like meditation and weighted blankets aren’t always an option. We needed a solution that integrates seamlessly into our lives . Haven is a garment that senses anxiety using our custom trained model, and provides instant relief through deep pressure and vibration therapy. We're redefining how we manage stress—effortlessly, stylishly, and scientifically.

### Inspiration

Anxiety is real. According to the American College Health Association, a staggering 66% of college students reported experiencing overwhelming anxiety in the past year. Even more concerning, 41.6% of college students meet the clinical criteria for an anxiety disorder. For us, we decided to combat this issue when we realized that there was no way to easily manage anxiety during those critical moments. As students, we juggle academic pressure, social expectations, and personal challenges—all of which can trigger anxiety. We've each had our share of panic attacks in public, moments where we felt isolated, unsure of how to stay calm. It's painful, and sometimes, help isn't within reach. We asked ourselves: What if there was a way to make these moments easier? What if we could create something that not only helped us deal with anxiety in real time, but also fit seamlessly into our busy, social lives? A solution that wouldn't be obvious to others or require pulling out a gadget or device, but one that could work discreetly and automatically in the background. Through research and conversation, we discovered that anxiety often showed up in physical signs: like a racing heartbeat, a hot flash, or maybe shallow breaths. These are things we could measure. Thanks to smart devices like the Apple Watch, we could track our biometric data—like heart rate variability, skin temperature, and motion patterns—and use this information to understand when anxiety was starting to take over. We also learned that there were a vast variety of therapies and technologies proven beneficial for anxiety, like deep pressure or vibration, that are currently underutilized. Therefore, that became our goal: create a garment that could adapt to those triggers and respond with these underutilized therapies. That’s when we combined this issue with our passion for fashion tech: what if we created clothing that can help with anxiety? This was the birth of Haven: the world’s first garment that detects and cares for anxiety.

### What it does

Haven is a smart garment designed to detect and alleviate anxiety in real time. Using data from pre-your own wearables and biometric sensors embedded within the garment, it continuously monitors physiological indicators like heart rate variability, skin temperature, and breathing patterns. When it detects signs of anxiety—such as an elevated heart rate or erratic breathing—Haven automatically activates its built-in therapeutic mechanisms. These include deep pressure stimulation, mimicking the calming effect of a weighted blanket, and vibration therapy, which provides soothing rhythmic pulses to help regulate stress responses. All of this happens seamlessly, without the need for user intervention, allowing wearers to manage their anxiety discreetly and effortlessly.

### How we built it

We used Python to develop and train machine learning models that analyze raw PPG data collected from long-term use of our wearable device. Our models leverage data from the Terra API, processing photoplethysmography (PPG) waveforms to classify heart rate patterns as “regular,” “irregular,” or “AFib.” Beyond classification, our models generate personalized probability scores and severity indicators for various heart conditions, aligning with the diagnostic insights provided in our metadata.csv. By tracking heart rate variability (HRV) trends over time, our system can detect dysregulated patterns indicative of potential health risks. For example, elevated HRV in a chaotic pattern could suggest an underlying anxiety disorder, prompting users to seek further evaluation or medical intervention for long-term relief. By combining wearable health monitoring, machine learning, and personalized cardiac risk assessment, our project empowers individuals with real-time insights into their heart health, supporting both preventative care and early diagnosis. Tech Stack Ideation & Research Perplexity ChatGPT Claude Cursor Data Collection from Wearables Apple Watch: Real-time data streaming via Swift in XCode Terra API: Accessing sponsor-provided Garmin data Firestore (Firebase): Storing real-time streaming data from wearables MongoDB: Storing simulated Garmin data for testing JavaScript (Node.js): Extracting and processing relevant data from MongoDB Machine Learning & Pattern Recognition Machine Learning Frameworks: Scikit-learn (traditional ML models) TensorFlow/PyTorch (deep learning models) PPG Signal Processing: HeartPy for feature extraction Custom-Trained ML Model: Classifying PPG waveforms as Regular, Irregular, AFib Data Handling: Pandas, NumPy for preprocessing and metadata fusion Hardware & Actuation Finite State Machine (FSM) for device control Microcontroller: Arduino-based (C++) Mechanical Design: Custom Fusion 360 mechanisms Fabrication: 3D-printed components using Bambu Studio Actuation & Stimulation: -Peltier cooling modules Vibration motors Linear actuator system (PWM powered) Biosensors: Additional data backup to wearables Assembly: Circuit soldering, garment pattern design, and sewing UI/UX & Design Figma: Prototyping & interface design Adobe Illustrator: Visual assets & garment design

### Challenges we ran into

Machine Learning Complexity We initially experimented with deep learning models, but simpler traditional ML models proved more efficient and accurate, leading us to pivot our approach mid-development. Real-Time Wearable Data Extraction Extracting real-time data from the Apple Watch was challenging without an Apple Developer account. We built custom Swift scripts to pull HealthKit data, but transmitting it to our Arduino-connected clothing via Firestore caused compatibility issues and slow speeds. Ultimately, we simulated real-time streaming using TerraAPI’s Garmin data. Hardware & Time Constraints Designing a functional wearable typically takes months—we had hours. We improvised with scrap materials, modified existing 3D models, and streamlined hardware-software integration to build our prototype within the hackathon timeframe. Despite these hurdles, we adapted quickly and delivered a functional prototype for real-time cardiac monitoring and anxiety detection.

### Accomplishments we're proud of

Once the raw PPG data is correctly classified, our ML models generate probability scores and severity indicators with high accuracy! Beyond functionality, we also focused on aesthetic design—our hardware device is not just wearable tech but a fashionable, trendy, and timeless top that seamlessly integrates health monitoring with style.

### What we learned

Learn to be scrappy and use the existing resources around you to build a creative product (using studio scrap fabrics!!) Don’t be afraid to ask, mentors and CAs helped so much in our project, especially as a beginner and starting out was very daunting but everyone was so willing to help

### What's next

An extended line of clothing, the next in perhaps a more masculine flavor Health warning signals: personalized insights on when anxiety attacks might be worth seen a doctor for Perhaps a venture...?

## README (from the GitHub repository)

treehacks 2025 project

details in devpost


## Detected evidence (automated analysis)

Indexed codebase: 25 recognized source files, 34 KB.
- C (language) — detected in the code
- C++ (language) — detected in the code
- JavaScript (language) — detected in the code
- Python (language) — detected in the code
- Swift (language) — detected in the code
- Firebase (technology) — claimed on Devpost, not found in the code
- MongoDB (technology) — claimed on Devpost, not found in the code
- Node.js (technology) — claimed on Devpost, not found in the code
- PyTorch (technology) — claimed on Devpost, not found in the code
- TensorFlow (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (53 of 53)

```
.DS_Store
applewatch/GoogleService-Info.plist
applewatch/TreeHacksAppleWatchApp2 Watch App/Assets.xcassets/AccentColor.colorset/Contents.json
applewatch/TreeHacksAppleWatchApp2 Watch App/Assets.xcassets/AppIcon.appiconset/Contents.json
applewatch/TreeHacksAppleWatchApp2 Watch App/Assets.xcassets/Contents.json
applewatch/TreeHacksAppleWatchApp2 Watch App/ContentView.swift
applewatch/TreeHacksAppleWatchApp2 Watch App/HeartRateManager.swift
applewatch/TreeHacksAppleWatchApp2 Watch App/HeartRateReading.swift
applewatch/TreeHacksAppleWatchApp2 Watch App/Preview Content/Preview Assets.xcassets/Contents.json
applewatch/TreeHacksAppleWatchApp2 Watch App/TreeHacksAppleWatchApp2 Watch App.entitlements
applewatch/TreeHacksAppleWatchApp2 Watch App/TreeHacksAppleWatchApp2App.swift
applewatch/TreeHacksAppleWatchApp2.xcodeproj/project.pbxproj
applewatch/TreeHacksAppleWatchApp2.xcodeproj/project.xcworkspace/contents.xcworkspacedata
applewatch/TreeHacksAppleWatchApp2.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
applewatch/TreeHacksAppleWatchApp2.xcodeproj/project.xcworkspace/xcuserdata/charlottechang.xcuserdatad/UserInterfaceState.xcuserstate
applewatch/TreeHacksAppleWatchApp2.xcodeproj/xcuserdata/charlottechang.xcuserdatad/xcschemes/xcschememanagement.plist
applewatch/TreeHacksAppleWatchApp2/.DS_Store
applewatch/TreeHacksAppleWatchApp2/Assets.xcassets/AccentColor.colorset/Contents.json
applewatch/TreeHacksAppleWatchApp2/Assets.xcassets/AppIcon.appiconset/Contents.json
applewatch/TreeHacksAppleWatchApp2/Assets.xcassets/Contents.json
applewatch/TreeHacksAppleWatchApp2/ContentView.swift
applewatch/TreeHacksAppleWatchApp2/Info.plist
applewatch/TreeHacksAppleWatchApp2/Preview Content/Preview Assets.xcassets/Contents.json
applewatch/TreeHacksAppleWatchApp2/TreeHacksAppleWatchApp2.entitlements
applewatch/TreeHacksAppleWatchApp2/TreeHacksAppleWatchApp2App.swift
data/cardiac_risk_predictions.csv
data/cardiac_risk_predictions.py
data/classified_ppg_results.csv
data/data_processing.py
data/deepML-classifyPPG.py
data/final_ppg_dataset.csv
data/interpret_unclassified_data.py
data/merge.py
data/metadata.csv
data/ppg_extracted_features.csv
demo/demo.ino
main/.DS_Store
main/fsm.h
main/fsm.ino
main/heart_rate.cpp
main/heart_rate.h
main/main.ino
main/servo_control.cpp
main/servo_control.h
main/terraapi/client.h
main/terraapi/server.js
main/vibration_control.cpp
main/vibration_control.h
models/ML-classifyPPG.py
models/MLpredictAfib.py
models/MLpredictIrr.py
models/MLpredictReg.py
README.md
```

### Dependencies

No dependency index available.

### Recent commits (newest first)

- Update README.md
- feat: done?
- Add files via upload
- Add files via upload
- Merge pull request #1 from fukaikeda/ML-models
- Add files via upload
- feat: new organization structure
- Add files via upload
- Uploading Swift Project
- Add files via upload
- Update fsm.ino
- Add files via upload

## Key source files (fetched from GitHub, selected and truncated for size)

### main/terraapi/server.js

```javascript
const { MongoClient } = require("mongodb");

const uri = "mongodb+srv://ctchang:lJ8XJpRv0p9EqM7b@treehacks25.8m1qt.mongodb.net/?retryWrites=true&w=majority&appName=TreeHacks25";
const client = new MongoClient(uri);

async function run() {
    try {
        await client.connect();
        console.log("Connected to MongoDB");

        const db = client.db("TreeHacks25");
        const collection = db.collection("activity");

        // Fetch all heart rate data
        const heartRateData = await collection.find(
            {}, 
            { projection: { "heart_rate_data.detailed.hr_samples": 1, _id: 0 } }
        ).toArray();

        // Print all data in a readable format
        console.log(JSON.stringify(heartRateData, null, 2)); // Pretty-print with indentation
    } finally {
        await client.close();
    }
}

run().catch(console.dir);


```

### main/servo_control.h

```c
#ifndef SERVO_CONTROL_H
#define SERVO_CONTROL_H

void servoSetup();
void moveServoShort();
void moveServoLong();

#endif

```

### main/vibration_control.h

```c
#ifndef VIBRATION_CONTROL_H
#define VIBRATION_CONTROL_H

void vibrationSetup();
void activateVibrationShort();
void activateVibrationLong();
void stopVibration();

#endif


```

### main/fsm.h

```c
#ifndef FSM_H
#define FSM_H

enum State {
    IDLE,
    SHORT,
    LONG,
    RELEASE
};

extern State currentState;  // Global variable to track state

void handleFSM();  // Function to handle state transitions

#endif
```

### main/servo_control.cpp

```c++
#include "servo_control.h"
#include <Servo.h>
#include <Arduino.h>

Servo myservo;  
const int servoPin = 9;

void servoSetup() {
    myservo.attach(servoPin);
}

void moveServoShort() {
    for (int pos = 0; pos <= 180; pos += 1) {
        myservo.write(pos);
        delay(15);
    }
    for (int pos = 180; pos >= 0; pos -= 1) {
        myservo.write(pos);
        delay(15);
    }
}

void moveServoLong() {
    for (int pos = 0; pos <= 180; pos += 1) {
        myservo.write(pos);
        delay(10);
    }
    for (int pos = 180; pos >= 0; pos -= 1) {
        myservo.write(pos);
        delay(10);
    }
}

```

### main/vibration_control.cpp

```c++
#include "vibration_control.h"
#include <Arduino.h>

#define MOTOR1_PIN 6
#define MOTOR2_PIN 7
#define PELTIER_RELAY_PIN 8

void vibrationSetup() {
    pinMode(MOTOR1_PIN, OUTPUT);
    pinMode(MOTOR2_PIN, OUTPUT);
    pinMode(PELTIER_RELAY_PIN, OUTPUT);
}

void activateVibrationShort() {
    Serial.println("Vibration: Short mode");
    analogWrite(MOTOR1_PIN, 255);
    analogWrite(MOTOR2_PIN, 255);
    digitalWrite(PELTIER_RELAY_PIN, HIGH);
    delay(1000);
    analogWrite(MOTOR1_PIN, 0);
    analogWrite(MOTOR2_PIN, 0);
    digitalWrite(PELTIER_RELAY_PIN, LOW);
}

void activateVibrationLong() {
    Serial.println("Vibration: Long mode");
    digitalWrite(PELTIER_RELAY_PIN, HIGH);
    
    for (int i = 0; i <= 255; i += 5) {
        analogWrite(MOTOR1_PIN, i);
        analogWrite(MOTOR2_PIN, i);
        delay(50);
    }
    delay(2000);
    for (int i = 255; i >= 0; i -= 5) {
        analogWrite(MOTOR1_PIN, i);
        analogWrite(MOTOR2_PIN, i);
        delay(50);
    }
    digitalWrite(PELTIER_RELAY_PIN, LOW);
}

void stopVibration() {
    Serial.println("Stopping vibration...");
    analogWrite(MOTOR1_PIN, 0);
    analogWrite(MOTOR2_PIN, 0);
    digitalWrite(PELTIER_RELAY_PIN, LOW);
}

```

### data/merge.py

```python
import pandas as pd

# Load PPG extracted features
ppg_features = pd.read_csv("ppg_extracted_features.csv")

#### PREPROCESS EXTRACTED DATA ####
# Step 1: Identify and remove rows with all null or corrupted values
ppg_features_cleaned = ppg_features.dropna(how='all')  # Drops rows where ALL values are NaN

# Step 2: Drop rows with too many missing values 
threshold = int(ppg_features.shape[1] * 0.69) 
ppg_features_cleaned = ppg_features_cleaned.dropna(thresh=threshold)

# Step 3: Fill remaining missing values with median (EXCLUDING the filename column)
numeric_cols = ppg_features_cleaned.select_dtypes(include=['number']).columns  # Only numeric columns
ppg_features_cleaned[numeric_cols] = ppg_features_cleaned[numeric_cols].fillna(ppg_features_cleaned[numeric_cols].median())

# Step 4: Ensure correct data types (convert all but `filename` to float)
for col in numeric_cols:
    ppg_features_cleaned[col] = pd.to_numeric(ppg_features_cleaned[col], errors='coerce')

# Display summary
#print("Cleaned dataset shape:", ppg_features_cleaned.shape)
#print(ppg_features_cleaned.head())

# Load Metadata
metadata = pd.read_csv("metadata.csv")

# Merge on the 'filename' column
merged_df = ppg_features_cleaned.merge(metadata, on="filename", how="inner")

# Save merged dataset
merged_df.to_csv("final_ppg_dataset.csv", index=False)

# Display summary
print("Merged dataset shape:", merged_df.shape)
print(merged_df.head())
```

### models/MLpredictAfib.py

```python
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.multioutput import MultiOutputRegressor
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_squared_error
import joblib

# Read the CSV file into a DataFrame
df = pd.read_csv('final_ppg_dataset.csv')

# Filter the DataFrame to only include rows where the 'label' column is 0
df_afib = df[df['label'] == 2]

# Extract features and targets:
#   - Features: first 13 columns (e.g., bpm, ibi, sdnn, etc.)
#   - Targets: last 14 columns (from 'sinus' to 'extrasystoles_trig_episode')
# Note: Adjust column indices if necessary.
X = df_afib.iloc[:, :13]
Y = df_afib.iloc[:, 15:29]

# Replace inf values with NaN
X.replace([np.inf, -np.inf], np.nan, inplace=True)

# Option 1: Drop rows with NaN values
X_clean = X.dropna()

# If you drop rows from X, make sure to drop corresponding rows from Y:
Y_clean = Y.loc[X_clean.index]

# Split data into training and testing sets
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.2, random_state=42)

# Use a MultiOutputRegressor with a RandomForestRegressor as the base estimator
model = MultiOutputRegressor(RandomForestRegressor(random_state=42))
model.fit(X_train, Y_train)

# Make predictions on the test set
Y_pred = model.predict(X_test)

# Evaluate the model using Mean Squared Error
mse = mean_squared_error(Y_test, Y_pred)
print("Mean Squared Error:", mse)

joblib.dump(model, 'afib_model.pkl')
```

### models/MLpredictReg.py

```python
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.multioutput import MultiOutputRegressor
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_squared_error
import joblib

# Read the CSV file into a DataFrame
df = pd.read_csv('final_ppg_dataset.csv')

# Filter the DataFrame to only include rows where the 'label' column is 0
df_regular = df[df['label'] == 0]

# Extract features and targets:
#   - Features: first 13 columns (e.g., bpm, ibi, sdnn, etc.)
#   - Targets: last 14 columns (from 'sinus' to 'extrasystoles_trig_episode')
# Note: Adjust column indices if necessary.
X = df_regular.iloc[:, :13]
Y = df_regular.iloc[:, 15:29]

# Replace inf values with NaN
X.replace([np.inf, -np.inf], np.nan, inplace=True)

# Option 1: Drop rows with NaN values
X_clean = X.dropna()

# If you drop rows from X, make sure to drop corresponding rows from Y:
Y_clean = Y.loc[X_clean.index]

# Split data into training and testing sets
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.2, random_state=42)

# Use a MultiOutputRegressor with a RandomForestRegressor as the base estimator
model = MultiOutputRegressor(RandomForestRegressor(random_state=42))
model.fit(X_train, Y_train)

# Make predictions on the test set
Y_pred = model.predict(X_test)

# Evaluate the model using Mean Squared Error
#mse = mean_squared_error(Y_test, Y_pred)
#print("Mean Squared Error:", mse)

joblib.dump(model, 'reg_model.pkl')
```

### models/MLpredictIrr.py

```python
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.multioutput import MultiOutputRegressor
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_squared_error
import joblib

# Read the CSV file into a DataFrame
df = pd.read_csv('final_ppg_dataset.csv')

# Filter the DataFrame to only include rows where the 'label' column is 0
df_irregular = df[df['label'] == 1]

# Extract features and targets:
#   - Features: first 13 columns (e.g., bpm, ibi, sdnn, etc.)
#   - Targets: last 14 columns (from 'sinus' to 'extrasystoles_trig_episode')
# Note: Adjust column indices if necessary.
X = df_irregular.iloc[:, :13]
Y = df_irregular.iloc[:, 15:29]

# Replace inf values with NaN
X.replace([np.inf, -np.inf], np.nan, inplace=True)

# Option 1: Drop rows with NaN values
X_clean = X.dropna()

# If you drop rows from X, make sure to drop corresponding rows from Y:
Y_clean = Y.loc[X_clean.index]

# Split data into training and testing sets
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.2, random_state=42)

# Use a MultiOutputRegressor with a RandomForestRegressor as the base estimator
model = MultiOutputRegressor(RandomForestRegressor(random_state=42))
model.fit(X_train, Y_train)

# Make predictions on the test set
Y_pred = model.predict(X_test)

# Evaluate the model using Mean Squared Error
#mse = mean_squared_error(Y_test, Y_pred)
#print("Mean Squared Error:", mse)

joblib.dump(model, 'irreg_model.pkl')
```

[11 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]