final-project-r-e-f-r-e-s-h

Review Assignment Due Date

final-project-skeleton

* Team Name: REFRESH - Racing Equipment for Fluid Regulation and Enhanced Sweat Handling
* Team Members: Chirag Satapathy, Sanskriti Binani, Megha Mistry
* Github Repository URL: https://github.com/upenn-embedded/final-project-r-e-f-r-e-s-h
* Github Pages Website URL: https://upenn-embedded.github.io/final-project-r-e-f-r-e-s-h/
* Description of hardware: Laptop, GSR Sensor, Pulse-Oximeter Sensor, Cooling DC Fan, ESP32, Logic Level Shifter

Final Project Report

Don’t forget to make the GitHub pages public website! If you’ve never made a Github pages website before, you can follow this webpage (though, substitute your final project repository for the Github username one in the quickstart guide): https://docs.github.com/en/pages/quickstart

1. Video

Video Timestamps:

00:00 - Disclaimer
0:04 - Video Starts
0:12 - Thinking
0:25 - News on F1
0:33 - About the Idea (F1 cooling Glove)
1:49 - Building the cooling glove (Megha, Sanskriti, Chirag - Team 20 timelapse)
2:27 - Version 1 and 2 of our glove
2:41 - GSR Data collection
2:48 - Brief about our prototype
4:25 - Demo of POC
7:05 - End

Link - https://www.youtube.com/embed/ZUF7TIl2jmc

Three Topics Covered

  1. Timers, PWM

  2. ADC

  3. Communication (I2C, UART, WiFi)

  4. Special Topics - Machine Learning, Signal Connditioning, IoT

Input Devices

  1. GSR Sensor

  2. MAXDESFE117 Pulse Oximeter Sensor

Output Devices

  1. DC Cooling Fan

  2. Blynk GUI

2. Images

3. Results

Fan Control Prototypes

Two distinct fan control prototypes were developed and tested to optimize the temperature regulation system integrated into the F1 cooling glove. These prototypes used different approaches to achieve precise and efficient fan speed control:

1. MOSFET-Based Control:

o This design employed a dual-MOSFET circuit to independently regulate the speed of two fans using PWM signals generated by the ATmega328PB microcontroller. o MOSFETs acted as high-speed electronic switches, allowing precise modulation of fan speed based on hydration thresholds. However, during testing, the MOSFETs exhibited significant heating issues when running for extended durations. This heating posed risks to both hardware integrity and the glove’s cooling performance, as the additional heat negated some of the cooling benefits. o The heating issue was identified as a result of incorrect connections between the MOSFET pins, leading to a short circuit during fan operation. This problem was resolved by rectifying the connections and redesigning the circuit. A mini-PCB was subsequently developed, incorporating two fans controlled by two MOSFETs. While this revised design effectively eliminated the heating issue, its relatively bulky form factor rendered it less suitable for integration into the glove, as it could compromise comfort for the user.

2. Direct PWM Connector:

o The second prototype simplified the design by using a fan with a built-in PWM control pin, directly connected to the microcontroller. This eliminated the need for MOSFETs to act as intermediary components. o By directly driving the PWM-enabled fan, this approach reduced heat dissipation and improved overall reliability. The built-in PWM control ensured precise speed adjustments based on hydration thresholds. o After thorough testing, the direct PWM connector design was chosen as the final implementation due to its efficiency, reliability, and alignment with the project’s objectives of maintaining optimal cooling performance.

Sensor Integration

The glove’s ability to monitor and respond to the wearer’s physiological state depended heavily on integrating accurate and reliable sensors:

1. MAXREFDES117 Pulse Oximeter Sensor:

o The MAXREFDES117 was incorporated to measure two critical physiological metrics: blood oxygen saturation (SpO₂) and heart rate. These metrics were crucial for assessing the driver’s physical state during races. o The sensor communicated with the ATmega328PB microcontroller via the I2C protocol. Initial testing revealed issues with static SpO₂ readings, attributed to either hardware faults or noise in the I2C lines. o Debugging with a logic analyzer identified and resolved the communication issues. A replacement sensor module ensured dynamic and accurate readings, achieving a precision of ±3%. This level of accuracy was sufficient for real-time monitoring in high-performance scenarios.

2. Grove GSR Sensor:

o The Grove GSR sensor was used to monitor skin resistance, serving as a proxy for hydration levels. Hydration affects skin conductivity, and the GSR sensor’s analog output provided data for hydration analysis. o The sensor’s output was processed through the microcontroller’s ADC. Hydration thresholds were experimentally validated and mapped to specific hydration states, such as “Hydrated,” “Medium Hydration,” and “Dehydrated.” o Extensive calibration ensured the sensor provided consistent and reliable readings, forming the basis for the glove’s cooling adjustments.

Data Transmission

Reliable data transmission between the glove and the monitoring system was essential for real-time updates and feedback:

1. Transition from LoRa to ESP32 and Blynk:

o Initially, LoRa communication modules (RYLR896) were used for long-range data transmission. While these modules were effective in static conditions, field tests revealed intermittent data losses in dynamic environments, such as during movement or vibration. o To overcome these issues, the project transitioned to using ESP32 for data communication. ESP32 offered Wi-Fi connectivity and integrated seamlessly with the Blynk platform, which provided a user-friendly interface for mobile monitoring. o The redesigned system transmitted GSR and SpO₂ data to the Blynk app every five seconds, ensuring real-time monitoring without significant latency. The Blynk app also allowed users to visualize physiological data through intuitive dashboards.

Machine Learning Integration

• A Random Tree Classifier was implemented to analyze GSR data and categorize hydration levels. This machine learning model divided hydration states into three categories: “Hydrated,” “Medium Hydration,” and “Dehydrated.” • The classifier demonstrated 100% accuracy on the test dataset, validating its effectiveness in identifying hydration levels. By linking hydration states to fan speed adjustments, the ML model enabled dynamic and automated cooling responses, ensuring the glove provided optimal comfort and performance in real-time.

Performance Metrics

1. Cooling System:

o The PWM-controlled fan system dynamically adjusted speeds based on hydration thresholds derived from GSR readings. Fans responded promptly to changes in hydration levels, effectively regulating temperature and ensuring the wearer’s comfort.

2. Data Transmission:

o ESP32 successfully transmitted GSR and SpO₂ data to the Blynk app with consistent updates every five seconds. The real-time communication ensured that physiological data was readily available for monitoring and analysis.

3. Sensor Accuracy:

o The MAXREFDES117 pulse oximeter provided SpO₂ and heart rate readings with a precision of ±3%, sufficient for real-time health monitoring. The Grove GSR sensor reliably detected hydration-related skin resistance changes, enabling accurate hydration analysis.

Challenges and Refinements

1. Thermal Management:

o The MOSFET-based fan control design was revised due to its bulkiness, and a direct PWM-controlled fan system was implemented to address this issue.

2. Sensor Calibration:

o Calibration efforts for the GSR sensor ensured its data aligned with real-world hydration levels, enabling seamless integration with the machine learning model.

3. Communication Issues:

o The reliability issues with LoRa modules were mitigated by adopting ESP32, which offered consistent and user-friendly data transmission.

By addressing these challenges and implementing refinements, the F1 cooling glove project achieved a robust, efficient, and reliable system tailored for real-time physiological monitoring and temperature regulation.

3.1 Software Requirements Specification (SRS) Results

