I am able to access Heart Rate of User using Optical Heart rate Sensor using SensorEventListener:
sensorManager.registerListener(this,
sensorManager.getDefaultSensor(Sensor.TYPE_HEART_RATE),
SensorManager.SENSOR_DELAY_NORMAL);
What I need is to get raw PPG data like:
https://ars.els-cdn.com/content/image/1-s2.0-S0960077915001344-gr1.jpg
Through Google Fit or any other means can I get this data?
I looked into Google Fit API usage:
https://developers.google.com/fit/android/sensors
It gives TYPE_HEART_RATE_BPM and HEART_RATE_SUMMARY , but not PPG raw data.
I spent some time figuring out if it's feasible to get the raw PPG signal from android API, and contrary to what some may say, it is definitely possible to do that. And here is how to do it:
1- Get the sensor type (represented by a number) of your PPG sensor (it is a constant that describes the sensor). This can be done by listing all the available sensors on your device:
List<Sensor> sensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
for (Sensor currentSensor : sensorList) {
Log.d("List sensors", "Name: "+currentSensor.getName() + " /Type_String: " +currentSensor.getStringType()+ " /Type_number: "+currentSensor.getType());
}
2- Then, try to find the sensorType of your PPG sensor. In your output, you will find one of the sensors containing the word ppg in it's type. For instance here is what the ppg sensor of my Huawei watch 2 looks like:
Name: AFE4405 light Sensor /Type_String: com.huawei.watch.ppg /Type_Number: 65537
3-Register your listener using the the type_number of your ppg sensor, mine is 65537 so I would do this:
sensorManager.registerListener(this,sensorManager.getDefaultSensor(65537),SensorManager.SENSOR_DELAY_NORMAL);
4-Listen to changes in your sensor data:
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == 65537) {
String msg = "PPG " + (int) event.values[0];
//Do something with your PPG signal
}
}
And you are all set. Hope it works for you.
Related
D5 runs Android 4.4
I want to measure the heart rate, but standard way of registerListener is not working (not hardware problem, with inbuilt app is working).
I get all sensors from device and i found this:
{Sensor name="ULPSH Heart Rate M", vendor="QuickLogic Inc", version=1, type=87, maxRange=100.0, resolution=1.0, power=1.1, minDelay=0}
In android.hardware.Sensor i don't found this type id 87 so i tried the following:
mSensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
mHeartRate = mSensorManager.getDefaultSensor(87);
mSensorManager.registerListener(this, mHeartRate, SensorManager.SENSOR_DELAY_NORMAL);
....
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == 87) {
//do something
}
}
but unfortunately is not working, the measurement is not getting started.
I tried this code with another sensor (Step Counter, type 19) that works well.
Any idea what I'm doing wrong?
I have the same problem. I think the problem is the firmware. You also can't get access to gyro, barometer and etc.
I found in the Launcher app how you can access the last heart rate.
System.getInt(getContentResolver(), "last_heart_rate", 0);
I am using ACCELEROMETER sensors and have registered a listener for the same via
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mAcceleratorSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mSensorManager.registerListener(this, mAcceleratorSensor , SensorManager.SENSOR_DELAY_NORMAL);
This is how my onSensorChanged looks like
#Override
public final void onSensorChanged(SensorEvent event) {
int sensorType = event.sensor.getType();
switch(sensorType){
case Sensor.TYPE_ACCELEROMETER:
float valueX = event.values[0];
float valueY = event.values[1];
float valueZ = event.values[2];
Log.d(TAG, "Sensor Changed value:"+valueX+":"+valueY+":"+valueZ);
break;
}
However this is what I see in my logs
01-12 02:01:18.063 19691-19691/com.taxis.locationupdates2 D/LocationActivity: Sensor Changed value:2.0:2.0:2.0
01-12 02:01:18.129 19691-19691/com.taxis.locationupdates2 D/LocationActivity: Sensor Changed value:2.0:2.0:2.0
It keeps calling the onSensorChanged in infinite loop even though there is no change in the values. I havent tested it with a real device yet. Is there any settings to control the same.
That method is very poorly named.
From the android docs for onSensorChanged:
Called when there is a new sensor event. Note that "on changed" is
somewhat of a misnomer, as this will also be called if we have a new
reading from a sensor with the exact same sensor values (but a newer
timestamp).
The sensor documentation has the details about the same
http://developer.android.com/guide/topics/sensors/sensors_overview.html
The default data delay is suitable for monitoring typical screen orientation changes and uses a delay of 200,000 microseconds. You can specify other data delays, such as SENSOR_DELAY_GAME (20,000 microsecond delay), SENSOR_DELAY_UI (60,000 microsecond delay), or SENSOR_DELAY_FASTEST (0 microsecond delay). As of Android 3.0 (API Level 11) you can also specify the delay as an absolute value (in microseconds).
The delay that you specify is only a suggested delay. The Android system and other applications can alter this delay. As a best practice, you should specify the largest delay that you can because the system typically uses a smaller delay than the one you specify (that is, you should choose the slowest sampling rate that still meets the needs of your application). Using a larger delay imposes a lower load on the processor and therefore uses less power.
I am having problems accessing heart rate sensor on Moto 360.
I tried following things :
Sensor mHeartRateSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_HEART_RATE);
mSensorManager.registerListener(this, mHeartRateSensor, SensorManager.SENSOR_DELAY_NORMAL);
and then implement SensorEventListener interface :
#Override
public void onSensorChanged(SensorEvent event) {
String TAG = "tag";
Log.i(TAG, "--------------------------");
Log.i(TAG, msg);
Log.i(TAG, ""+ event.sensor.getType());
Log.i("live","--------------");
And what is strange to me I do not get any messages at all (not only heart rate).
Also I tried listing all sensors and it does not show Heart rate sensor on the list.
Of course I've added persmissions
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.watchtest" >
<uses-feature android:name="android.hardware.type.watch" />
<uses-permission android:name="android.permission.BODY_SENSORS" />
Any ideas ?
thanks.
w.
Started to work for me after I did the following:
Uninstalled my app from the watch with
adb -s localhost:4444 uninstall com.example.android.wearable.jumpingjack
Added permissions to get the heart rate sensor
<uses-permission android:name="android.permission.BODY_SENSORS"/>
Set the min and target SDK version to match the watch
android:minSdkVersion="20" android:targetSdkVersion="20"
Started the app again. I received the heart rate sensor with Sensor.TYPE_HEART_RATE and I started to receive its readings. Although they were far from good. There were a lot of readings, but they were just the same, limited to these 5 values:
heartRate onSensorChanged values = [0.0], accuracy = 0, sensor = {Sensor name="Heart Rate Sensor", vendor="Motorola", version=1, type=21, maxRange=65535.0, resolution=1.0, power=0.45, minDelay=0}
heartRate onSensorChanged values = [53.0], accuracy = 2, sensor = {Sensor name="Heart Rate Sensor", vendor="Motorola", version=1, type=21, maxRange=65535.0, resolution=1.0, power=0.45, minDelay=0}
heartRate onSensorChanged values = [54.0], accuracy = 2, sensor = {Sensor name="Heart Rate Sensor", vendor="Motorola", version=1, type=21, maxRange=65535.0, resolution=1.0, power=0.45, minDelay=0}
heartRate onSensorChanged values = [55.0], accuracy = 2, sensor = {Sensor name="Heart Rate Sensor", vendor="Motorola", version=1, type=21, maxRange=65535.0, resolution=1.0, power=0.45, minDelay=0}
heartRate onSensorChanged values = [77.0], accuracy = 1, sensor = {Sensor name="Heart Rate Sensor", vendor="Motorola", version=1, type=21, maxRange=65535.0, resolution=1.0, power=0.45, minDelay=0}
Most of the time I was getting the same 53.0 value which doesn't seem to be my real heart rate. 77 could have been the one.
I had quite a similar problem on the Moto 360. The sensor always returned 0.0f as a value.
Then I waited for two minutes, and suddenly values!=0 came in. It seems that this sensor needs a "warmup" before showing anything. Not really astonishing if you take into account that it measures something happening roughly once a second with the unit "beats per minute". It cannot be reliable before one or two minutes have passed. And each app has its own measurement: It doesn't matter if another heartbeat app is also running (like the Moto Body thing).
This also means that you must create a service to listen to the sensor (and a binder to pass the sensor's value to your activity or your phone).
Have a look at the demo project I shared on github:
https://github.com/upost/MyHeartbeat
As #Kent and #Murphy suggested, updated SDK was the solution. In my case I needed to drop the project and create new from scratch as even with updated SDK old one did not work.
So, I came here, with the same problem and the simple solution is to remove the application from the watch using the adb:
adb -s localhost:4444 uninstall com.*packagename*
Then simply reinstall it using android studio, eclipse or whatever you used originally.
Thanks to Alexander K for this solution
Android supports many sensors and the API supports periodic sampling i.e. I can sample the sensor data at every specified sampling time. Ideally, the periodic sampling is useful, but often it is unnecessary to continuously read a sensor say the accelerometer when the phone is not vibrating for a long duration (e.g. during charging). I want to read the data of accelerometer randomly, where the sampling frequency is not fixed i.e. the duration between two samples is random. How do I do that ?
I tried the following code for reading the accelerometer data:
final public SensorEventListener listener=new SensorEventListener() {
public void onSensorChanged(SensorEvent e) {
Log.d(TAG, "in sensor changed");
if (e.sensor.getType()==Sensor.TYPE_ACCELEROMETER) {
double netForce=e.values[0]*e.values[0];
netForce+=e.values[1]*e.values[1];
netForce+=e.values[2]*e.values[2];
AccelX=e.values[0];
AccelY=e.values[1];
AccelZ=e.values[2];
message_acc= String.valueOf(AccelX)+","+String.valueOf(AccelY)+","+String.valueOf(AccelZ);
Log.d(TAG, message_acc);
//check if the phone is shaking
if (threshold<netForce) {
isShaking();
}
else {
isNotShaking();
}
}
}
This method is called in a thread, which is called periodically. In the above code, the API allows to collect the data when ever there is change in the data. My doubt is, is this approach a periodic data collection for a given sampling period? How do I deterministically specify the period of sampling ?
Secondly, If I want to perform random data collection, should it be done at the thread level, I mean, call/schedule the above method randomly? Any pointers to implement this or better ways to achieve this?
I'm building a App the requires high resolution on one of the acceleration sensors, but no data at all from any others. so, when I'm listening to the event, I'm doing the following:
mSensorManager.registerListener(this, mAccelerometer,
SensorManager.SENSOR_DELAY_FASTEST);
the thing is, the event shoots every time a sensor changes, and preforms a check - lots of CPU time wasted, that could be used for better things.
Is there a way to save this CPu time?
You only have to listen to one sensor:
sensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
accSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
sensorManager.registerListener(this, accSensor, SensorManager.SENSOR_DELAY_GAME);
Now as to performance, in my experience even fastest will not have a major impact on performance but there are some thins to remember. Values as they arrive should be immediately dumped into another thread for processing. Doing all the processing in the sensor thread can impact performance or delivery time of future samples.
Copy the values out of the SensorData and don't keep a reference to the values array. Those are part of a fixed pool that may get re-used later and you don't want those changed out from under your code.
SENSOR_DELAY_FASTEST is generally not suitable if you have any care about precision. The speed comes at a price of having jittery data. Dump the sensor data to the lof on fastest and put the device on a stable flat surface. It still dumps out tons of samples because it thinks there are lots of changes to report while with SENSOR_DELAY_GAME the samples slow way down because there is nothing to report and that's correct. Motorola is another story with this since they aggressively clamp down on duplicate samples and setting a device on a flat surface will see the number of reported samples go to 0.
try another flag:
SENSOR_DELAY_FASTEST //get sensor data as fast as possible
SENSOR_DELAY_GAME //rate suitable for games
SENSOR_DELAY_NORMAL //rate (default) suitable for screen orientation changes
SENSOR_DELAY_UI //rate suitable for the user interface
Since API 3, you can register for a single sensor. Use the approach bellow:
try {
sensorMgr = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
sensors = sensorMgr.getSensorList(Sensor.TYPE_ACCELEROMETER);
if (sensors.size() > 0) sensorGrav = sensors.get(0);
sensorMgr.registerListener(this, sensorGrav, SensorManager.SENSOR_DELAY_FASTEST);
} catch (Exception ex1) {
try {
if (sensorMgr != null) {
sensorMgr.unregisterListener(this, sensorGrav);
sensorMgr = null;
}
} catch (Exception ex2) {
ex2.printStackTrace();
}
}
and the listener will be called only for changes in the acceleration sensors.
good luck.
i don't want a function to be called for every sensor, just the one that interests me
That's the way Android works, but it only happens if you register more than one sensor. In case you want to read more than one sensor, what you can do is use a switch-case to filter your favorite sensor.
#Override
public void onSensorChanged(SensorEvent event) {
synchronized (this){
switch (event.sensor.getType()) {
case Sensor.TYPE_ACCELEROMETER:
//Do your magic here
break;
case Sensor.TYPE_MAGNETIC_FIELD:
System.arraycopy(event.values, 0, magneticFieldValues, 0,
event.values.length);
break;
}
}
}
Is there a way to save this CPU time?
Do you REALLY need SENSOR_DELAY_FASTEST? The fastest you read, the fastest you drain the battery. If you changed it, you will save CPU time because you won't have to read the sensor as many times as you do when you use SENSOR_DELAY_FASTEST nor you won't execute that many time the onSensorChanged method.
Remember that using FASTEST, GAME, NORMAL or UI doesn't mean how fast you will get a data when a change occurs. It means how often the device reads the registered sensor(s) (it doesn't matter if the values are the same or not) and executes the onSensorChanged method if the last value is different from the just-read-value (both from the same sensor).