Trouble handling motion events

edited June 2016 in Android
When the board is connected to the phone, then I have no problem handling motion events, e.g. turning on the LED when motion is detected then listening for no motion. However, I would like this to happen even when the phone is not connected. The following code won't work if the board isn't connected with the phone. Any ideas?
motionModule.routeData().fromMotion().monitor(new DataSignal.ActivityHandler() {
    @Override
public void onSignalActive(Map<String, DataProcessor> map, DataSignal.DataToken dataToken) {
//startSensors();
//settingsModule.startAdvertisement();
//mwBoard.connect();
turnOnLed(Led.ColorChannel.BLUE);
Log.d(TAG, "MOTION DETECTED");
//beaconModule.configure().setAdPeriod(advertisementPeriod).commit();
//beaconModule.enable();
startNoMotionDetection();
}
}).commit();
motionModule.enableMotionDetection(Bmi160Accelerometer.MotionType.ANY_MOTION);
motionModule.configureAnyMotionDetection().setThreshold(0.1f).commit();
motionModule.startLowPower();

Comments

  • Which part of the code doesn't work and what does "startNoMotionDetection" do?
  • edited June 2016
    You can ignore startNoMotionDetection - I get the same issue when I comment it out.

    But let me make a correction. The LED will turn on when I call turnOnLed() while connected to the phone. But the LED will not turn on from this call, whether or not I'm connected to the device. The log "MOTION DETECTED" occurs not when motion is detected, it seems, but a few seconds after the motionModule is set up. I'm not entirely sure if I really understand the semantics of monitor().
  • edited June 2016
    What's interesting and possibly relevant is that I took the example code from the Routing Sensor Data documentation at https://mbientlab.com/androiddocs/latest/routing_sensor_data.html. The LED turns on when I call the code that is in the onSignalActive() method elsewhere. But pressing the switch does not turn on the LED. I also added a debug log in the onSignalActive() method and it got called twice (but not for any other switch presses); both times the LED did not turn on.
  • I was able to get the example code working after a soft reset. By using stream() instead of monitor() I'm able to get the behavior I want, but only until I disconnect. I thought that was why I should have used monitor, but it does not work.


  • edited June 2016
    This goes back to my earlier question of "What does 'startNoMotion' do?"  An activity monitor only executes MetaWear specific commands; it is limited in what it can do as the MetaWear is responding to some activity on board, not on your mobile device.
  • I see. startNoMotionDetection waits for no motion to occur and calls startMotionDetection which is the problem. But I need the board to react both to motion and no motion events, even when it is not connected. Is that possible?
  • It seems that I can enable both NO_MOTION and ANY_MOTION detection, but I must handle both in the same message handler, because they both require the enableMotionDetection() method. I don't know how to interpret the data values in the messages I am getting. And I don't get any message after 5 seconds of no motion, even though I configure it as (I've also tried several thresholds):

    motionModule.configureNoMotionDetection().setThreshold(0.01f).setDuration(5000).commit();
  • You can mimic an if-else branch by using two passthrough filters (P1, P2) in conditional mode where only one permits data through.  Assuming P1 represents no motion, when data passes through, it will disable itself, enable P2, and switch to any motion detection.  P2 will do the opposite and switch back to no motion detection.

    Nothing looks out of place with your no motion config though the threshold is a bit low given accelerometer drift.  You can try the code snippet here to see if it works for you.
  • That's a really good idea! Thanks Eric
  • edited June 2016
    I think that I've correctly implemented what you suggested using branches and passthroughs to emulate an if else and setting each passthrough value according to whether we want to detect motion or no motion. The problem I'm having is (1) void success(RouteManager result) is never called and (2) I get the logs "NO MOTION DETECTED" and "MOTION DETECTED" in that order once connected, and only one time and not in response to any motion/no motion. The LED never turns on.

    motionModule.routeData().fromMotion().split()
    .branch().process("no_motion_cond_gate", new Passthrough(Passthrough.Mode.CONDITIONAL, (short) 1))
    .monitor(new DataSignal.ActivityHandler() {
    @Override
    public void onSignalActive(Map<String, DataProcessor> map, DataSignal.DataToken dataToken) {
    Log.d(TAG, "NO MOTION DETECTED");
    turnOnLed(Led.ColorChannel.RED);

    motionModule.stop();
    motionModule.disableMotionDetection();

    map.get("motion_cond_gate").setState(new Passthrough.State((short) 1));
    map.get("no_motion_cond_gate").setState(new Passthrough.State((short) 0));

    motionModule.enableMotionDetection(Bmi160Accelerometer.MotionType.ANY_MOTION);
    motionModule.configureAnyMotionDetection().setThreshold(0.2f).commit();
    motionModule.startLowPower();
    }
    })
    .branch().process("motion_cond_gate", new Passthrough(Passthrough.Mode.CONDITIONAL, (short) 0))
    .monitor(new DataSignal.ActivityHandler() {
    @Override
    public void onSignalActive(Map<String, DataProcessor> map, DataSignal.DataToken dataToken) {
    Log.d(TAG, "MOTION DETECTED");
    turnOnLed(Led.ColorChannel.GREEN);

    motionModule.stop();
    motionModule.disableMotionDetection();

    map.get("motion_cond_gate").setState(new Passthrough.State((short) 0));
    map.get("no_motion_cond_gate").setState(new Passthrough.State((short) 1));


    motionModule.enableMotionDetection(Bmi160Accelerometer.MotionType.NO_MOTION);
    motionModule.configureNoMotionDetection().setThreshold(0.1f).setDuration(5000).commit();
    motionModule.startLowPower();
    }
    }).end()
    .commit()
    .onComplete(new AsyncOperation.CompletionHandler<RouteManager>() {
    @Override
    public void success(RouteManager result) {
    motionModule.enableMotionDetection(Bmi160Accelerometer.MotionType.ANY_MOTION);
    motionModule.configureAnyMotionDetection().setThreshold(0.2f).commit();
    motionModule.startLowPower();
    }
    });
  • It seems that if I use stream instead of monitor, then the void success(RouteManager result) method is properly called and I can respond to motion/no motions events, but I cannot respond to both because I cannot change the passthrough state, since I don't have access to DataProcessor map unless I use monitor.

    Also monitor seems like the logical choice since I don't have to stream the data to the phone. I think there could possibly be a bug with the monitor and motion detection behavior?
  • I have a workaround below that might suffice. The only issue I have with it is that I can't start the accelerometer in low power. Using an oscilloscope I do see a bit of power savings when I use startLowPower() in Bmi160Accelerometer as opposed to using startPower() in Accelerometer. But it does work at least!
    accModule.routeData().fromAxes()
    .process(new Rss())
    .process(new Maths(Maths.Operation.SUBTRACT, 1))
    .process(new Maths(Maths.Operation.ABS_VALUE, 0))
    .split()
    .branch()
    .process(new Comparison(Comparison.Operation.GT, 0.025))
    .monitor(new DataSignal.ActivityHandler() {
    @Override
    public void onSignalActive(Map<String, DataProcessor> map, DataSignal.DataToken dataToken) {
    turnOnLed(Led.ColorChannel.GREEN);
    }
    })
    .branch()
    .process(new Average((byte) 127))
    .process(new Comparison(Comparison.Operation.LT, 0.013))
    .monitor(new DataSignal.ActivityHandler() {
    @Override
    public void onSignalActive(Map<String, DataProcessor> map, DataSignal.DataToken dataToken) {
    turnOnLed(Led.ColorChannel.RED);
    }
    })
    .end().commit()
    .onComplete(new AsyncOperation.CompletionHandler<RouteManager>() {
    @Override
    public void success(RouteManager result) {
    accModule.enableAxisSampling();
    accModule.start();
    }
  • edited June 2016
    I was able to implement the no/any motion switching using the counter and maths processor to simulate the if-else instead of the passthrough processors.

    acc.routeData().fromMotion().process(new Counter()).process(new Maths(Maths.Operation.MODULUS, 2)).split()
    .branch()
    .process(new Comparison(Comparison.Operation.EQ, 0)).monitor(new ActivityHandler() {
    @Override
    public void onSignalActive(Map processors, DataToken token) {
    led.stop(true);
    led.configureColorChannel(Led.ColorChannel.GREEN)
    .setHighIntensity((byte) 31)
    .setHighTime((short) 1000)
    .setRepeatCount((byte) 1)
    .commit();
    led.play(false);

    acc.stop();
    acc.disableMotionDetection();
    // Configure any motion
    acc.configureAnyMotionDetection().setDuration(10).commit();
    acc.enableMotionDetection(Bmi160Accelerometer.MotionType.ANY_MOTION);
    acc.start();
    }
    })
    .branch()
    .process(new Comparison(Comparison.Operation.EQ, 1)).monitor(new ActivityHandler() {
    @Override
    public void onSignalActive(Map processors, DataToken token) {
    led.stop(true);
    led.configureColorChannel(Led.ColorChannel.BLUE)
    .setHighIntensity((byte) 31)
    .setHighTime((short) 1000)
    .setRepeatCount((byte) 1)
    .commit();
    led.play(false);

    acc.stop();
    acc.disableMotionDetection();
    // Configure no motion
    acc.configureNoMotionDetection().setDuration(5000).commit();
    acc.enableMotionDetection(Bmi160Accelerometer.MotionType.NO_MOTION);
    acc.start();
    }
    })
    .end()
    .commit().onComplete(new CompletionHandler() {
    @Override
    public void success(RouteManager result) {
    // Start with any motion
    acc.configureAnyMotionDetection().setDuration(10).commit();
    acc.enableMotionDetection(Bmi160Accelerometer.MotionType.ANY_MOTION);
    acc.start();
    }
    });
  • That's a clever idea. Thank you so much Eric, it works perfectly!
This discussion has been closed.