Requirement No. Test Method Result
SRS 01 The device successfully monitored and logged GSR values, updating hydration levels every second using predefined thresholds. Pass
SRS 02 The MAXREFDES117 sensor measured SpO₂ and heart rate with a precision of ±3%, updating readings every second. Pass
SRS 03 The device successfully transmitted sensor data via LoRa to a remote monitoring system at intervals of 5 seconds. LoRa communication showed minimal latency. Blocked
SRS 04 Utilizing ESP32 for backup communication and sends data to Blynk over Wi-Fi. Pass
SRS 05 The cooling subsystem dynamically adjusted fan speed using PWM control based on hydration thresholds from GSR sensor readings. Pass
SRS 06 Machine learning algorithms, specifically Random Tree Classifier, were used to analyze GSR data and classify hydration levels, controlling the cooling system. Pass
SRS 07 GSR and pulse oximeter data is successfully transferred from the ATmega328PB to the ESP32, which then sends the data to Blynk, ensuring real-time monitoring and control. Pass
SRS 08 Both versions of the cooling system (V1 with a two-pin fan controlled by MOSFET and V2 with a four-pin fan controlled by PWM) were tested and evaluated for their effectiveness in cooling based on hydration levels. Pass

From my perspective, the results show that most of the device’s core features were successful, especially in terms of health monitoring, cooling control, and data analysis. The device effectively tracked hydration levels, heart rate, and SpO₂, and the cooling system adjusted based on these readings. The machine learning algorithms used to classify hydration levels and control the cooling system also performed as expected.

3.2 Hardware Requirements Specification (HRS) Results

Requirement No. Test Method Result
HRS 01 The project uses the ATmega328PB microcontroller as the central unit, successfully tested with basic I/O operations. Pass
HRS 02 The GSR sensor measures skin resistance and hydration levels, with data successfully read by the ADC of the microcontroller. Pass
HRS 03 The pulse oximeter (MAXDESREF117) measures SpO₂ and heart rate with the I2C protocol, data accurately transmitted to the MCU. Pass
HRS 04 LoRa modules (RYLR896) enable data transmission over long distances, interfacing with the MCU via UART. Blocked
HRS 05 The cooling subsystem consists of PWM-controlled fans, adjusting speed dynamically based on GSR sensor hydration data. Pass
HRS 06 If we fail to achieve LoRa transmission, we will shift the system to use ESP32 for communication, sending data to Blynk over Wi-Fi. Pass
HRS 07 Communication between ATmega328PB and ESP32 is successfully established, with data (GSR and pulse oximeter) being transferred to Blynk over Wi-Fi. Pass
HRS 08 Two versions of the cooling system with fans were tested (V1 - without PWM, V2 - With PWM). Pass

The results indicate that most of the key components of the project were successful. The ATmega328PB microcontroller performed well with basic I/O operations, and the GSR sensor successfully measured skin resistance and hydration levels, with accurate data readings. The pulse oximeter also functioned as expected, transmitting SpO₂ and heart rate data to the MCU. The cooling system, both with and without PWM control, was tested successfully, and fan speed dynamically adjusted based on hydration data. However, there were challenges with the communication side of things, as the LoRa transmission failed, and the backup system using the ESP32 for communication was implemennted. Despite these setbacks, communication with the ESP32 over Wi-Fi to send data to Blynk was successful. Overall, while there are communication issues to address, the core functionality of the device works amazing.

4. Conclusion

Team

Meet the Creators


Chirag Satapathy

Email Instagram LinkedIn

Sanskriti Binani

Email Instagram LinkedIn

Megha Mistry

Email LinkedIn

Acknowledgments

We would like to express our sincere gratitude to Professor Nicholas McGill-Gardner for his invaluable expertise, mentorship, and encouragement throughout the course. His guidance has been instrumental in our learning journey. Our thanks also go to Account Manager Yadnik Bendale for his assistance and unwavering support in managing our course-related needs, ensuring everything ran smoothly. We are equally grateful to Course TAs Vishnu and Maryam for their dedication and helpfulness, which played a significant role in providing us with a smooth and enriching learning experience.

MVP Demo

Show a system block diagram & explain the hardware implementation.

alt text

The core of this device is a custom processing board that serves as the central hub, connecting and coordinating the various components. The key sensors integrated into this system are:

The ATMega328PB board interfaces with these sensors using I2C and ADC connections, allowing it to gather critical biometric data from the driver’s hand. It then utilizes on-board timers, ADC, PWM, and communication protocols to handle and process this sensor information.

Explain your firmware implementation, including application logic and critical drivers you’ve written.

Application Logic:

1. Sensor Data Acquisition:

2. Cooling System Control:

The cooling system consists of PWM-controlled fans. The fan speed is dynamically adjusted based on the GSR data. If the skin resistance is low (indicating high sweat levels), the fan speed increases, providing cooling. The fan speed is controlled by varying the PWM duty cycle, which is managed by the microcontroller’s Timer1 module.

3. Data Transmission:

The data from the sensors is transmitted to a remote system using LoRa modules (RYLR896). We also are implementing the ESP32 as a backup to send data to the Blynk platform over Wi-Fi. The system attempts to send data every 5 seconds.

Critical Drivers:

1. ADC Driver (GSR Sensor): {GSR Sensor Test Code}

The GSR sensor measures skin resistance, which correlates with hydration levels. This sensor provides an analog voltage signal, which the microcontroller reads using its Analog-to-Digital Converter (ADC). The driver configures the ADC to read values from the GSR sensor, processes them, and calculates hydration levels.

// ADC initialization and read function
void ADC_Init() {
    ADMUX = (1<<REFS0);  // Set Vref=AVcc (assuming 5V reference voltage)
    ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0); // Enable ADC and set prescaler to 128
}

// Function to read ADC value from a given channel
uint16_t ADC_Read(uint8_t channel) {
    ADMUX = (ADMUX & 0xF0) | (channel & 0x0F);  // Select ADC channel (bits 0-3)
    ADCSRA |= (1<<ADSC);  // Start conversion
    while (ADCSRA & (1<<ADSC));  // Wait for conversion to complete
    return ADC;  // Return the ADC result
}

Explanation:

2. I2C (TWI) Driver (Pulse Oximeter - MAXDESREF117): {MAXDESREF117 Sensor Test Code}

The I2C (Inter-Integrated Circuit) protocol is used to communicate with the MAX30102 sensor. The microcontroller acts as the master device, and the MAX30102 acts as the slave device.

void TWI_init(void) {
    TWSR0 = 0x00; // Prescaler = 1
    TWBR0 = ((F_CPU / SCL_CLOCK) - 16) / 2; // Set SCL frequency
    TWCR0 = (1 << TWEN); // Enable TWI
}

Explanation:

The TWI_start() function sends the START condition on the I2C bus. The START condition signals the beginning of communication.

uint8_t TWI_start(void) {
    TWCR0 = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); // Send START
    while (!(TWCR0 & (1 << TWINT)));                  // Wait for completion
    if (((TWSR0 & 0xF8) != TW_START) && ((TWSR0 & 0xF8) != TW_REP_START)) return 1;    // Check status
    return 0;
}

Explanation:

The TWI_stop() function sends the STOP condition, marking the end of communication.

void TWI_stop(void) {
    TWCR0 = (1 << TWINT) | (1 << TWSTO) | (1 << TWEN); // Send STOP
    while (TWCR0 & (1 << TWSTO));                     // Wait for completion
}

Explanation:

The TWI_write() function writes a byte of data to the slave device (MAX30102).

