# Project export: HeadshotX

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: Cal Hacks 11.0
- Tagline: HeadshotX is a real-time headgear impact tracking system designed for boxing and contact sports. This data is streamed to a mobile app providing athletes and coaches with insights on impact intensity.
- Devpost: https://devpost.com/software/headshotx
- GitHub: https://github.com/rickNrip/headshotX
- Demo: http://github.com/TheLastBeast/Backend-Headshot
- Video: https://www.youtube.com/embed/TrtU-_P9IPA?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 1 GitHub contributor(s) — rickNrip (1 commits)

## Devpost submission (written by the team)

### Inspiration

Sac State is the first college to recognize combat sports as part of its athletics program, moving beyond just club sports. However, in the first three months of training, a significant number of boxers have experienced concussions during sparring.

### What it does

To address this issue, we’ve developed a solution: smart headgear designed to reduce the risk of concussions. By using advanced sensors, the headgear monitors impact forces in real-time and provides critical feedback, helping to protect athletes and enhance safety during training.

### How we built it

We developed our smart headgear using a combination of hardware and software technologies to provide real-time impact monitoring. The hardware setup includes: Raspberry Pi Pico W – serving as the core controller for collecting sensor data. MPU-6050 Sensors – three-axis accelerometers and gyroscopes are integrated into the headgear to measure both linear and angular accelerations, which are critical for assessing impact forces.

### Challenges we ran into

One of chllenges we ran into being able to stream data in real-time to our mobile app to provide real time feedback. We also broke ALOT of pins testing our headgear.

### Accomplishments we're proud of

We are very proud to have our prototype completed

## README (from the GitHub repository)

# headshotX
 This is our repo for the Cal hacks 11.0 project.


## Detected evidence (automated analysis)

Indexed codebase: 6 recognized source files, 33 KB.
- Dart (language) — detected in the code
- Python (language) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (7 of 7)

```
.gitattributes
configuration_page.dart
head_gear_page.dart
home_page.dart
main.dart
README.md
sensor_data.dart
```

### Dependencies

No dependency index available.

### Recent commits (newest first)

- this push fixes our github issues
- Initial commit

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

### head_gear_page.dart

```dart
import 'package:flutter/material.dart';

class HeadGearPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Head Gear'),
      ),
      body: Center(
        child: Text(
          'This is the Head Gear page',
          style: TextStyle(fontSize: 24),
        ),
      ),
    );
  }
}

```

### configuration_page.dart

```dart
// ConfigurationPage
import 'package:flutter/material.dart';

class ConfigurationPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Configuration'),
      ),
      body: Center(
        child: Text(
          'This is the Configuration page',
          style: TextStyle(fontSize: 24),
        ),
      ),
    );
  }
}

```

### main.dart

```dart
import 'package:flutter/material.dart';
import 'home_page.dart'; // Import the HomePage file
import 'sensor_data.dart'; // Import the new SensorDataWidget

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      initialRoute: '/',
      routes: {
        '/': (context) => HomePage(), // HomePage is the initial route
      },
    );
  }
}

```

### sensor_data.dart

```dart
import 'package:http/http.dart' as http;
import 'dart:io';
import 'dart:convert';
import 'dart:async';

// Buffer to accumulate incomplete JSON data
String _buffer = '';

// Function to handle partial JSON updates
void _handlePartialJson(String rawJson, Function(double) onDataReceived) {
  print('Handling partial JSON data...');

  // Append the new data chunk to the buffer
  _buffer += rawJson;

  // Try to decode the accumulated buffer
  try {
    // Attempt to parse the accumulated buffer
    var parsedData = jsonDecode(_buffer);

    // Process the parsed data
    List<double> totalAccelG = List<double>.from(
        parsedData.map((entry) => entry['total_acceleration_g']));
    double gForce = totalAccelG.reduce((a, b) => a + b) / totalAccelG.length;

    print('Complete data processed. G-force = $gForce');

    // Call the onDataReceived callback to update the UI
    onDataReceived(gForce);

    // Clear the buffer after successful parsing
    _buffer = '';
  } catch (e) {
    // If parsing fails, print the error but do not clear the buffer
    // This likely means the JSON is incomplete and more data is coming
    print('Error parsing JSON: $e');
  }
}

Future<void> fetchSensorData(
    Function(double) onDataReceived, Function(String) onStatusUpdate) async {
  HttpClient client = HttpClient();
  try {
    print('Attempting to connect to Pico W for streaming...');

    final request = await client.getUrl(Uri.parse('http://192.168.4.1/events'));
    print('Request created successfully.');

    final response = await request.close();
    print('Response received. Status Code: ${response.statusCode}');

    if (response.statusCode == 200) {
      print('Successfully connected to Pico W.');
      onStatusUpdate("connected");

      // Listen to the stream and process data as it comes in
      await response.listen(
        (data) {
          final message = String.fromCharCodes(data);
          print('Data chunk received: $message');

          // Use _handlePartialJson() to handle the streamed data and buffer it
          try {
            _handlePartialJson(message, onDataReceived);
          } catch (parseError) {
            print('Error handling partial JSON: $parseError');
          }
        },
        onError: (e) {
          print('Stream error: $e');
        },
        onDone: () {
          print('Stream closed.');
          onStatusUpdate("disconnected");
        },
        cancelOnError: true,
      ).asFuture();

      print('Stream finished successfully.');
    } else {
      print('Failed to connect to Pico W. Status Code: ${response.statusCode}');
      onStatusUpdate("disconnected");
    }
  } catch (e) {
    print('Error connecting to Pico W: $e');
    onStatusUpdate("disconnected");
  } finally {
    print('Closing the client.');
    client.close(); // Close the client when done
  }
}

```

