Differences

This shows you the differences between two versions of the page.

Link to this comparison view

iothings:proiecte:2025sric:runtrack32 [2025/05/29 00:45]
alexandra.cornea01 [Software Design]
iothings:proiecte:2025sric:runtrack32 [2025/05/29 02:31] (current)
alexandra.cornea01 [Demonstration and Results]
Line 17: Line 17:
 The sensors are powered via the breadboard'​s power rails, and communication is established using serial (for GPS) and I2C (for MAX30100). The sensors are powered via the breadboard'​s power rails, and communication is established using serial (for GPS) and I2C (for MAX30100).
  
-{{:​iothings:​proiecte:​2025sric:​runtracker.jpg?​500|}}+{{:​iothings:​proiecte:​2025sric:​runtracker.jpg?​570|}}
  
 **GPS (Neo-6M) Connections** **GPS (Neo-6M) Connections**
Line 35: Line 35:
  
 === Arduino IDE Code === === Arduino IDE Code ===
 +
 +The firmware running on the ESP32 is responsible for collecting sensor data and sending it via Bluetooth. The setup initializes three main components: the MAX30100 pulse oximeter for measuring heart rate (BPM) and oxygen saturation (SpO₂), the Neo-6M GPS module for providing location data, and Bluetooth Serial for transmitting all collected values to the Android app.
 +
 +In the ''​loop()''​ function, the ESP32 continuously reads GPS data over serial in the NMEA 0183 format, focusing on ''​$GPRMC''​ sentences. These sentences contain comma-separated values representing time, fix status, latitude, longitude, and more. For example, from ''​$GPRMC,​200142.00,​A,​4426.63451,​N,​02603.24242,​E,​...'',​ the latitude value ''​4426.63451''​ and direction ''​N''​ are decoded into decimal degrees (''​44.443908''​) for easier mapping. At the same time, BPM and SpO₂ values are read once per second and all the data is sent over Bluetooth in a human-readable format.
  
 <​code>​ <​code>​
Line 60: Line 64:
 BluetoothSerial SerialBT; BluetoothSerial SerialBT;
  
-void setup(){+void setup() {
   // Serial Monitor   // Serial Monitor
   Serial.begin(57600);​   Serial.begin(57600);​
Line 82: Line 86:
 } }
  