uint8_t TWI_write(uint8_t data) {
    TWDR0 = data; // Load data
    TWCR0 = (1 << TWINT) | (1 << TWEN); // Start transmission
    while (!(TWCR0 & (1 << TWINT)));    // Wait for completion
    if ((TWSR0 & 0xF8) != TW_MT_DATA_ACK && (TWSR0 & 0xF8) != TW_MT_SLA_ACK && (TWSR0 & 0xF8) != TW_MR_SLA_ACK)
        return 1; // Check for ACK
    return 0;
}

Explanation:

The TWI_read_ack() function reads a byte of data from the slave device (MAX30102) and sends an ACK (Acknowledgment) to continue reading, while TWI_read_nack() reads data and sends a NACK (No Acknowledgment) to indicate the end of the read operation.

uint8_t TWI_read_ack(void) {
    TWCR0 = (1 << TWINT) | (1 << TWEN) | (1 << TWEA); // Enable ACK
    while (!(TWCR0 & (1 << TWINT)));                 // Wait for completion
    return TWDR0;
}

uint8_t TWI_read_nack(void) {
    TWCR0 = (1 << TWINT) | (1 << TWEN); // Disable ACK
    while (!(TWCR0 & (1 << TWINT)));   // Wait for completion
    return TWDR0;
}

Explanation:

The readFIFOData() function reads data from the FIFO_DATA register of the MAXDESREF117, which stores the raw Red and IR sensor values. The function reads six bytes of data (three for Red and three for IR) and combines them into 32-bit values.

max30102_data_t readFIFOData(void) {
    max30102_data_t data = {0, 0};
    uint8_t temp[6]; // Temporary buffer for 6 bytes (3 bytes each for RED and IR)
    
    // Start reading from FIFO_DATA register
    TWI_start();
    TWI_write(MAX30102_WRITE_ADDR);
    TWI_write(0x07);  // FIFO_DATA register
    
    // Restart for reading
    TWI_start();
    TWI_write(MAX30102_READ_ADDR);
    
    // Read all 6 bytes
    temp[0] = TWI_read_ack(); // RED[23:16]
    temp[1] = TWI_read_ack(); // RED[15:8]
    temp[2] = TWI_read_ack(); // RED[7:0]
    temp[3] = TWI_read_ack(); // IR[23:16]
    temp[4] = TWI_read_ack(); // IR[15:8]
    temp[5] = TWI_read_nack(); // IR[7:0]
    
    TWI_stop();
    
    // Combine bytes into 32-bit values
    data.red = ((uint32_t)temp[0] << 16) | ((uint32_t)temp[1] << 8) | temp[2];
    data.ir = ((uint32_t)temp[3] << 16) | ((uint32_t)temp[4] << 8) | temp[5];
    
    return data;
}

Explanation:

3. PWM Driver (Cooling System)

The cooling system uses PWM-controlled fans, and the fan speed is dynamically adjusted based on hydration data derived from the GSR sensor. The Timer1 is used to generate the PWM signal, and the duty cycle is adjusted based on the hydration level.

// PWM initialization and control for fan speed
void PWM_Init() {
    DDRB |= (1 << PB1);  // Set PB1 (OC1A) as output for PWM signal
    TCCR1A |= (1 << WGM11); // Set Fast PWM mode
    TCCR1B |= (1 << WGM13) | (1 << WGM12);  // Fast PWM mode with ICR1 as top value
    TCCR1A |= (1 << COM1A1); // Non-inverting PWM on OC1A
    ICR1 = 639;  // Set the PWM frequency to approximately 1kHz
}

void Set_Fan_Speed(uint8_t speed) {
    OCR1A = (ICR1 + 1) * speed / 100 - 1;  // Set the PWM duty cycle based on the speed percentage
}

Explanation:

  1. UART Driver - For all sensors to send data {LORA Test Code with UART}

The LoRa module (RYLR896) is used for long-range data transmission. The UART driver enables communication between the microcontroller and LoRa, sending sensor data to a remote system.

// UART initialization and LoRa communication
void UART0_init(unsigned int ubrr) {
    // Set baud rate
    UBRR0H = (unsigned char)(ubrr>>8);
    UBRR0L = (unsigned char)ubrr;
    
    // Enable transmitter and receiver
    UCSR0B = (1<<TXEN0) | (1<<RXEN0);
    
    // Set frame format: 8data, 1stop bit
    UCSR0C = (1<<USBS0)|(3<<UCSZ00);
}

void UART0_transmit(unsigned char data) {
    // Wait for empty transmit buffer
    while (!(UCSR0A & (1<<UDRE0)));
    
    // Put data into buffer, sends the data
    UDR0 = data;
}

Explanation:

The firmware includes critical drivers for ADC, I2C, PWM, and UART communication, ensuring that the GSR sensor, pulse oximeter, cooling system, and LoRa module operate seamlessly. Each driver is modular, enabling independent testing and easy integration into the overall system. The application logic processes sensor data, controls the cooling system, and handles communication for real-time monitoring.

Have you achieved some or all of your Software Requirements Specification (SRS)?

The project aims to develop firmware for real-time monitoring and management of hydration and health parameters. Data is processed onboard and transmitted wirelessly for remote analysis. The cooling system is dynamically controlled based on hydration data.

Users

The device is targeted at Formula 1 drivers, enabling real-time monitoring and management of hydration and physiological metrics during races.

Definitions and Abbreviations

Functionality

Software Requirements Specification (SRS) Test Results

Requirement No. Test Method Result
SRS 01 The device successfully monitored and logged GSR values, updating hydration levels every second using predefined thresholds. Pass
SRS 02 The MAXREFDES117 sensor measured SpO₂ and heart rate with a precision of ±3%, updating readings every second. Pass
SRS 03 The device successfully transmitted sensor data via LoRa to a remote monitoring system at intervals of 5 seconds. LoRa communication showed minimal latency. Ongoing
SRS 04 Utilizing ESP32 for backup communication and sends data to Blynk over Wi-Fi. Ongoing
SRS 05 The cooling subsystem dynamically adjusted fan speed using PWM control based on hydration thresholds from GSR sensor readings. Pass
SRS 06 Machine learning algorithms, specifically Random Tree Classifier, were used to analyze GSR data and classify hydration levels, controlling the cooling system. Pass

Show how you collected data and the outcomes.

SRS 01 (Hydration Monitoring via GSR)

SRS 02 (SpO₂ and Heart Rate Measurement)

SRS 03 (Data Transmission via LoRa)

SRS 04 (Backup Communication via ESP32 and Blynk)

SRS 05 (Cooling Subsystem with PWM Control)

SRS 06 (Machine Learning for Hydration Classification)

Have you achieved some or all of your Hardware Requirements Specification (HRS)?

The project integrates an ATmega328PB microcontroller with sensors and peripherals to monitor hydration levels, physiological parameters, and manage cooling for Formula 1 drivers. Key components include sensors for GSR (Galvanic Skin Response) and SpO₂, a cooling fan subsystem, and LoRa modules for long-range data communication.

Definitions and Abbreviations

Functionality

Hardware Requirements Specification (HRS) Test Results

