3

I am working on a app which will run on wearable (Sony Smart Watch 3) and handheld (Samsung S5).

I want to sense the accelerometer and gyroscope data from watch continuously and send and save the data to handheld.

I am wondering which is the best way to achieve this. Currently I am using DataApi.

Since accelerometer and gyroscope data sampling rate is high enough, and app on wearable is sending data to phone continuously, it is allocating too much memory, and then killed by OS.

Here is the activity on the watch:

public class MyActivity extends Activity implements SensorEventListener{

    private TextView mTextView;
    private TextView textViewGyro;
    private SensorManager mSensorManager;
    private GoogleApiClient mGoogleApiClient;
    private String mNode;
    private float x,y,z;
    private float gx,gy,gz;
    private final String TAG = MyActivity.class.getName();
    private final float GAIN = 0.9f;
    public static final float EPSILON = 0.000000001f;
    String WEARABLE_DATA_PATH = "/wearable_data";
    //String WEARABLE_GYROSCOPE_PATH = "/wearable_gyroscope";


    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my);
        final WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub);
        stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
            @Override
            public void onLayoutInflated(WatchViewStub stub) {
                mTextView = (TextView) stub.findViewById(R.id.text);
                textViewGyro= (TextView) stub.findViewById(R.id.textgyro);
            }
        });

        mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);

        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addApi(Wearable.API)
                .addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
                    @Override
                    public void onConnected(Bundle bundle) {
                        Log.d(TAG, "onConnected");
                        Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).setResultCallback(new ResultCallback<NodeApi.GetConnectedNodesResult>() {
                            @Override
                            public void onResult(NodeApi.GetConnectedNodesResult nodes) {                               
                                if (nodes.getNodes().size() > 0) {
                                    mNode = nodes.getNodes().get(0).getId();
                                    Log.e("Wearable","Device found");
                                    Log.e("Wearable",mNode);
                                }
                            }
                        });
                    }

                    @Override
                    public void onConnectionSuspended(int i) {
                        Log.d(TAG, "onConnectionSuspended");

                    }
                })
                .addOnConnectionFailedListener(new GoogleApiClient.OnConnectionFailedListener() {
                    @Override
                    public void onConnectionFailed(ConnectionResult connectionResult) {
                        Log.d(TAG, "onConnectionFailed : " + connectionResult.toString());
                    }
                })
                .build();
    }

    @Override
    protected void onResume() {
        super.onResume();

        Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);

        mSensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL);

        Sensor sensor1 = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
        mSensorManager.registerListener(this, sensor1, SensorManager.SENSOR_DELAY_NORMAL);

        mGoogleApiClient.connect();
    }

    @Override
    protected void onPause() {
        super.onPause();
        mSensorManager.unregisterListener(this);
        mGoogleApiClient.disconnect();
    }

    @Override
    public void onSensorChanged(SensorEvent event) {
        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
            x = (x * GAIN + event.values[0] * (1 - GAIN));
            y = (y * GAIN + event.values[1] * (1 - GAIN));
            z = (z * GAIN + event.values[2] * (1 - GAIN));

            if (mTextView != null) mTextView.setText(String.format("\n" + "Accelerometer Data: \nX : %f\nY : %f\nZ : %f\n",x, y, z));

            DataMap dataMap = new DataMap();
            dataMap.putLong("timestamp", new Date().getTime());
            dataMap.putFloat("ax",x);
            dataMap.putFloat("ay",y);
            dataMap.putFloat("az",z);

            new SendToDataLayerThread(WEARABLE_DATA_PATH, dataMap).start();

        }

        if (event.sensor.getType() == Sensor.TYPE_GYROSCOPE) {
            gx = event.values[0];
            gy = event.values[1];
            gz = event.values[2];

            float omegaMagnitude = (float)java.lang.Math.sqrt(gx*gx + gy*gy + gz*gz);

            // Normalize the rotation vector if it's big enough to get the axis
            // (that is, EPSILON should represent your maximum allowable margin of error)
            if (omegaMagnitude > EPSILON) {
                gx /= omegaMagnitude;
                gy /= omegaMagnitude;
                gz /= omegaMagnitude;
            }

            if (textViewGyro != null)
                textViewGyro.setText(String.format("\nGyroscope Data: \nX : %f\nY : %f\nZ : %f\n", gx, gy, gz));

            DataMap dataMap = new DataMap();
            dataMap.putLong("timestamp", new Date().getTime());
            dataMap.putFloat("gx",gx);
            dataMap.putFloat("gy",gy);
            dataMap.putFloat("gz",gz);

            new SendToDataLayerThread(WEARABLE_DATA_PATH, dataMap).start();
        }
    }

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

    }

    class SendToDataLayerThread extends Thread {
        String path;
        DataMap dataMap;

        // Constructor for sending data objects to the data layer
        SendToDataLayerThread(String p, DataMap data) {
            path = p;
            dataMap = data;
        }

        public void run() {
            NodeApi.GetConnectedNodesResult nodes = Wearable.NodeApi.getConnectedNodes(mGoogleApiClient).await();
            for (Node node : nodes.getNodes()) {

                // Construct a DataRequest and send over the data layer
                PutDataMapRequest putDMR = PutDataMapRequest.create(path);
                putDMR.getDataMap().putAll(dataMap);
                PutDataRequest request = putDMR.asPutDataRequest();
                DataApi.DataItemResult result = Wearable.DataApi.putDataItem(mGoogleApiClient,request).await();
                if (result.getStatus().isSuccess()) {
                    Log.e("myTag", "DataMap: " + dataMap + " sent to: " + node.getDisplayName());
                } else {
                    // Log an error
                    Log.e("myTag", "ERROR: failed to send DataMap");
                }
            }
        }
    }
}