-void loop(){ +void loop() { 
-  while (gpsSerial.available() > 0){+  while (gpsSerial.available() > 0) {
     // get the byte data from the GPS     // get the byte data from the GPS
     char gpsData = gpsSerial.read();​     char gpsData = gpsSerial.read();​
Line 151: Line 155:
 float convertToDecimalDegrees(String raw, String dir) { float convertToDecimalDegrees(String raw, String dir) {
   int degreeLength = (dir == "​N"​ || dir == "​S"​) ? 2 : 3;   int degreeLength = (dir == "​N"​ || dir == "​S"​) ? 2 : 3;
 +  ​
   float deg = raw.substring(0,​ degreeLength).toFloat();​   float deg = raw.substring(0,​ degreeLength).toFloat();​
   float min = raw.substring(degreeLength).toFloat();​   float min = raw.substring(degreeLength).toFloat();​
   float result = deg + (min / 60.0);   float result = deg + (min / 60.0);
 +  ​
   if (dir == "​S"​ || dir == "​W"​)   if (dir == "​S"​ || dir == "​W"​)
     result = -result;     result = -result;
Line 163: Line 169:
 === Mobile App Code === === Mobile App Code ===
  
-==== Setup ====+The Android application is built using Kotlin and connects to the ESP32 via Bluetooth to display real-time fitness data. Upon launch, the app searches for a bonded device named RunTracker32,​ establishes a Bluetooth socket connection, and listens for incoming data in a background thread. 
 + 
 +The ''​listenForData()''​ function reads Bluetooth messages, and once a newline character is detected, the message is passed to ''​parseAndDisplay()''​. This function uses regular expressions to extract values for heart rate (BPM), oxygen saturation (SpO₂), and GPS coordinates. These values are then shown in the app's user interface. 
 + 
 +<​code>​ 
 +private fun listenForData() { 
 +  val input: InputStream? ​bluetoothSocket?​.inputStream 
 +  val buffer ​ByteArray(1024) 
 +  val messageBuilder ​StringBuilder() 
 + 
 +  while (true) { 
 +    val bytes input?​.read(buffer) ?: break 
 +    val readMessage ​String(buffer,​ 0, bytes) 
 + 
 +    messageBuilder.append(readMessage) 
 + 
 +    if (readMessage.contains("​\n"​)) { 
 +      val fullMessage ​messageBuilder.toString().trim() 
 +      messageBuilder.clear() 
 + 
 +      runOnUiThread { 
 +        parseAndDisplay(fullMessage) 
 +      } 
 +    } 
 +  } 
 +
 +</​code>​ 
 + 
 +<​code>​ 
 +private fun parseAndDisplay(message:​ String) { 
 +  val bpmRegex ​Regex("""​BPM:​\s*([\d.]+)"""​) 
 +  val spo2Regex ​Regex("""​SpO2:​\s*(\d+)"""​) 
 +  val latRegex = Regex("""​Latitude:​\s*([\d.]+)"""​) 
 +  val lonRegex = Regex("""​Longitude:​\s*([\d.]+)"""​) 
 + 
 +  val bpm = bpmRegex.find(message)?​.groupValues?​.get(1) ?: "?"​ 
 +  val spo2 = spo2Regex.find(message)?​.groupValues?​.get(1) ?: "?"​ 
 +  val latStr = latRegex.find(message)?​.groupValues?​.get(1) ?: ""​ 
 +  val lonStr = lonRegex.find(message)?​.groupValues?​.get(1) ?: ""​ 
 + 
 +  pulseText.text = getString(R.string.bpm_format,​ bpm) 
 +  spo2Text.text = getString(R.string.spo2_format,​ spo2) 
 + 
 +  if (latStr.isNotEmpty() && lonStr.isNotEmpty()) { 
 +    gpsText.text = getString(R.string.location_format,​ latStr, lonStr) 
 +    lastLat = latStr 
 +    lastLon = lonStr 
 + 
 +    val lat = latStr.toDoubleOrNull() 
 +    val lon = lonStr.toDoubleOrNull() 
 +    val now = System.currentTimeMillis() 
 + 
 +    if (lat != null && lon != null) { 
 +      if (lastLatValue != null && lastLonValue != null && lastTimestamp != null) { 
 +        val speed = calculateSpeedKmH( 
 +          lastLatValue!!,​ lastLonValue!!,​ lastTimestamp!!,​ 
 +          lat, lon, now 
 +        ) 
 +        speedText.text = getString(R.string.speed_format,​ "​%.2f"​.format(speed)) 
 +      } 
 +      lastLatValue = lat 
 +      lastLonValue = lon 
 +      lastTimestamp = now 
 +    } 
 +  } 
 +
 +</​code>​ 
 + 
 +To estimate speed, the app implements a Haversine-based function, calculateSpeedKmH(),​ which computes the distance between two sets of latitude/​longitude coordinates and divides it by the time difference. This allows the app to display an approximate speed in kilometers per hour (km/h), updated every time a new valid GPS coordinate is received. 
 + 
 +<​code>​ 
 +private fun calculateSpeedKmH( 
 +  lat1: Double, lon1: Double, time1: Long, 
 +  lat2: Double, lon2: Double, time2: Long 
 +): Double { 
 +  val deltaTimeSec = (time2 - time1) / 1000.0 
 +  if (deltaTimeSec == 0.0) return 0.0 
 + 
 +  val R = 6371.0 
 + 
 +  val dLat = Math.toRadians(lat2 - lat1) 
 +  val dLon = Math.toRadians(lon2 - lon1) 
 +  val a = sin(dLat / 2).pow(2.0) + 
 +      cos(Math.toRadians(lat1)) * cos(Math.toRadians(lat2)) * 
 +      sin(dLon / 2).pow(2.0) 
 +  val c = 2 * atan2(sqrt(a),​ sqrt(1 - a)) 
 +  val distanceKm = R * c 
 + 
 +  return distanceKm / (deltaTimeSec / 3600.0) 
 +
 +</​code>​ 
 + 
 +==== Demonstration and Results ==== 
 + 
 +To illustrate the functionality of the system, the images and logs offer a glimpse into how the components interact in practice, with real-time data flowing from the hardware to the mobile app. 
 + 
 +The Android application displays real-time values such as heart rate, oxygen saturation, speed, and GPS location in a clean interface. 
 + 
 +{{:​iothings:​proiecte:​2025sric:​app.jpg?​300|}} 
 + 
 +Serial logs from the Arduino console show how data is continuously read from the GPS and pulse oximeter sensors. 
 + 
 +{{:​iothings:​proiecte:​2025sric:​monitor.png?​570|}}
  
-==== Results ====+the image shows the MAX30100 sensor in use for pulse measurement,​ alongside the GPS module actively providing location and speed data.
  
 +{{:​iothings:​proiecte:​2025sric:​testspeed.jpg?​570|}}
 ==== References ==== ==== References ====
  
 +  * [[https://​randomnerdtutorials.com/​esp32-neo-6m-gps-module-arduino/​|ESP32 with NEO-6M GPS Module (Arduino IDE)]]
 +  * [[https://​www.electronicwings.com/​esp32/​max30100-pulse-oximeter-interfacing-with-esp32|MAX30100 Pulse Oximeter Interfacing with ESP32]]
 +  * [[https://​www.ridgesolutions.ie/​index.php/​2013/​11/​14/​algorithm-to-calculate-speed-from-two-gps-latitude-and-longitude-points-and-time-difference/​|Algorithm to calculate speed from two GPS latitude and longitude points and time difference]]
iothings/proiecte/2025sric/runtrack32.1748468754.txt.gz · Last modified: 2025/05/29 00:45 by alexandra.cornea01
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0