Requirement No. Test Method Result
HRS 01 The project uses the ATmega328PB microcontroller as the central unit, successfully tested with basic I/O operations. Pass
HRS 02 The GSR sensor measures skin resistance and hydration levels, with data successfully read by the ADC of the microcontroller. Pass
HRS 03 The pulse oximeter (MAXDESREF117) measures SpO₂ and heart rate with the I2C protocol, data accurately transmitted to the MCU. Pass
HRS 04 LoRa modules (RYLR896) enable data transmission over long distances, interfacing with the MCU via UART. Ongoing
HRS 05 The cooling subsystem consists of PWM-controlled fans, adjusting speed dynamically based on GSR sensor hydration data. Pass
HRS 06 If we fail to achieve LoRa transmission, we will shift the system to use ESP32 for communication, sending data to Blynk over Wi-Fi. Ongoing

Show how you collected data and the outcomes.

HRS 01 (ATmega328PB Microcontroller)

HRS 02 (GSR Sensor for Hydration Measurement)

HRS 03 (Pulse Oximeter - MAXDESREF117)

HRS 04 (LoRa Modules for Data Transmission)

HRS 05 (Cooling Subsystem with PWM)

HRS 06 (Backup Communication with ESP32 and Blynk)

Show off the remaining elements that will make your project whole: mechanical casework, supporting graphical user interface (GUI), web portal, etc.

The Cooling Fans are attached to the Glove as seen in the first two images. The MOSFET switch (Image 3) is connected to the cooling system to make sure it adjusts the FAN speed using PWM through the GSR Data. The final implementation will have all the remaning sensors and modules attached to the glove - the finished F1 Glove - REFRESH !!

The final hardware setup implemented is attached below with the Blynk GUI!!

Demo your device.

We have attached a simple Demo of our device here. It shows the working of MAXDESREF117 pulse oximeter sensor, GSR Sensor for skin resistance values and these values being exported to Blynk App. For the final Demo, the hardware setup will be implemented to the gloves with all sensors callibrated to their best accuracy. Also, the GSR values from Blynk will be passed through the ML model to deduce the hydration level of the individual - in our case the F1-racer.

https://drive.google.com/file/d/1uibdiCdPrKdcX2FJHqtnBflvKcRHCYEj/view?usp=sharing

What is the riskiest part remaining of your project?

How do you plan to de-risk this?

Incremental Testing and Validation:

Rather than attempting to integrate all components at once, we will test each component separately before integrating them:

Modular Approach:

We will test our device in three main phases.

Debugging and Optimization:

During integration, debugging will be a continuous process. Software and hardware issues will be resolved iteratively, and any performance bottlenecks will be addressed. For example, if there is a delay in fan speed control or inaccurate hydration classification, the code and hardware connections will be optimized immediately.

What questions or help do you need from the teaching team?

We have encountered issues with data transmission between LoRa1 and LoRa2. Despite the correct configuration, communication fails always. Could you provide guidance on making the LoRa communication work in ATmega328PB? As the LoRa works with the Arduino MCU.

Sprint review #2

Current state of project

Last week’s progress

Hydration Level Computation

  1. We tested the GSR sensor to capture skin resistance values under different conditions. The first test involved printing all the raw skin resistance values, which allowed us to observe the sensor’s behavior and the variation in resistance based on the individual’s hydration level and physical state. These values will be further analyzed, and we will categorize them into predefined ranges: Normal, Medium, and High sweat levels. These categories will be determined after testing additional individuals to create a broader dataset. The skin resistance thresholds for each category will be used for calibration and further analysis in the ML model.

Please find the attached code and images below for your reference.

#include <avr/io.h>
#include <util/delay.h>
#include <stdio.h>
#include <stdint.h>

#define F_CPU 16000000UL  // 16 MHz clock frequency
#define BAUD 9600  // Baud rate
#define MYUBRR F_CPU/16/BAUD-1  // UBRR calculation for 9600 baud

#define GSR_CHANNEL 0  // GSR sensor connected to ADC0 (A0)

int sensorValue = 0;
int gsr_average = 0;

// Function prototypes
void USART_Init(unsigned int ubrr);
void USART_Transmit(unsigned char data);
void USART_PrintString(char *str);
void USART_PrintInteger(int value);
void ADC_Init();
uint16_t ADC_Read(uint8_t channel);

// UART Initialization
void USART_Init(unsigned int ubrr) {
    UBRR0H = (unsigned char)(ubrr>>8);
    UBRR0L = (unsigned char)ubrr;
    UCSR0B = (1<<TXEN0)|(1<<RXEN0);  // Enable transmitter and receiver
    UCSR0C = (1<<UCSZ01)|(1<<UCSZ00);  // 8 data bits, 1 stop bit
}

// UART Transmit Character
void USART_Transmit(unsigned char data) {
    while (!(UCSR0A & (1<<UDRE0)));  // Wait for empty transmit buffer
    UDR0 = data;  // Send data
}

// UART Print String
void USART_PrintString(char *str) {
    while (*str) {
        USART_Transmit(*str);
        str++;
    }
}

// UART Print Integer
void USART_PrintInteger(int value) {
    char buffer[16];
    snprintf(buffer, sizeof(buffer), "%d", value);  // Convert integer to string
    USART_PrintString(buffer);  // Send the string to UART
    USART_Transmit('\r');  // Newline
    USART_Transmit('\n');
}

// ADC Initialization
void ADC_Init() {
    ADMUX = (1<<REFS0);  // Set Vref=AVcc (assuming 5V reference voltage)
    ADCSRA = (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);  // Enable ADC and set prescaler to 128
}

// Read ADC Channel
uint16_t ADC_Read(uint8_t channel) {
    ADMUX = (ADMUX & 0xF0) | (channel & 0x0F);  // Select ADC channel
    ADCSRA |= (1<<ADSC);  // Start conversion
    while (ADCSRA & (1<<ADSC));  // Wait for conversion to complete
    return ADC;  // Return ADC value
}

int main(void) {
    USART_Init(MYUBRR);  // Initialize UART
    ADC_Init();  // Initialize ADC
    _delay_ms(100);  // Stabilization delay

    while (1) {
        long sum = 0;
        
        // Average 10 ADC readings
        for (int i = 0; i < 10; i++) {
            sensorValue = ADC_Read(GSR_CHANNEL);  // Read GSR sensor value
            sum += sensorValue;
            _delay_ms(5);  // Small delay to allow ADC to settle
        }
        
        gsr_average = sum / 10;  // Compute average of the readings
        
        // Send the average GSR value to the serial monitor
        USART_PrintInteger(gsr_average);
        
        _delay_ms(1000);  // Delay before the next reading
    }

    return 0;
}

alt text

Cooling Fan Subsystem

  1. The Cooling Fan Subsystem has been successfully tested using a MOSFET. As discussed above, MOSEFT heats up and can cause a problem to the circuitry and it will be a contradiction of our goal to cool.

    Attached below are the codes and picture of the circuit using MOSFET. There is also a video attached showing the fan operating at different speed set according to the code in last week’s sprint review.

Main Code

/* 
 * File:   Fan_control.c
 * Author: Megha31
 *
 * Created on 14 November, 2024, 2:20 PM
 */

