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:55]
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 169: 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.
  
-==== Results ====+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|}}
 +
 +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.1748469353.txt.gz · Last modified: 2025/05/29 00:55 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