### home_page.dart

```dart
import 'package:flutter/material.dart';
import 'package:headshotx/configuration_page.dart';
import 'package:headshotx/head_gear_page.dart';
import 'package:headshotx/sensor_data.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'dart:async';
import 'package:percent_indicator/circular_percent_indicator.dart';

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  late Timer _timer;
  String userName = "Ricky B.";
  String detectedM = "";
  String detectedL = "";
  String detectedH = '';
  int totalHits = 0;
  double totalGForce = 0.0; // Accumulate total G-force here
  double gForce = 0.0;
  int highImpact = 0;
  int mediumImpact = 0;
  int lowImpact = 0;
  int _seconds = 0;
  int heightFt = 0;
  int heightInch = 0;
  bool isConnected = false;
  String status = "not connected";
  int weight = 0;
  int age = 0;
  bool _isRunning = false;
  bool _isStopped = false;

  // Function to show the dialog for editing stats
  void _showEditDialog() {
    TextEditingController weightController =
        TextEditingController(text: '$weight');
    TextEditingController heightFtController =
        TextEditingController(text: '$heightFt');
    TextEditingController heightInchController =
        TextEditingController(text: '$heightInch');
    TextEditingController ageController = TextEditingController(text: '$age');

    showDialog(
      context: context,
      builder: (BuildContext context) {
        return AlertDialog(
          title: Text('Edit Fighter Stats'),
          content: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              TextField(
                controller: weightController,
                decoration: InputDecoration(labelText: 'Weight (lbs)'),
                keyboardType: TextInputType.number,
              ),
              TextField(
                controller: heightFtController,
                decoration: InputDecoration(labelText: 'Height (feet)'),
                keyboardType: TextInputType.number,
              ),
              TextField(
                controller: heightInchController,
                decoration: InputDecoration(labelText: 'Height (inches)'),
                keyboardType: TextInputType.number,
              ),
              TextField(
                controller: ageController,
                decoration: InputDecoration(labelText: 'Age'),
                keyboardType: TextInputType.number,
              ),
            ],
          ),
          actions: [
            TextButton(
              onPressed: () {
                setState(() {
                  weight = int.parse(weightController.text);
                  heightFt = int.parse(heightFtController.text);
                  heightInch = int.parse(heightInchController.text);
                  age = int.parse(ageController.text);
                });
                Navigator.of(context).pop();
              },
              child: Text('Save'),
            ),
            TextButton(
              onPressed: () {
                FocusScope.of(context).unfocus();
                Navigator.of(context).pop();
              },
              child: Text('Cancel'),
            ),
          ],
        );
      },
    );
  }

  // Function to format the time as hh:mm:ss
  String _formatTime(int seconds) {
    int minutes = (seconds % 3600) ~/ 60;
    int secs = seconds % 60;
    return '${minutes.toString().padLeft(2, '0')}:${secs.toString().padLeft(2, '0')}';
  }

  @override
  void initState() {
    super.initState();
    print('Initializing connection to fetch sensor data...');
    _startFetchingSensorData(); // Start fetching data when the app starts
  }

  void _startFetchingSensorData() async {
    try {
      print('Sending request to Pico W...');
      final request =
          http.Request('GET', Uri.parse('http://192.168.4.1/events'));
      final response = await request.send();

      print('Response status: ${response.statusCode}');
      if (response.statusCode == 200) {
        setState(() {
          status = "connected";
        });
        print('Successfully connected to Pico W.');

        // Listen to the stream and process data as it comes in
        await response.stream.listen((data) {
          final message = String.fromCharCodes(data);
          print('Data chunk received: $message');
          _handlePartialJson(
              message, updateData); // Pass updateData as the callback
        }).asFuture(); // Wait for the stream to finish
      } else {
        setState(() {
          status = "disconnected";
        });
        print('Disconnected: Status code ${response.statusCode}');
      }
    } catch (e) {
      print('Error connecting to Pico W: $e');
      setState(() {
        status = "disconnected";
      });
      // Retry after a short delay
      Future.delayed(Duration(seconds: 3), () {
        _startFetchingSensorData(); // Retry connecting after 3 seconds
      });
    }
  }

// Function to handle partial JSON updates
  void _handlePartialJson(String rawJson, Function(double) onDataReceived) {
    print('Handling partial JSON data: $rawJson');

    // Strip the 'data: ' prefix if present
    if (rawJson.startsWith('data: ')) {
      rawJson = rawJson.substring(6); // Remove the 'data: ' part
    }

    // Try to decode the accumulated buffer
    try {
      // Parse the JSON
      var parsedData = jsonDecode(rawJson);

      // Process the parsed data
      List<double> totalAccelG = List<double>.from(
          parsedData.map((entry) => entry['total_acceleration_g']));
      double gForce = totalAccelG.reduce((a, b) => a + b) / totalAccelG.length;

      print('Complete data processed. G-force = $gForce');

      // Call the onDataReceived callback to update the UI
      onDataReceived(gForce);
    } catch (e) {
      // If parsing fails, print the error
      print('Error processing partial JSON: $e');
[truncated — 23974 more characters]
```