#include <stdio.h>
#include <stdlib.h>
#include <avr/io.h>
#ifndef F_CPU
#define F_CPU 16000000UL  // CPU frequency set to 16MHz
#endif
#include <util/delay.h>
#include "uart.h"
void timer1_init(void) {
    // Set PB1 (OC1A) as output
    DDRB |= (1 << PB1);

    // Configure Timer1 for Fast PWM mode
    // WGM13:0 = 1110 for Fast PWM mode with ICR1 as TOP
    TCCR1A |= (1 << WGM11);
    TCCR1B |= (1 << WGM13) | (1 << WGM12);

    // Set COM1A1:0 = 10 for non-inverting PWM on OC1A (PB1)
    TCCR1A |= (1 << COM1A1);
    TCCR1A &= ~(1 << COM1A0);

    // Set prescaler to 1 (no prescaling)
    TCCR1B |= (1 << CS10);

    // Set PWM frequency to about 25kHz
    // F_PWM = F_CPU / (prescaler * (1 + TOP))
    ICR1 = 639;

    // Print timer configuration
    uart_puts("Timer1 initialized\r\n");
//    uart_printf("PWM Frequency: ~25kHz\r\n");
}
void set_fan_speed(uint8_t speed_percent) {
    
    if (speed_percent > 100) {
        speed_percent = 100;
    }

    // Calculate OCR1A value based on speed percentage
    // OCR1A determines duty cycle
    uint16_t ocr_value = (uint16_t)((ICR1 + 1) * speed_percent / 100) - 1;
    OCR1A = ocr_value;

    // Print speed information
    uart_printf("\n=== Fan Speed: %u%% ===\r\n", speed_percent);
    uart_printf("OCR1A Value: %u\r\n", ocr_value);

    // Calculate and print actual duty cycle
    float duty_cycle = ((float)ocr_value + 1) / ((float)ICR1 + 1) * 100;
    uart_printf("Duty Cycle: %.1f%%\r\n", duty_cycle);
}
int main(void) {
    // Initialize UART first
    uart_init();
    uart_puts("\nStarting Fan System\r\n");

    // Initialize Timer1 for PWM
    timer1_init();

    uart_puts("Beginning fan cycle\r\n");

    while (1) {
                // Run at full speed
        uart_puts("\n--- Testing 100% Speed ---\r\n");
        set_fan_speed(100);
        _delay_ms(17000);
        
        // Run at 50% speed
        uart_puts("\n--- Testing 50% Speed ---\r\n");
        set_fan_speed(50);
        _delay_ms(17000);
        
        // Run at 0% speed
        uart_puts("\n--- Testing 0% Speed ---\r\n");
        set_fan_speed(0);
        _delay_ms(17000);

    }

    return 0;
}

uart.c file

/* 
 * File:   uart.c
 * Author: Megha31
 *
 * Created on 16 November, 2024, 12:52 PM
 */

#include "uart.h"
#include <stdarg.h>

static char uart_buffer[UART_BUFFER_SIZE];

void uart_init(void) {
    // Set baud rate
    UBRR0H = (uint8_t)(BAUD_PRESCALER >> 8);
    UBRR0L = (uint8_t)(BAUD_PRESCALER);
    
    // Enable transmitter and receiver
    UCSR0B = (1 << TXEN0) | (1 << RXEN0);
    
    // Set frame format: 8 data bits, 1 stop bit, no parity
    UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
}

void uart_putchar(char c) {
    // Wait for empty transmit buffer
    while (!(UCSR0A & (1 << UDRE0)));
    
    // Put data into buffer, sends data
    UDR0 = c;
}

void uart_puts(const char* str) {
    while (*str) {
        uart_putchar(*str++);
    }
}

void uart_printf(const char* format, ...) {
    va_list args;
    va_start(args, format);
    vsnprintf(uart_buffer, UART_BUFFER_SIZE, format, args);
    uart_puts(uart_buffer);
    va_end(args);
}

uart.h file

/* 
 * File:   uart.h
 * Author: Megha31
 *
 * Created on 14 November, 2024, 6:00 PM
 */


#ifndef UART_H
#define UART_H

#ifndef F_CPU
#define F_CPU 16000000UL
#endif

#define BAUD_RATE 9600
#define BAUD_PRESCALER ((F_CPU / (BAUD_RATE * 16UL)) - 1)

#include <avr/io.h>
#include <stdio.h>

// UART buffer size
#define UART_BUFFER_SIZE 64

// Function declarations
void uart_init(void);
void uart_putchar(char c);
void uart_puts(const char* str);
void uart_printf(const char* format, ...);

#endif

Fan Control Using MOSFET

Video: https://drive.google.com/file/d/1g3qslzX7e6XxaYxJ7Z7_XcgOKAf2ZaF2/view?usp=sharing

Pulse Oximeter Sensor

  1. Over the past week, we established an I2C connection with the MAX pulse oximeter sensor, which initially seemed operational as it successfully transmitted SpO₂ data. However, the values remained static at 100%, indicating a potential issue with the sensor or its configuration. To investigate further, we used a logic analyzer to debug the I2C communication. While the I2C protocol was verified to be functional (validated by testing with a pressure sensor), the MAX pulse oximeter did not provide dynamic or accurate readings.

    Attached to this report are the relevant codes, I2C communication analysis, and images of the debugging process.

MAX30100. code

#include <avr/io.h>
#include <util/delay.h>
#include <stdio.h>
#include <string.h>
#include "i2c.h"
//#include <util/twi.h>

#define F_CPU 16000000UL
#define BAUD 9600
#define MYUBRR ((F_CPU/16/BAUD) - 1)

#define MAX_ADDR 0xAE  // I2C address (0x57 << 1)

// Register addresses
#define REG_INT_STATUS    0x00
#define REG_INT_ENABLE    0x01
#define REG_FIFO_WR_PTR   0x02
#define REG_FIFO_OVF      0x03
#define REG_FIFO_RD_PTR   0x04
#define REG_FIFO_DATA     0x05
#define REG_MODE_CONFIG   0x06
#define REG_SPO2_CONFIG   0x07
#define REG_LED_CONFIG    0x09
#define REG_TEMP_INT      0x16
#define REG_REVISION      0xFE
#define REG_PART_ID       0xFF

// Function prototypes
void uart_init(void);
void uart_transmit(uint8_t data);
void uart_print(const char* str);
void sensor_init(void);
void read_sensor_data(void);

// UART functions
void uart_init(void) {
    UBRR0H = (uint8_t)(MYUBRR >> 8);
    UBRR0L = (uint8_t)MYUBRR;
    UCSR0B = (1 << TXEN0);
    UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
}

void uart_transmit(uint8_t data) {
    while (!(UCSR0A & (1 << UDRE0)));
    UDR0 = data;
}

void uart_print(const char* str) {
    while (*str) {
        uart_transmit(*str++);
    }
}

// Sensor initialization
void sensor_init(void) {
    // Reset the sensor
    i2c_write_reg(MAX_ADDR, REG_MODE_CONFIG, 0x40);
    _delay_ms(100);
    
    // Read revision ID
    uint8_t rev_id;
    i2c_read_reg(MAX_ADDR, REG_REVISION, &rev_id, 1);
    char buf[32];
    sprintf(buf, "Revision ID: 0x%02X\r\n", rev_id);
    uart_print(buf);
    
    // Configure sensor
    i2c_write_reg(MAX_ADDR, REG_MODE_CONFIG, 0x03);  // SpO2 mode
    _delay_ms(10);
    
    i2c_write_reg(MAX_ADDR, REG_SPO2_CONFIG, 0x27);  // SPO2 ADC range 4096, Sample rate 100Hz
    _delay_ms(10);
    
    i2c_write_reg(MAX_ADDR, REG_LED_CONFIG, 0x7F);  // LED current 24mA
    _delay_ms(10);
    
    uart_print("Sensor initialized\r\n");
}

