What's the best way to extend the "starter" module to read from multiple boards at once?

Hi there, I'm a pretty inexperienced programmer (physics background but working in a HCI lab currently) and am working on a Unity app that can display the orientation of a human leg based on information streaming from 3 MMC sensors. So far I have modified the "starter" script to stream quaternions to a Unity app on an android phone in real time, and update the orientation of a cube accordingly.

The last step before I'm out of the woods programming-wise (and into the area I'm actually competent in) is extending this so that data is being taken from 3 sensors instead of 1. I spent most of today trying to modify the starter script but attempting to change certain parts to set up 3 sensors seemed to completely break the rest of the code. I'm sorry I don't have a concise/specific question here but if anyone could give me some tips on how to move forward with this it would save me a lot of hours. I know that there is a multimw script in the tutorial pack, but changing that to stream 3 quaternions seems more difficult than updating the starter script to connect to multiple boards (I think, anyways).

Thanks in advance.

Comments

  • For the record, this is my first time doing any Android programming whatsoever so it's been a challenge. I feel like my goal should be easy to accomplish if I knew what I was looking for so I'm hoping there's a simple solution.

    • What "starter" script are you referring to?
    • What have you tried so far?
  • edited May 2019

    I'm referring to the starter script located here:
    https://github.com/mbientlab/MetaWear-Tutorial-Android/tree/master/starter

    My attempt so far involves keeping count of how many devices have been connected and if it is not yet the desired amount (3 in my case), then "startActivityForResult(navActivityIntent, REQUEST_START_APP);" is bypassed in the onDeviceSelected part of MainActivity. Once the required number of devices have been connected, the intent containing the 3 devices is passed to startActivityForResult.

    To add to this I also made some changes to DeviceSetupActivityFragment, such as replacing "MetawearBoard metawear" with a list of such objects (similarly I have a list of SensorFusionBosch objects, and BluetoothDevice). Does this approach make sense in general? My main problem is that I have so little experience with android that I'm not even certain what many of the other methods are doing so it's hard to adjust them to work for multiple boards.

  • @gammonm said:
    Does this approach make sense in general?

    Looks go to me so far

  • edited May 2019

    This ended up being more trouble than its worth so I just wrote my own script. If anyone is trying to do something similar in the future (stream 3 quaternions simultaneously) my code is below. It's very bare bones but gets the job done. Basically it assigns the values to a static variable which is then accessed by an android service and streamed to my unity app. There are certainly much cleaner/more efficient ways to accomplish this but feel free to use my work as a starting point.

  • `public class MainActivity extends Activity implements ServiceConnection {
    private BtleService.LocalBinder serviceBinder;

    private final String MW_MAC_ADDRESS_1 = "C4:F1:0F:4F:7B:3A";
    private final String MW_MAC_ADDRESS_2 = "EB:36:F0:3E:4D:4E";
    private final String MW_MAC_ADDRESS_3 = "CD:12:FA:85:48:31";
    
    private MetaWearBoard board1;
    private MetaWearBoard board2;
    private MetaWearBoard board3;
    private SensorFusionBosch sensorFusion1;
    private SensorFusionBosch sensorFusion2;
    private SensorFusionBosch sensorFusion3;
    
    public static String quat1Data;
    public static String quat2Data;
    public static String quat3Data;
    
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    
        startService(new Intent(this, MyService.class));
    
        getApplicationContext().bindService(new Intent(this, BtleService.class), this, Context.BIND_AUTO_CREATE);
    }
    
    @Override
    public void onDestroy() {
        super.onDestroy();
    
        //disconnect from all 3 boards
    
        board1.disconnectAsync().continueWith(new Continuation<Void, Void>() {
            @Override
            public Void then(Task<Void> task) throws Exception {
                Log.i("MainActivity", "Disconnected 1");
                return null;
            }
        });
    
        board2.disconnectAsync().continueWith(new Continuation<Void, Void>() {
            @Override
            public Void then(Task<Void> task) throws Exception {
                Log.i("MainActivity", "Disconnected 2");
                return null;
            }
        });
    
        board3.disconnectAsync().continueWith(new Continuation<Void, Void>() {
            @Override
            public Void then(Task<Void> task) throws Exception {
                Log.i("MainActivity", "Disconnected 3");
                return null;
            }
        });
    
        // Unbind the service when the activity is destroyed
        getApplicationContext().unbindService(this);
    }
    
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        // Typecast the binder to the service's LocalBinder class
        serviceBinder = (BtleService.LocalBinder) service;
    
        retrieveBoard();
    
        //connect to all 3 boards
        connectBoard(board1,1);
    
        connectBoard(board2,2);
    
        connectBoard(board3,3);
    
    
    
    }
    
    public void connectBoard(MetaWearBoard board,int boardNo){
        board.connectAsync().continueWith(new Continuation<Void, Void>() {
            @Override
            public Void then(Task<Void> task) throws Exception {
                if (task.isFaulted()) {
                    Log.i("MainActivity", "Failed to connect "+boardNo);
                    connectBoard(board,boardNo);
                } else {
                    Log.i("MainActivity", "Connected "+boardNo);
                }
                return null;
            }
        });
    }
    
    public void onClick_Button(View v){
    
        sensorFusion1 = board1.getModule(SensorFusionBosch .class);
        sensorFusion2 = board2.getModule(SensorFusionBosch.class);
        sensorFusion3 = board3.getModule(SensorFusionBosch.class);
    
        sensorFusion1.configure()
                .mode(SensorFusionBosch.Mode.NDOF)
                .accRange(SensorFusionBosch.AccRange.AR_2G)
                .gyroRange(SensorFusionBosch.GyroRange.GR_250DPS)
                .commit();
    
        sensorFusion2.configure()
                .mode(SensorFusionBosch.Mode.NDOF)
                .accRange(SensorFusionBosch.AccRange.AR_2G)
                .gyroRange(SensorFusionBosch.GyroRange.GR_250DPS)
                .commit();
    
        sensorFusion3.configure()
                .mode(SensorFusionBosch.Mode.NDOF)
                .accRange(SensorFusionBosch.AccRange.AR_2G)
                .gyroRange(SensorFusionBosch.GyroRange.GR_250DPS)
                .commit();
    
    
        sensorFusion1.quaternion().addRouteAsync(new RouteBuilder() {
            @Override
            public void configure(RouteComponent source) {
                source.stream(new Subscriber() {
                    @Override
                    public void apply(Data data, Object... env) {
                        //how to send data to the service here??
                        quat1Data = data.value(Quaternion.class).w()+","+data.value(Quaternion.class).x()+","+ data.value(Quaternion.class).y()+","+data.value(Quaternion.class).z();
                        Log.i("data retrieved 1",data.value(Quaternion.class).toString());
    
    
                    }
                });
            }
        }).continueWith(new Continuation<Route, Void>() {
            @Override
            public Void then(Task<Route> task) throws Exception {
                sensorFusion1.quaternion().start();
                sensorFusion1.start();
                return null;
            }
        });
    
        sensorFusion2.quaternion().addRouteAsync(new RouteBuilder() {
            @Override
            public void configure(RouteComponent source) {
                source.stream(new Subscriber() {
                    @Override
                    public void apply(Data data, Object... env) {
                        //how to send data to the service here??
                        quat2Data = data.value(Quaternion.class).w()+","+data.value(Quaternion.class).x()+","+ data.value(Quaternion.class).y()+","+data.value(Quaternion.class).z();
                        Log.i("data retrieved 2",data.value(Quaternion.class).toString());
    
    
                    }
                });
            }
        }).continueWith(new Continuation<Route, Void>() {
            @Override
            public Void then(Task<Route> task) throws Exception {
                sensorFusion2.quaternion().start();
                sensorFusion2.start();
                return null;
            }
        });
    
        sensorFusion3.quaternion().addRouteAsync(new RouteBuilder() {
            @Override
            public void configure(RouteComponent source) {
                source.stream(new Subscriber() {
                    @Override
                    public void apply(Data data, Object... env) {
                        //how to send data to the service here??
                        quat3Data = data.value(Quaternion.class).w()+","+data.value(Quaternion.class).x()+","+ data.value(Quaternion.class).y()+","+data.value(Quaternion.class).z();
                        Log.i("data retrieved 3",data.value(Quaternion.class).toString());
    
    
                    }
                });
            }
        }).continueWith(new Continuation<Route, Void>() {
            @Override
            public Void then(Task<Route> task) throws Exception {
                sensorFusion3.quaternion().start();
                sensorFusion3.start();
                return null;
            }
        });
    
    
    }
    
    @Override
    public void onServiceDisconnected(ComponentName componentName) { }
    
    public void retrieveBoard(){
        final BluetoothManager btManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
        final BluetoothDevice remoteDevice1 = btManager.getAdapter().getRemoteDevice(MW_MAC_ADDRESS_1);
        final BluetoothDevice remoteDevice2 = btManager.getAdapter().getRemoteDevice(MW_MAC_ADDRESS_2);
        final BluetoothDevice remoteDevice3 = btManager.getAdapter().getRemoteDevice(MW_MAC_ADDRESS_3);
    
        board1 = serviceBinder.getMetaWearBoard(remoteDevice1);
        board2 = serviceBinder.getMetaWearBoard(remoteDevice2);
        board3 = serviceBinder.getMetaWearBoard(remoteDevice3);
    }
    

    }`

  • Nice job!

Sign In or Register to comment.