This shows you the differences between two versions of the page.
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]] |