// Calculate SpO2 from red and IR values
uint8_t calculate_spo2(uint32_t red, uint32_t ir) {
    float ratio = (float)red / (float)ir;
    float spo2 = 110.0 - (25.0 * ratio);
    
    if (spo2 > 100.0) spo2 = 100.0;
    if (spo2 < 0.0) spo2 = 0.0;
    
    return (uint8_t)spo2;
}

// Read and process sensor data
void read_sensor_data(void) {
    uint8_t data[6];
    
    // Read FIFO data
    i2c_read_reg(MAX_ADDR, REG_FIFO_DATA, data, 6);
    
    // Process the data
    uint32_t red = ((uint32_t)data[0] << 16) | ((uint32_t)data[1] << 8) | data[2];
    uint32_t ir = ((uint32_t)data[3] << 16) | ((uint32_t)data[4] << 8) | data[5];
    
    // Calculate SpO2
    uint8_t spo2 = calculate_spo2(red, ir);
    
    // Print values
    char buffer[64];
    sprintf(buffer, "SpO2: %u%%\r\n", spo2);
    uart_print(buffer);
}

int main(void) {
    // Initialize UART and I2C
    uart_init();
    i2c_init();
    
    _delay_ms(1000);  // Startup delay
    
    uart_print("MAXDEFRES117 SPO2 Sensor Test\r\n");
    uart_print("---------------------------\r\n");
    
    // Initialize sensor
    sensor_init();
    
    while (1) {
        read_sensor_data();
        _delay_ms(1000);  // Read every second
    }
    
    return 0;
}

i2c.h

#ifndef I2C_H_
#define I2C_H_

#include <avr/io.h>
#include <stdint.h>

// Function prototypes for I2C communication
void i2c_init(void);
uint8_t i2c_start(uint8_t address);
uint8_t i2c_write(uint8_t data);
uint8_t i2c_read_ack(void);
uint8_t i2c_read_nack(void);
void i2c_stop(void);
uint8_t i2c_write_reg(uint8_t dev_addr, uint8_t reg_addr, uint8_t data);
uint8_t i2c_read_reg(uint8_t dev_addr, uint8_t reg_addr, uint8_t *data, uint8_t len);

#endif /* I2C_H_ */

i2c.c

#include "i2c.h"
#include <util/delay.h>

void i2c_init(void) {
    // Set SCL frequency to 100kHz with 16MHz CPU clock
    TWBR0 = 72;
    TWSR0 = 0;  // Prescaler = 1
    TWCR0 = (1 << TWEN);  // Enable TWI
}

uint8_t i2c_start(uint8_t address) {
    // Send START condition
    TWCR0 = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN);
    while (!(TWCR0 & (1 << TWINT)));
    
    // Send slave address + R/W bit
    TWDR0 = address;
    TWCR0 = (1 << TWINT) | (1 << TWEN);
    while (!(TWCR0 & (1 << TWINT)));
    
    return 1;
}

uint8_t i2c_write(uint8_t data) {
    TWDR0 = data;
    TWCR0 = (1 << TWINT) | (1 << TWEN);
    while (!(TWCR0 & (1 << TWINT)));
    return 1;
}

uint8_t i2c_read_ack(void) {
    TWCR0 = (1 << TWINT) | (1 << TWEN) | (1 << TWEA);
    while (!(TWCR0 & (1 << TWINT)));
    return TWDR0;
}

uint8_t i2c_read_nack(void) {
    TWCR0 = (1 << TWINT) | (1 << TWEN);
    while (!(TWCR0 & (1 << TWINT)));
    return TWDR0;
}

void i2c_stop(void) {
    TWCR0 = (1 << TWINT) | (1 << TWSTO) | (1 << TWEN);
    while (TWCR0 & (1 << TWSTO));
}

uint8_t i2c_write_reg(uint8_t dev_addr, uint8_t reg_addr, uint8_t data) {
    i2c_start(dev_addr);
    i2c_write(reg_addr);
    i2c_write(data);
    i2c_stop();
    return 1;
}

uint8_t i2c_read_reg(uint8_t dev_addr, uint8_t reg_addr, uint8_t *data, uint8_t len) {
    i2c_start(dev_addr);
    i2c_write(reg_addr);
    i2c_start(dev_addr | 0x01);  // Restart with read bit
    
    for(uint8_t i = 0; i < len-1; i++) {
        data[i] = i2c_read_ack();
    }
    data[len-1] = i2c_read_nack();
    
    i2c_stop();
    return 1;
}

SpO2

Next week’s plan

Task: Build GSR Dataset and Calibration

Task: Test Cooling Subsystem with GSR Dataset

Task: Pulse Sensor Accurate Reading

Task: LoRa Module Final Testing

Sprint review #1

Current state of project

Last week’s progress

Cooling Fan Subsystem

  1. The Cooling Fan Subsystem has been successfully tested. Based on a specified threshold of GSR values, the fan is now activated and controlled via PWM (we have used a MOSFET as a switch) to optimize power consumption. The fan turns on when the skin resistance is lowest (indicating higher sweat levels) and gradually slows down as the skin becomes drier, effectively conserving battery life. The subsystem has been fine-tuned to ensure smooth operation and efficient energy usage.

Please find the attached code and images below for further reference.

Main Code

/* 
 * File:   Fan_control.c
 * Author: Megha31
 *
 * Created on 14 November, 2024, 2:20 PM
 */

#include <stdio.h>
#include <stdlib.h>
#include <avr/io.h>

/*
 * 
 */

#ifndef F_CPU
#define F_CPU 16000000UL  // CPU frequency set to 16MHz
#endif

#include <avr/io.h>
#include <util/delay.h>

void timer1_init(void) {
    // Set PB1 (OC1A) as output
    DDRB |= (1 << PB1);
    
    // Configure Timer1 for Fast PWM mode
    // WGM13:0 = 1110 for Fast PWM mode with ICR1 as TOP
    TCCR1A |= (1 << WGM11);
    TCCR1B |= (1 << WGM13) | (1 << WGM12);
    
    // Set COM1A1:0 = 10 for non-inverting PWM on OC1A (PB1)
    TCCR1A |= (1 << COM1A1);
    TCCR1A &= ~(1 << COM1A0);
    
    // Set prescaler to 1 (no prescaling)
    TCCR1B |= (1 << CS10);
    
    // Set PWM frequency to about 25kHz
    // F_PWM = F_CPU / (prescaler * (1 + TOP))
    // 16000000 / (1 * (1 + 639)) ? 25kHz
    ICR1 = 639;
}

void set_fan_speed(uint8_t speed_percent) {
    // Limit speed_percent to 0-100 range
    if (speed_percent > 100) {
        speed_percent = 100;
    }
    
    // Calculate OCR1A value based on speed percentage
    // OCR1A determines duty cycle
    uint16_t ocr_value = (uint16_t)((ICR1 + 1) * speed_percent / 100) - 1;
    OCR1A = ocr_value;
}

int main(void) {
    // Initialize Timer1 for PWM
    timer1_init();
    
    while (1) {
        // Example speed control pattern
        
        // Run at 0% speed
        set_fan_speed(0);
        _delay_ms(6000);
        
//        // Run at 25% speed
        set_fan_speed(25);
        _delay_ms(6000);
//        
//        // Run at 50% speed
        set_fan_speed(50);
        _delay_ms(6000);
//        
//        // Run at 75% speed
        set_fan_speed(75);
        _delay_ms(6000);
//        
//        // Run at full speed
        set_fan_speed(100);
        _delay_ms(6000);
    }
    
    return 0;
}