I am having the following exceptions and errors:

02-13 02:06:37.777  17349-17360/? E/DataBuffer? Internal data leak within a DataBuffer object detected!  Be sure to explicitly call release() on all DataBuffer extending objects when you are done with them. (su@357af50)

02-13 02:24:09.300      594-685/? D/WearableService? putData: exception during processing: PutDataRequest[dataSz=67, numAssets=0, uri=wear:/wearable_gyroscope]
    android.os.DeadObjectException

02-13 02:24:09.108      594-685/? D/WearableService? getConnectedNodes: exception during processing
    android.os.DeadObjectException

android.system.ErrnoException: read failed: EAGAIN (Try again)
            at libcore.io.Posix.readBytes(Native Method)
            at libcore.io.Posix.read(Posix.java:147)
            at libcore.io.BlockGuardOs.read(BlockGuardOs.java:230)
            at android.system.Os.read(Os.java:364)
            at com.android.server.am.NativeCrashListener.consumeNativeCrashData(NativeCrashListener.java:240)
            at com.android.server.am.NativeCrashListener.run(NativeCrashListener.java:138)
02-13 15:46:29.977      150-150/? E/DEBUG? AM write failure (32 / Broken pipe)
02-13 15:46:34.704  21570-21581/? E/DataBuffer? Internal data leak within a DataBuffer object detected!  Be sure to explicitly call release() on all DataBuffer extending objects when you are done with them. (su@35563df0)

02-13 11:14:44.482  12012-12012/? E/AndroidRuntime? FATAL EXCEPTION: main
    Process: com.example.fazlay.accelerometerwearable, PID: 12012
    java.lang.OutOfMemoryError: pthread_create (1040KB stack) failed: Try again
            at java.lang.Thread.nativeCreate(Native Method)
            at java.lang.Thread.start(Thread.java:1063)
            at com.example.fazlay.accelerometerwearable.MyActivity.onSensorChanged(MyActivity.java:168)
            at android.hardware.SystemSensorManager$SensorEventQueue.dispatchSensorEvent(SystemSensorManager.java:405)
            at android.os.MessageQueue.nativePollOnce(Native Method)
            at android.os.MessageQueue.next(MessageQueue.java:143)
            at android.os.Looper.loop(Looper.java:122)
            at android.app.ActivityThread.main(ActivityThread.java:5221)
            at java.lang.reflect.Method.invoke(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:372)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)

02-13 15:52:47.934      424-464/? E/ActivityManager? ANR in com.example.fazlay.accelerometerwearable (com.example.fazlay.accelerometerwearable/.MyActivity)
    PID: 0
    Reason: Input dispatching timed out (Waiting to send non-key event because the touched window has not finished processing certain input events that were delivered to it over 500.0ms ago.  Wait queue length: 6.  Wait queue head age: 13150.8ms.)


02-13 16:19:23.999  24797-24797/? E/AndroidRuntime? FATAL EXCEPTION: main
    Process: com.example.fazlay.accelerometerwearable, PID: 24797
    java.lang.IllegalStateException: await must not be called on the UI thread
            at com.google.android.gms.internal.jx.a(Unknown Source)
            at com.google.android.gms.common.api.BaseImplementation$AbstractPendingResult.await(Unknown Source)
            at com.example.fazlay.accelerometerwearable.MyActivity.onSensorChanged(MyActivity.java:171)
            at android.hardware.SystemSensorManager$SensorEventQueue.dispatchSensorEvent(SystemSensorManager.java:405)
            at android.os.MessageQueue.nativePollOnce(Native Method)
            at android.os.MessageQueue.next(MessageQueue.java:143)
            at android.os.Looper.loop(Looper.java:122)
            at android.app.ActivityThread.main(ActivityThread.java:5221)
            at java.lang.reflect.Method.invoke(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:372)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
Ravi Patel
  • 5,121
  • 2
  • 25
  • 44
Sniper
  • 31
  • 1
  • 2

1 Answers1

1

Take a look at Github project SensorDashboard, they receive all sensors data and pass it to mobile via MessageAPI. I think it is working really well. From what I remember they used some sort of cap to limit amount of data being send.

Michał Tajchert
  • 10,333
  • 4
  • 30
  • 47