---
# System prepended metadata

title: Radiance - Smart Watch App Monitering Heart Beat
tags: [Fullstack, App Development, Javascript, 'Web Development ']

---

# Smart Watch App Monitering Heart Beat
GitHub Reposite: [Link](https://github.com/khchandn/Android_Watch_Radiance.git)


The Heart Rate, Inter-Beat Interval and the Breathing rate should be taken for the comprehensive investigation of the user. Which would be uploaded to the Radiance cloud server for real time monitering.

!!!!!!!!!!

**Issue left behind: the database cannot connect to the web and the watch tasks**

!!!!!!!!

In control.js
```
function update() { // fetch data from database and update the chart
  fetch('http://127.0.0.1:8000/heartbeat')
      .then(response => response.json())
      .then(data => {
      //   console.log(data);
        sqlheartdata = data;
```
the part responsible for the communication.

In HeartBeatController.php:
```
public function index()
    {
        // change here
        $serverName = "127.0.0.1:3306";
        $databaseName = "laravel";
        $userName = "root";
        $password = "";
        
        $data = array();

        try {
            $con = mysqli_connect($serverName, $userName, $password, $databaseName);
            if (!$con) {
                throw new \Exception('Could not connect to database: ' . mysqli_connect_error());
            }

            $sql = "SELECT * FROM heartbeat";
            $result = mysqli_query($con, $sql);

            while($row = mysqli_fetch_assoc($result)){
                $UUID = (String)$row['UUID'];
                $timestamp = (int) $row['Timestamp'];
                $heartbeat = (int) $row['HeartBeat'];
                $data[] = array('userID' => $UUID, 'x' => $timestamp*1000,'y' => $heartbeat);
            }

            mysqli_close($con);
        } catch (\Exception $e) {
            return response()->json([
                'error' => $e->getMessage(),
            ], 500);
        }

        return response()->json($data);
    }
```

The part responsible for the connecting to the database.

Good Luck :crying_cat_face: 



[TOC]


## Important Useful Information

### Connect the Watch
Use the CMD ADB connect functions and enter the ip address in the "Debug through Wifi" which found in the "Developer Option" or "開發者選項". (Which is usually: 10.71.22.202:5555)

The CMD command:
```
adb connect xx.xx.xx.xx:xxxx
```

Then open the android studio and select the device and then can run the code in the watch. :cat: 

### The Important File Directory

The **main event** of the app(send data, receive data):
Watch App/test_java/app/src/main/java/com/example/test_java/**MainActivity.java**

The **layout file** of the watch app:
Watch App/test_java/app/src/main/res/layout/**activity_main.xml**

The **gradle build file** which is used to include the library you:
Watch App/test_java/**build.gradle**

### Important Web files
The web is using the **Laravel framework** (please go check it)

The **layout file** for the threapy control panel:
therapy-remake/resources/views/user/**vr-therapy-control.blade.php**

The **route file** for the web:
therapy-remake/routes/**web.php**

The **controller file** for the heartbeat graph(HeartBeatController):
therapy-remake/app/Http/Controllers/**HeartBeatController.php**

The **Javascript file** to run the actions:
therapy-remake/resources/js/**control.js**

The **migration file** for creating the SQL table:
therapy-remake/database/migrations/**create_heartbeat.php**




## To-do List
- [x] Find a way to collect sensor data
    - [x] Find way to get the IBI and breath rate
- [x] Find a way to upload the data to the server
    - [x] Construst a local server
    - [x] Send data to local server
    - [x] Upload data to the server
- [x] Implement the Control Panel
    - [x] The single person control panel
    - [x] The duo person control panel
    - [x] Implement the control panel & database


## App Development Setup

First is the developmet platform. There are 2 main operation system(OS) for the smart watch: Tizen OS and Wear OS. Since the watch used is Samsung Galaxy Watch 5. The OS is restricted to Wear OS because it runs on the Wear OS.

After installed the platform: Andorid Studio, First is to learn what language it is using. For the Wear OS, the preferred app development language is Kotlin, a language based on Java. However, the syntax is not too kind for me (a C++/C user). Hence, I chosen Java to do the app programme.

The link for android studio: [Andorid Studio](https://developer.android.com/studio/install?hl=zh-tw)

## Implementation

### Import packages

In the search of the methods, there are 2 main ways to collect the data:
- [Use Health Service API](https://developer.android.com/guide/health-and-fitness/health-connect)
- [Use Sensors API](https://developer.android.com/guide/topics/sensors/sensors_overview)

Both API/Library have their own advantage. But in this case, the data need to be analysis, a faster data rate is needed. Hence **Sensor API** is used.

To setup the library is need to be included in this project. There are .gradle files in the newly created project.(Act like .make file) 

![](https://hackmd.io/_uploads/SJjszNDL2.png)

The packages should be implement in the project. Below is the code of the build.gradle:
```
dependencies {
    compileOnly 'com.google.android.wearable:wearable:2.9.0'
    implementation 'com.google.android.support:wearable:2.9.0'
    implementation 'com.google.android.gms:play-services-wearable:18.0.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
}
```
They are mainly the packages for the display.

### MainActivity Class

A MainActivity which is a Activity Sub-Class is needed for the App to launch.

![](https://hackmd.io/_uploads/SyUZ7EPLn.png)


It contain of following member object:
```
    private SensorManager sensorManager;
    private Sensor heartRateSensor;
    private TextView textView;
    private SensorEventListener sensorEventListener
```

Those are the esstenial member to call upon the API of the sensor.

Then is the first function: **onSensorChanged** which is an override function. It is to get the sensor function when it found the sensor.

![onSensorChanged](https://hackmd.io/_uploads/HkifUhcLh.png)


```
if (event.sensor.getType() == Sensor.TYPE_HEART_RATE)
```
It is called to check whether the sensor is heartbeat sensor or not.

The next function is to create the layout when the app is launched:

![](https://hackmd.io/_uploads/BJtMOn9In.png)

We will find the different text in the .xml layout file by the findViewById function with the TextView id.




## Setup a local server for Testing

After asking CHEN for help, he suggest the use of Laravel for the setup of local server. First is the setup. There are serval link for the setup: 
[Video Link](https://www.youtube.com/watch?v=MYyJ4PuL4pY&ab_channel=TraversyMedia)
[Offical Web](https://laravel.com/docs/10.x)
But the essential module need to be install is the: [**XAMPP**](https://www.apachefriends.org/download.html) and [**Composer**](https://getcomposer.org/download/)

Requirement: **Git**

After installing the XAMPP and the Composer, the [Laravel](https://laravel.com/docs/10.x) should be setup.

Then we will need to install the laravel homestead: [install video](https://www.youtube.com/watch?v=6Su5hScxPnc&ab_channel=QuentinWattTutorials)

Then We need to create a web (Both Backend and Frontend) for the task. :crying_cat_face: 

Then I found a tutorial and it used a web called 000webhost which can create a webhost include the SQL database and the php files can be uploaded and get the url from the web.

## Get Data from Watch

First is the app, the [volley library](https://google.github.io/volley/) is used since it can send HTTP Request to the server.

After the construction of the 000Webhost, the php file can be upload for the testing. I upload **getdata.php** as a test for the watch to send time stamp and the heartbeat rate to the online server. Here is the code:
```
<?php
        
$serverName = "localhost";
$databaseName = "id20885790_watchheartbeat";
$userName = "id20885790_onlyfortesting";
$password = "Aa!042604260426";
$con = mysqli_connect($serverName, $userName, $password, $databaseName);
if(!$con) echo "Error<br>";
else echo "Connected<br>";

$timestamp = $_POST['timestamp'];
$heartBeat = $_POST['heartBeat'];
$heartBeatint = (int)$heartBeat;
$timestampstring = (String)$timestamp;

$sql = "INSERT INTO HeartBeat (Timestamp, HeartBeat) VALUES ('$timestampstring', '$heartBeatint')";

if (mysqli_query($con, $sql)) {
  $response["success"] = 1;
  $response["message"] = "Received value: ". $heartBeat. ",". $timestamp;
} else {
  $response["success"] = 0;
  $response["message"] = "Failed to receive value";
}

mysqli_close($con);

echo json_encode($response);
?>
```
> ~~Remember to check syntax.低級軟件錯誤...~~ :crying_cat_face: 


And the Android code is Like this:
```
package com.example.senddatawatch;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import com.android.volley.DefaultRetryPolicy;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;

import org.json.JSONObject;

import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

public class MainActivity extends Activity {

    private int heartBeat = 1235; // Test value
    private String ts = "";

    private String postData = "";
    private Button button;
    private TextView textView;
    private String sendUrl = "https://crocodilian-buds.000webhostapp.com/test/getdata.php";
    private RequestQueue requestQuene;
    private static final String TAG = MainActivity.class.getSimpleName();
    int sucess;
    private String T_Sucess = "success";
    private String T_Message = "message";
    private String T_json_obj = "json_obj_req";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        button = findViewById(R.id.button);
        textView = (TextView)findViewById(R.id.a);
        textView.setText("test");
        requestQuene = Volley.newRequestQueue(getApplicationContext());
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                textView = (TextView)findViewById(R.id.a);
                textView.setText(postData);
                sendData();
            }
        });
    }

    private void sendData(){
        StringRequest request = new StringRequest(Request.Method.POST, sendUrl, new Response.Listener<String>() {
            @Override
            public void onResponse(String response) {
                try {
                    JSONObject jsonObject = new JSONObject(response);
                    sucess = jsonObject.getInt(T_Sucess);
                    if (sucess == 1) {
                        Toast.makeText(MainActivity.this, jsonObject.getString(T_Message), Toast.LENGTH_SHORT).show();
                    } else {
                        Toast.makeText(MainActivity.this, jsonObject.getString(T_Message), Toast.LENGTH_SHORT).show();
                    }
                } catch (Exception e) {
                    Toast.makeText(MainActivity.this, "" + e, Toast.LENGTH_SHORT).show();
                }
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Toast.makeText(MainActivity.this, error.getMessage().toString(), Toast.LENGTH_SHORT).show();
            }
        }){
            @Override
            public String getBodyContentType() {
                return "application/x-www-form-urlencoded; charset=UTF-8";
            }

            @Override
            public byte[] getBody() {
                Long tsLong = System.currentTimeMillis()/1000;
                ts = tsLong.toString();
                postData = "timestamp=" + ts +"&heartBeat=" + heartBeat;
                return postData.getBytes();
            }
        };
        request.setRetryPolicy(new DefaultRetryPolicy(10000,1,1.0f));
        requestQuene.add(request);

    }

}

```

> Will do explaination later... YG...

Then is the combination of 2 code. The heartrate app and the send app.

After combine the code should look like this:
```
package com.example.test_java;

import android.app.Activity;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast;
import android.support.wearable.view.WatchViewStub;

import static com.google.android.gms.wearable.DataMap.TAG;

import com.android.volley.DefaultRetryPolicy;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;

import org.json.JSONObject;

public class MainActivity extends Activity {
    private SensorManager sensorManager;
    private Sensor heartRateSensor;
    private TextView textView;
    private int heartBeat ;
    private String ts = "";
    private String postData = "";
    private String sendUrl = "https://crocodilian-buds.000webhostapp.com/test/getdata.php";
    private RequestQueue requestQuene;
    int sucess;

//    Switch dtmode = (Switch) findViewById(R.id.DTswitch);


    private String T_Sucess = "success";
    private String T_Message = "message";
    private String T_json_obj = "json_obj_req";
    private static final String TAG = MainActivity.class.getSimpleName();
    private SensorEventListener sensorEventListener = new SensorEventListener() {
        @Override
        public void onSensorChanged(SensorEvent event) {
            textView = (TextView) findViewById(R.id.b);
            if (event.sensor.getType() == Sensor.TYPE_HEART_RATE) {
                String msg = "" + (float)event.values[0];
                heartBeat = (int)event.values[0];
                textView.setText(msg);
                sendData();
                Log.d(TAG, msg);
            }
            else {
                textView = (TextView) findViewById(R.id.b);
                Log.d(TAG, "Unknown sensor type");
                String msg = "unknown";
                textView.setText(msg);
            }
        }

        @Override
        public void onAccuracyChanged(Sensor sensor, int accuracy) {

        }
    };
//    WatchViewStub



    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        textView = (TextView) findViewById(R.id.a);
        requestQuene = Volley.newRequestQueue(getApplicationContext());
        sensorManager = ((SensorManager) getSystemService(Context.SENSOR_SERVICE));
        heartRateSensor = sensorManager.getDefaultSensor(Sensor.TYPE_HEART_RATE);
        sensorManager.registerListener(sensorEventListener, heartRateSensor, SensorManager.SENSOR_DELAY_FASTEST);
        Log.i(TAG, "LISTENER REGISTERED.");
        textView.setText("Heart Rate");
        textView = (TextView) findViewById(R.id.b);
        textView.setText("Detecting...");
        textView = (TextView) findViewById(R.id.DTswitch);
        textView.setText("Data Transfer");
//        dtmode = (Switch) findViewById(R.id.DTswitch);


        sensorManager.registerListener(sensorEventListener, heartRateSensor, sensorManager.SENSOR_DELAY_FASTEST);
    }
    public void onResume(){
        super.onResume();
    }
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        Log.d(TAG, "onAccuracyChanged - accuracy: " + accuracy);
    }

    private void sendData(){
        StringRequest request = new StringRequest(Request.Method.POST, sendUrl, new Response.Listener<String>() {
            @Override
            public void onResponse(String response) {
                try {
                    JSONObject jsonObject = new JSONObject(response);
                    sucess = jsonObject.getInt(T_Sucess);
                    if (sucess == 1) {
                        Toast.makeText(MainActivity.this, jsonObject.getString(T_Message), Toast.LENGTH_SHORT).show();
                    } else {
                        Toast.makeText(MainActivity.this, jsonObject.getString(T_Message), Toast.LENGTH_SHORT).show();
                    }
                } catch (Exception e) {
                    Toast.makeText(MainActivity.this, "" + e, Toast.LENGTH_SHORT).show();
                }
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Toast.makeText(MainActivity.this, error.getMessage().toString(), Toast.LENGTH_SHORT).show();
            }
        }){
            @Override
            public String getBodyContentType() {
                return "application/x-www-form-urlencoded; charset=UTF-8";
            }

            @Override
            public byte[] getBody() {
                Long tsLong = System.currentTimeMillis()/1000;
                ts = tsLong.toString();
                postData = "timestamp=" + ts +"&heartBeat=" + heartBeat;
                textView = (TextView) findViewById(R.id.a);
                textView.setText(postData);
                return postData.getBytes();
            }
        };
        request.setRetryPolicy(new DefaultRetryPolicy(1000,1,1.0f));
        requestQuene.add(request);

    }



}

```

> Remember to set permission in AndroidManifest.xml
> <uses-permission android:name="android.permission.BODY_SENSORS"/>
    <uses-permission android:name="android.permission.INTERNET"/>
    
    


With the use of volley library, We can send and retrieve the response from the online .php file.

How ever it has few problems: First, the php webpage does not update instantly.Ｉt would be done in the html file afterward. (The graph generation).


![](https://hackmd.io/_uploads/S14ZIrlPh.png)


And second when I tried to connect the SQL through my getdata.php, it cannot be access and show the error code. 
The php code:
```
<?php
        
$serverName = "localhost";
$databaseName = "id20885790_watchheartbeat";
$userName = "id20885790_onlyfortesting";
$password = "Aa!042604260426";
$con = mysqli_connect($serverName, $userName, $password, $databaseName);
if(!$con) echo "Error<br>";
else echo "Connected<br>";

$heartBeat = $_POST['heartBeat'];

$sql = "INSERT INTO your_table_name (heartBeat) VALUES ('$heartBeat')";

if (mysqli_query($conn, $sql)) {
  $response["success"] = 1;
  $response["message"] = "Received value: " . $heartBeat;
} else {
  $response["success"] = 0;
  $response["message"] = "Failed to receive value";
}

mysqli_close($conn);

echo json_encode($response);
?>
```
The webpage result:
![](https://hackmd.io/_uploads/SkmPDBlv2.png)

## Reading the Data from DataBase

In the 000WebHost, we can access the SQL database for reading the data to generate graph for the display of the realtime data.

First a .php file should be built to retrive the data. The code: 

readdata.php
```
<?php

$serverName = "localhost";
$databaseName = "id20885790_watchheartbeat";
$userName = "id20885790_onlyfortesting";
$password = "Aa!042604260426";
$con = mysqli_connect($serverName, $userName, $password, $databaseName);

// Same as sending data

// if(!$con) echo "Error<br>";
// else echo "Connected<br>";

/*DO NOT echo the status since the html file only read the json for 
  showing the graph*/


$sql = "SELECT * FROM HeartBeat";
$result = mysqli_query($con, $sql);

$data = array(); // Put the data into an array

while($row = mysqli_fetch_assoc($result)){ // Retriving Data Non-Stop
    $timestamp = (int) $row['Timestamp'];
    $heartbeat = (int) $row['HeartBeat']; // Cast to int for graph to generate
    $data[] = array('x' => $timestamp*1000,'y' => $heartbeat);
}

header('Content-Type: application/json');
echo json_encode($data); // ONLY ECHO JSON FOR THE HTML

mysqli_close($con);

?>
```
Result photo:
![](https://hackmd.io/_uploads/BkJ748Nw3.png)
> Somewhat like this la



The graph generating file should read the data from the readdata.php. The html code:

linegraph.html:
```
<!DOCTYPE html>
<html>
    <head>
        <title>HeartBeat Line Graph Temp</title>
        <script src="https://code.highcharts.com/highcharts.js"></script>
            <!--To retrive the chart library from the website-->
    </head>
    <body>
        <div id="container"></div>
        <script>
            document.addEventListener('DOMContentLoaded', function() {
                var chart = Highcharts.chart('container', {
                    chart: {
                        type: 'spline' <!--For smooth curve-->
                    },
                    title: {
                        text: 'HeartBeat Graph'
                    },
                    xAxis: {
                        title: {
                            text: 'Time'
                        }
                    },
                    yAxis: {
                        title: {
                            text: 'HBR'
                        }
                    },
                    series: [{
                        name: 'My Data',
                        data: [] // Empty the array at start
                    }]
                });
                
                function updateChart() {
                    var xhr = new XMLHttpRequest(); // XML Request
                    xhr.onreadystatechange = function(){
                        if (this.readyState == 4 && this.status == 200) {
                            var allData = JSON.parse(this.responseText); 
                            // Get all data first
                            var lastestData = allData.slice(-10);
                            //Get lastest 10 data
                            chart.series[0].setData(lastestData);
                        }
                    };
                    var url = "readdata.php?nocache=" + Math.random();
                    // cache-busting parameter Force fetching
                    xhr.open("GET", url, true);
                    xhr.send();
                }
                
                updateChart(); //Init generate graph
                setInterval(updateChart, 1000); // update every 1s
            });
        </script>
    </body>
</html>
```


The resulting graph will auto update. :smile_cat: :smile_cat: 

![](https://hackmd.io/_uploads/H18wE84Dh.png)





## Integrate to the Cloud Server

After asking CHEN for the server address and account. A aaPanelLinux panel website is given.

URL: http://43.252.167.19:8888/60c26441
Username: radiance
Password: radiance88#

Then I need to integrate my application to the server. However I didn't see any .php file. But I think I can use it.

The VR Therapy System html file is "index.html".

The file getdata.php file was uploaded to the server for the watch to access the database.
The SQL connection parameter would be changed:
```
$serverName = "43.252.167.19";
$databaseName = "Therapy";
$userName = "Radiance";
$password = "Radiance88#";
```

And in the app, the parameter of the sendUrl would be changed too:
```
private String sendUrl = "http://43.252.167.19:9012/getdata.php";
```

The AndroidManifest.xml file should be changed too since there will be error if the text traffic is not clear:

```
 <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:usesCleartextTraffic="true" // Added this code
        android:theme="@android:style/Theme.DeviceDefault" >
```

After the modification the watch data can be uploaded to the server. :cat: 

![](https://hackmd.io/_uploads/r1l26LrPn.png)

> Data is uploaded to database from the watch.

After the data transmition, the html and the js file should be modified to update the resulting graph. After checking the html file is scripted the control2.js file.
```
<script src="./js/control2.js"></script>
```

After checking the control2.js file, the syntax and the functions is not suitable for my idea. After a long time of modifing the code. I decide to write a new file based on the old .js file. :crying_cat_face: 


After write a new js file for the receiveing the data and based on original code.(Take a long time), the control panel can auto update now and have the check funtions on the top graph. The code is on github.
![](https://hackmd.io/_uploads/SykxYL6P2.png)

> History: show all of the history data
> Back to Update: Back to the lastest data and continue to update
> Clear: Clear all database data for a new therapy session


Now is to implement the 2 person panel. And the new php file for receiveing 2 dataset and the chart.js are needed for the displaying.

After creating the js file and the html, the duo person control panel is like this:

![](https://hackmd.io/_uploads/Sk_Li5lsn.jpg)