uart.c file

/* 
 * File:   uart.c
 * Author: Megha31
 *
 * Created on 16 November, 2024, 12:52 PM
 */

#include "uart.h"
#include <stdarg.h>

static char uart_buffer[UART_BUFFER_SIZE];

void uart_init(void) {
    // Set baud rate
    UBRR0H = (uint8_t)(BAUD_PRESCALER >> 8);
    UBRR0L = (uint8_t)(BAUD_PRESCALER);
    
    // Enable transmitter and receiver
    UCSR0B = (1 << TXEN0) | (1 << RXEN0);
    
    // Set frame format: 8 data bits, 1 stop bit, no parity
    UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
}

void uart_putchar(char c) {
    // Wait for empty transmit buffer
    while (!(UCSR0A & (1 << UDRE0)));
    
    // Put data into buffer, sends data
    UDR0 = c;
}

void uart_puts(const char* str) {
    while (*str) {
        uart_putchar(*str++);
    }
}

void uart_printf(const char* format, ...) {
    va_list args;
    va_start(args, format);
    vsnprintf(uart_buffer, UART_BUFFER_SIZE, format, args);
    uart_puts(uart_buffer);
    va_end(args);
}

uart.h file

/* 
 * File:   uart.h
 * Author: Megha31
 *
 * Created on 14 November, 2024, 6:00 PM
 */

#ifndef UART_H
#define UART_H

#ifndef F_CPU
#define F_CPU 16000000UL
#endif

#define BAUD_RATE 9600
#define BAUD_PRESCALER ((F_CPU / (BAUD_RATE * 16UL)) - 1)

#include <avr/io.h>
#include <stdio.h>

// UART buffer size
#define UART_BUFFER_SIZE 64

// Function declarations
void uart_init(void);
void uart_putchar(char c);
void uart_puts(const char* str);
void uart_printf(const char* format, ...);

#endif

Hydration Level Computation

  1. We also completed computing basic hydration levels based on GSR data. We used an existing dataset (Dataset) of GSR readings and employed a Random Tree Classifier to analyze the data and estimate hydration levels. The classifier was trained to identify patterns in the GSR readings that correlate with hydration status, and it has shown promising results in predicting hydration levels (hydrated, medium hydration and dehydrated) with 100% accuracy.

Please find the attached code and images below for your reference.

import pandas as pd
from sklearn.model_selection import train_test_split, GridSearchCV, learning_curve
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
from sklearn.preprocessing import LabelEncoder
import matplotlib.pyplot as plt
import matplotlib

# Set matplotlib to use the Agg backend (for PyCharm or headless environments)
matplotlib.use('Agg')

# Function to convert stress level to hydration level
def stress_to_hydration(stress_level):
    if stress_level == 'Low Stress':  # Hydrated
        return 'Hydrated'
    elif stress_level == 'Medium Stress':  # Medium Hydration
        return 'Medium Hydrated'
    elif stress_level == 'High Stress':  # Dehydrated
        return 'Dehydrated'

# Load the dataset from a CSV file
df = pd.read_csv('C:/Users/geniu/Desktop/datasetFinal1.csv')

# Check if the CSV has the expected columns (GSR and Stress_Level)
if 'GSR' not in df.columns or 'Stress_Level' not in df.columns:
    raise ValueError("The CSV file must contain 'GSR' and 'Stress_Level' columns.")

# Apply the hydration level function to each row based on the Stress_Level
df['Hydration_Level'] = df['Stress_Level'].apply(stress_to_hydration)

# Encode the 'Hydration_Level' (target) column into numeric labels
le = LabelEncoder()
df['Hydration_Level_Label'] = le.fit_transform(df['Hydration_Level'])

# Feature and target variables
X = df[['GSR']]  # Features (GSR values)
y = df['Hydration_Level_Label']  # Target (Hydration Levels)

# Normalize the features (GSR values) to improve performance for models like Random Forest
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# Split the data into training and testing sets
X_train, X_test, y_train, y_test = train_test_split(X_scaled, y, test_size=0.2, random_state=42)

# Print the number of samples being trained and tested
print(f"Training Samples: {X_train.shape[0]}")
print(f"\nTest Samples: {X_test.shape[0]}")

# Initialize a Random Forest Classifier
rf_classifier = RandomForestClassifier(random_state=42)

# Optionally, perform hyperparameter tuning with GridSearchCV
param_grid = {
    'n_estimators': [100, 200, 300],
    'max_depth': [10, 20, 30],
    'min_samples_split': [2, 5, 10],
    'min_samples_leaf': [1, 2, 4]
}

# Use GridSearchCV to find the best hyperparameters
grid_search = GridSearchCV(estimator=rf_classifier, param_grid=param_grid, cv=5, n_jobs=-1, verbose=2)
grid_search.fit(X_train, y_train)

# Get the best estimator from GridSearchCV
best_rf_classifier = grid_search.best_estimator_

# Train the model on the training data
best_rf_classifier.fit(X_train, y_train)

# Get the best number of estimators from the grid search
best_n_estimators = best_rf_classifier.n_estimators

# Calculate the total operations (trees * samples)
total_operations = best_n_estimators * X_train.shape[0]
print(f"\nTotal Operations: {total_operations} operations")

# Make predictions on the test set
y_pred = best_rf_classifier.predict(X_test)

# Compute accuracy
accuracy = accuracy_score(y_test, y_pred)
print(f'\nAccuracy: {accuracy * 100:.2f}%')

# Optionally, print feature importance
print("\nFeature Importances:")
print(best_rf_classifier.feature_importances_)

# Optionally, print the classification results for test samples
print("\nPredictions on Test Set:")
print("Predicted Hydration Levels:")
print(le.inverse_transform(y_pred))

# Optionally, save the updated data with predictions to a new CSV file
df['Predicted_Hydration_Level'] = le.inverse_transform(best_rf_classifier.predict(X_scaled))
df.to_csv('hydration_levels_with_predictions.csv', index=False)

# Get the learning curve data
train_sizes, train_scores, test_scores = learning_curve(best_rf_classifier, X_train, y_train, cv=5)

# Debugging: Print the learning curve data
print("\nTrain Sizes: ", train_sizes)
print("\nTrain Scores: ", train_scores)
print("\nTest Scores: ", test_scores)

# Check if the shape of arrays is correct
print("\nTrain Sizes Shape:", train_sizes.shape)
print("Train Scores Shape:", train_scores.shape)
print("Test Scores Shape:", test_scores.shape)

# Calculate mean and standard deviation of training and testing scores
train_mean = train_scores.mean(axis=1)
test_mean = test_scores.mean(axis=1)

# Plot learning curves (will save the plot as an image)
plt.plot(train_sizes, train_mean, label="Training Score", color="blue", linestyle='-', marker='o')
plt.plot(train_sizes, test_mean, label="Validation Score", color="green", linestyle='--', marker='x')

plt.xlabel("Training Size")
plt.ylabel("Score (Accuracy)")
plt.title("Learning Curve")
plt.legend()

# Save the plot to a file (no need to display it interactively)
plt.savefig('learning_curve.png')  # Saves as a PNG image

# Optionally, close the plot to avoid any issues with PyCharm
plt.close()

The image shows a learning curve plot with Training Score and Validation Score as a function of Training Size.

  1. Training Score (in blue) is consistently high at 1.0 across all training sizes, indicating perfect accuracy during training.
  2. Validation Score (in green, dashed line) starts lower (around 0.8 for smaller training sizes) but quickly rises to nearly 1.0 as the training size increases.
  3. After reaching around 40 training samples, both the training and validation scores stabilize, indicating minimal variance and no overfitting.

Overall this plot suggests that the model is performing very well, with both training and validation accuracy being close to perfect.

Next week’s plan

Task: Build GSR Dataset and Calibration

Task: Test Cooling Subsystem with GSR Dataset

Task: Pulse Oximeter Accurate Sensor Reading

Task: LoRa Module Final Testing

Sprint review trial

Current state of project

Last week’s progress

Next week’s plan

Task: LoRa Module Long-Range Testing

Task: GSR Data Calibration

Task: Test the Cooling Fan Subsystem

Task: Pulse Oximeter Accurate Sensor Reading

In addition to these tasks, we will simultaneously work on completing the final report and developing the project website.

Final Project Proposal

1. Abstract

R.E.F.R.E.S.H is an innovative wearable device/glove designed to improve safety, performance, and health for Formula 1 drivers by providing real-time monitoring of vital signs and managing body temperature during races. In the extreme conditions of the F1 cockpit, drivers face significant physical stress, elevated temperatures, and dehydration risks, which can hinder focus, decision-making, and overall performance. The glove incorporates advanced sensors to measure Galvanic Skin Response (GSR), pulse, and SpO2 levels, providing comprehensive insights into the driver’s hydration status and physical condition. Using LoRa technology, the glove transmits real-time data to the team, allowing for prompt interventions to maintain the driver’s hydration levels. By leveraging the ATmega328PB microcontroller, the system autonomously triggers cooling mechanisms based on GSR and feedback, optimizing body temperature and promoting safety. R.E.F.R.E.S.H offers a proactive approach to driver wellness and performance, paving the way for advanced wearable technology applications in high-stress, physically demanding environments.

2. Motivation

3. Goals

To develop a wearable glove that:

4. System Block Diagram

System  Block Diagram

5. Design Sketches

AI-generated images that bring our vision of the product to life.

6. Software Requirements Specification (SRS)

  1. Real-time Sensor Data Monitoring
  1. Cooling System Activation
  1. Data Transmission via LoRa
  1. User Feedback Interface

7. Hardware Requirements Specification (HRS)

  1. Pulse Oximeter (MAX30102)
  1. GSR Sensor Module
  1. LoRa Module (RYLR 689)
  1. USB to TTL Converter (CP2102)
  1. Cooling System (Brushless DC Fan or Peltier Module)

8. Components

  1. MAX3010/MAXREFDES117# (Pulse Oximeter) (Qty - 1)
    • Purpose: To continuously monitor the driver’s pulse rate and SpO₂ levels, ensuring real-time tracking of vital signs.
    • Link: https://www.digikey.com/en/products/detail/analog-devices-inc-maxim-integrated/MAXREFDES117/6165562
  2. Grove GSR Sensor Module (Qty - 1)
    • Purpose: This sensor measures skin resistance, which helps determine skin moisture levels. Monitoring skin moisture is crucial for assessing hydration status.
    • Link: https://www.digikey.com/en/products/detail/seeed-technology-co-ltd/101020052/5488086?s=N4IgTCBcDaIOYCcD2A3ApgAjgZwSAugL5A
  3. RYLR 896 (LoRa Module) (Qty - 2)
    • Purpose: Enables long-range, low-power wireless communication for transmitting health data and GSR data from the glove to the racing team’s platform.
    • Link: https://www.digikey.com/en/products/detail/reyax/RYLR896/22145027
  4. Anti-Vibration Gloves (Qty - 2)
    • Purpose: Provides comfort and reduces the impact of vibrations on the driver’s hands during racing, helping maintain focus and dexterity.
    • Link: https://www.amazon.com/Vibration-Oil-proof-Resistant-Glassfirbe-Excellent/dp/B073FKRP99 , https://www.amazon.com/Vibration-Gloves-Padding-Protector-Mechanic/dp/B073QHXJJK
  5. Brushless DC Cooling Fan (Qty - 2)
    • Purpose: To create airflow and cool the interior of the glove in response to real-time moisture and temperature readings.
    • Link: https://www.amazon.com/Dorhea-Raspberry-30x30x7mm-Brushless-Cooling/dp/B07G97L9TL
  6. CP2102 (USB to TTL Serial) (Qty - 2)
    • Purpose: Facilitates USB to TTL serial communication between the microcontroller and the computer for debugging and data upload.
    • Link: https://www.amazon.com/HiLetgo-CP2102-Converter-Adapter-Downloader/dp/B00LODGRV8
  7. Peltier Module (Optional) (Qty - 1)
    • Purpose: Provides additional cooling by utilizing thermoelectric principles to lower the glove’s internal temperature effectively.
    • Link: https://www.amazon.com/WWZMDiB-TEC1-12706-Semiconductor-Refrigeration-Heatsinks/dp/B0CBDNS9H6
  8. Microcontroller (ATmega328PB) (Qty - 1)
    • Purpose: Acts as the central processing unit to gather sensor data, control the cooling system, and manage communication with the LoRa module.
    • Link: https://www.mouser.com/ProductDetail/Microchip-Technology/ATMEGA328PB-XMINI?qs=jy4bLUHv09jRyNVdIGMiUQ%3D%3D

9. Final Demo

By the final demonstration, we expect to have a functional prototype of the wearable glove that integrates all key components, including sensors for monitoring pulse, SpO₂, skin moisture, and temperature, alongside a cooling system. The prototype will enable real-time data monitoring and feedback, utilizing LoRa technology for reliable data transmission to a central platform. Initial testing will validate sensor accuracy and cooling responsiveness, and we will compile a preliminary report documenting results and insights. This foundation will guide future development, focusing on enhancements and integration with advanced data analytics, ultimately aiming to improve driver health and performance in competitive racing environments.

10. Methodology

alt text

What is your approach to the problem?

1. Individual Sensor Testing

2. Sensor Integration and Combined Testing

3. Microcontroller and Data Processing

4. Positioning and Embedding in the Glove

5. Data Transmission Testing

6. Testing and Optimization Finally, we conduct simulations and real-world testing in controlled environments to evaluate the system’s ability to accurately read and transmit data while worn, assessing sensor accuracy, LoRa transmission reliability, and comfort. Feedback from these tests will guide iterative improvements to enhance functionality, durability, and overall performance under race conditions.

11. Evaluation

To effectively evaluate our product’s success, we will employ a comprehensive set of metrics focusing on sensor accuracy, cooling system responsiveness, and user comfort. This includes measuring the precision of sensors against validated standards, analyzing the cooling system’s temperature reduction rate and activation time, and gathering user feedback on comfort during prolonged use. We’ll assess the reliability and latency of LoRa technology for data transmission, ensuring timely updates and minimal data loss. Additionally, we will evaluate the speed of data processing and the clarity of insights provided to users, establishing a multi-faceted user testing framework to gather real-world feedback and drive iterative improvements. This holistic approach ensures our solution delivers meaningful, actionable results in racing environments.

12. Proposal Presentation

https://docs.google.com/presentation/d/1Jt2f54SHfv9yaWAEWJdul8VxFSMtWL5YvAFFXDAgIP4/edit#slide=id.g30f18db8311_187_453

References

Fill in your references here as you work on your proposal and final submission. Describe any libraries used here.