diff --git a/library/src/main/java/com/mbientlab/metawear/data/Activity.java b/library/src/main/java/com/mbientlab/metawear/data/Activity.java new file mode 100644 index 0000000..876d172 --- /dev/null +++ b/library/src/main/java/com/mbientlab/metawear/data/Activity.java @@ -0,0 +1,50 @@ +/* + * Copyright 2014-2015 MbientLab Inc. All rights reserved. + * + * IMPORTANT: Your use of this Software is limited to those specific rights granted under the terms of a software + * license agreement between the user who downloaded the software, his/her employer (which must be your + * employer) and MbientLab Inc, (the "License"). You may not use this Software unless you agree to abide by the + * terms of the License which can be found at www.mbientlab.com/terms. The License limits your use, and you + * acknowledge, that the Software may be modified, copied, and distributed when used in conjunction with an + * MbientLab Inc, product. Other than for the foregoing purpose, you may not use, reproduce, copy, prepare + * derivative works of, modify, distribute, perform, display or sell this Software and/or its documentation for any + * purpose. + * + * YOU FURTHER ACKNOWLEDGE AND AGREE THAT THE SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS" WITHOUT WARRANTY + * OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY, TITLE, + * NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MBIENTLAB OR ITS LICENSORS BE LIABLE OR + * OBLIGATED UNDER CONTRACT, NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, BREACH OF WARRANTY, OR OTHER LEGAL EQUITABLE + * THEORY ANY DIRECT OR INDIRECT DAMAGES OR EXPENSES INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT, + * PUNITIVE OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY, + * SERVICES, OR ANY CLAIMS BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR COSTS. + * + * Should you have any questions regarding your right to use this Software, contact MbientLab via email: + * hello@mbientlab.com. + */ + +package com.mbientlab.metawear.data; + +/** + * Enumeration of activities (available on the BMI270) + * + * @author Simon Tran + */ +public enum Activity { + STILL, + WALKING, + RUNNING, + UNKNOWN; + + public static Activity fromInt(final int value) { + switch (value) { + case 0: + return STILL; + case 1: + return WALKING; + case 2: + return RUNNING; + default: + return UNKNOWN; + } + } +} diff --git a/library/src/main/java/com/mbientlab/metawear/impl/AccelerometerBmi270Impl.java b/library/src/main/java/com/mbientlab/metawear/impl/AccelerometerBmi270Impl.java index 29cb9ac..f2a47ce 100644 --- a/library/src/main/java/com/mbientlab/metawear/impl/AccelerometerBmi270Impl.java +++ b/library/src/main/java/com/mbientlab/metawear/impl/AccelerometerBmi270Impl.java @@ -29,6 +29,8 @@ import com.mbientlab.metawear.Route; import com.mbientlab.metawear.builder.RouteBuilder; import com.mbientlab.metawear.data.Acceleration; +import com.mbientlab.metawear.data.Activity; +import com.mbientlab.metawear.impl.DataPrivate.ClassToObject; import com.mbientlab.metawear.impl.platform.TimedTask; import com.mbientlab.metawear.module.Accelerometer; import com.mbientlab.metawear.module.AccelerometerBmi270; @@ -47,28 +49,41 @@ */ class AccelerometerBmi270Impl extends ModuleImplBase implements AccelerometerBmi270 { static String createUri(DataTypeBase dataType) { - switch (dataType.eventConfig[1]) { - case DATA_INTERRUPT: - return dataType.attributes.length() > 2 ? "acceleration" : String.format(Locale.US, "acceleration[%d]", (dataType.attributes.offset >> 1)); - case PACKED_ACC_DATA: - return "acceleration"; - default: - return null; + switch (Util.clearRead(dataType.eventConfig[1])) { + case DATA_INTERRUPT: + return dataType.attributes.length() > 2 ? "acceleration" : String.format(Locale.US, "acceleration[%d]", (dataType.attributes.offset >> 1)); + case PACKED_ACC_DATA: + return "acceleration"; + case STEP_COUNT_INTERRUPT: + return "step-counter"; + default: + return null; } } private static final long serialVersionUID = 0000000; - final static byte IMPLEMENTATION= 0x4; + final static byte IMPLEMENTATION = 0x4; + private static final String STEP_DETECTOR_PRODUCER = "com.mbientlab.metawear.impl.AccelerometerBmi270Impl.STEP_DETECTOR_PRODUCER", + STEP_COUNTER_PRODUCER = "com.mbientlab.metawear.impl.AccelerometerBmi270Impl.STEP_COUNTER_PRODUCER"; + private static final byte FEATURE_ENABLE = 0x6, + FEATURE_INTERRUPT_ENABLE = 0x7, + FEATURE_CONFIG = 0x8, + STEP_COUNT_INTERRUPT = 0xb; protected static final byte POWER_MODE = 1, - DATA_INTERRUPT_ENABLE = 2, DATA_CONFIG = 3, DATA_INTERRUPT = 4, PACKED_ACC_DATA= 0x5; - protected final static String ACCEL_PRODUCER= "com.mbientlab.metawear.impl.AccelerometerBmi270Impl.ACCEL_PRODUCER", - ACCEL_X_AXIS_PRODUCER= "com.mbientlab.metawear.impl.AccelerometerBmi270Impl.ACCEL_X_AXIS_PRODUCER", - ACCEL_Y_AXIS_PRODUCER= "com.mbientlab.metawear.impl.AccelerometerBmi270Impl.ACCEL_Y_AXIS_PRODUCER", - ACCEL_Z_AXIS_PRODUCER= "com.mbientlab.metawear.impl.AccelerometerBmi270Impl.ACCEL_Z_AXIS_PRODUCER", - ACCEL_PACKED_PRODUCER= "com.mbientlab.metawear.impl.AccelerometerBmi270Impl.ACCEL_PACKED_PRODUCER"; - private final byte[] accDataConfig= new byte[] {(byte) 0xa8, 0x02}; + DATA_INTERRUPT_ENABLE = 2, DATA_CONFIG = 3, DATA_INTERRUPT = 4, PACKED_ACC_DATA = 0x5; + protected final static String ACCEL_PRODUCER = "com.mbientlab.metawear.impl.AccelerometerBmi270Impl.ACCEL_PRODUCER", + ACCEL_X_AXIS_PRODUCER = "com.mbientlab.metawear.impl.AccelerometerBmi270Impl.ACCEL_X_AXIS_PRODUCER", + ACCEL_Y_AXIS_PRODUCER = "com.mbientlab.metawear.impl.AccelerometerBmi270Impl.ACCEL_Y_AXIS_PRODUCER", + ACCEL_Z_AXIS_PRODUCER = "com.mbientlab.metawear.impl.AccelerometerBmi270Impl.ACCEL_Z_AXIS_PRODUCER", + ACCEL_PACKED_PRODUCER = "com.mbientlab.metawear.impl.AccelerometerBmi270Impl.ACCEL_PACKED_PRODUCER"; + private final byte[] accDataConfig = new byte[] { (byte) 0xa8, 0x02 }; + private final static String ACTIVITY_DETECTOR_PRODUCER = "com.mbientlab.metawear.impl.AccelerometerBmi270Impl.ACTIVITY_DETECTOR_PRODUCER"; + private static final byte ACTIVITY_INTERRUPT = 0xc; private transient AsyncDataProducer packedAcceleration, acceleration; + private transient StepDetectorDataProducer stepDetector; + private transient StepCounterDataProducer stepCounter; + private transient AsyncDataProducer activityDetector; private transient TimedTask pullConfigTask; private static class BoschAccCartesianFloatData extends FloatVectorData { @@ -79,7 +94,7 @@ public BoschAccCartesianFloatData() { } public BoschAccCartesianFloatData(byte register, byte copies) { - super(ACCELEROMETER, register, new DataAttributes(new byte[] {2, 2, 2}, copies, (byte) 0, true)); + super(ACCELEROMETER, register, new DataAttributes(new byte[] { 2, 2, 2 }, copies, (byte) 0, true)); } public BoschAccCartesianFloatData(DataTypeBase input, Constant.Module module, byte register, byte id, DataAttributes attributes) { @@ -93,7 +108,7 @@ public DataTypeBase copy(DataTypeBase input, Constant.Module module, byte regist @Override public DataTypeBase[] createSplits() { - return new DataTypeBase[] {new BoschAccSFloatData((byte) 0), new BoschAccSFloatData((byte) 2), new BoschAccSFloatData((byte) 4)}; + return new DataTypeBase[] { new BoschAccSFloatData((byte) 0), new BoschAccSFloatData((byte) 2), new BoschAccSFloatData((byte) 4) }; } @Override @@ -104,9 +119,9 @@ protected float scale(MetaWearBoardPrivate mwPrivate) { @Override public Data createMessage(boolean logData, MetaWearBoardPrivate mwPrivate, final byte[] data, final Calendar timestamp, DataPrivate.ClassToObject mapper) { ByteBuffer buffer = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN); - short[] unscaled = new short[]{buffer.getShort(), buffer.getShort(), buffer.getShort()}; - final float scale= scale(mwPrivate); - final Acceleration value= new Acceleration(unscaled[0] / scale, unscaled[1] / scale, unscaled[2] / scale); + short[] unscaled = new short[] { buffer.getShort(), buffer.getShort(), buffer.getShort() }; + final float scale = scale(mwPrivate); + final Acceleration value = new Acceleration(unscaled[0] / scale, unscaled[1] / scale, unscaled[2] / scale); return new DataPrivate(timestamp, data, mapper) { @Override @@ -116,26 +131,28 @@ public float scale() { @Override public Class[] types() { - return new Class[] {Acceleration.class, float[].class}; + return new Class[] { Acceleration.class, float[].class }; } @Override public T value(Class clazz) { if (clazz.equals(Acceleration.class)) { return clazz.cast(value); - } else if (clazz.equals(float[].class)) { - return clazz.cast(new float[] {value.x(), value.y(), value.z()}); + } + else if (clazz.equals(float[].class)) { + return clazz.cast(new float[] { value.x(), value.y(), value.z() }); } return super.value(clazz); } }; } } + private static class BoschAccSFloatData extends SFloatData { private static final long serialVersionUID = 0000000; BoschAccSFloatData(byte offset) { - super(ACCELEROMETER, DATA_INTERRUPT, new DataAttributes(new byte[] {2}, (byte) 1, offset, true)); + super(ACCELEROMETER, DATA_INTERRUPT, new DataAttributes(new byte[] { 2 }, (byte) 1, offset, true)); } BoschAccSFloatData(DataTypeBase input, Constant.Module module, byte register, byte id, DataAttributes attributes) { @@ -153,17 +170,50 @@ public DataTypeBase copy(DataTypeBase input, Constant.Module module, byte regist } } + + private static class Bmi270ActivityData extends UintData { + public Bmi270ActivityData() { + super(ACCELEROMETER, ACTIVITY_INTERRUPT, new DataAttributes(new byte[] { 1 }, (byte) 1, (byte) 0, false)); + } + + @Override + public Data createMessage(boolean logData, MetaWearBoardPrivate mwPrivate, byte[] data, Calendar timestamp, ClassToObject mapper) { + final Activity activity = Activity.fromInt(data[0] >> 1); + + return new DataPrivate(timestamp, data, mapper) { + @Override + public T value(final Class clazz) { + if (clazz == Activity.class) { + return clazz.cast(activity); + } + return super.value(clazz); + } + + @Override + public Class[] types() { + return new Class[] { Activity.class }; + } + }; + } + } + + AccelerometerBmi270Impl(MetaWearBoardPrivate mwPrivate) { super(mwPrivate); DataTypeBase cfProducer = new BoschAccCartesianFloatData(); - this.mwPrivate= mwPrivate; + this.mwPrivate = mwPrivate; this.mwPrivate.tagProducer(ACCEL_PRODUCER, cfProducer); this.mwPrivate.tagProducer(ACCEL_X_AXIS_PRODUCER, cfProducer.split[0]); this.mwPrivate.tagProducer(ACCEL_Y_AXIS_PRODUCER, cfProducer.split[1]); this.mwPrivate.tagProducer(ACCEL_Z_AXIS_PRODUCER, cfProducer.split[2]); this.mwPrivate.tagProducer(ACCEL_PACKED_PRODUCER, new BoschAccCartesianFloatData(PACKED_ACC_DATA, (byte) 3)); + this.mwPrivate.tagProducer(STEP_DETECTOR_PRODUCER, new UintData(ACCELEROMETER, STEP_COUNT_INTERRUPT, + new DataAttributes(new byte[] { 1 }, (byte) 1, (byte) 0, false))); + this.mwPrivate.tagProducer(STEP_COUNTER_PRODUCER, new UintData(ACCELEROMETER, Util.setSilentRead(STEP_COUNT_INTERRUPT), + new DataAttributes(new byte[] { 2 }, (byte) 1, (byte) 0, false))); + this.mwPrivate.tagProducer(ACTIVITY_DETECTOR_PRODUCER, new Bmi270ActivityData()); } @Override @@ -179,19 +229,19 @@ protected float getAccDataScale() { @Override public AccelerometerBmi270.ConfigEditor configure() { return new AccelerometerBmi270.ConfigEditor() { - private OutputDataRate odr= OutputDataRate.ODR_100_HZ; - private AccRange ar= AccRange.AR_8G; + private OutputDataRate odr = OutputDataRate.ODR_100_HZ; + private AccRange ar = AccRange.AR_8G; private FilterMode mode = FilterMode.NORMAL; @Override public AccelerometerBmi270.ConfigEditor odr(OutputDataRate odr) { - this.odr= odr; + this.odr = odr; return this; } @Override public AccelerometerBmi270.ConfigEditor range(AccRange ar) { - this.ar= ar; + this.ar = ar; return this; } @@ -213,14 +263,14 @@ public AccelerometerBmi270.ConfigEditor range(float fsr) { @Override public void commit() { - accDataConfig[0]&= 0xf0; - accDataConfig[0]|= odr.ordinal() + 1; + accDataConfig[0] &= 0xf0; + accDataConfig[0] |= odr.ordinal() + 1; - accDataConfig[0]&= 0x0f; - accDataConfig[0]|= 0xa0; //TODO + accDataConfig[0] &= 0x0f; + accDataConfig[0] |= 0xa0; //TODO - accDataConfig[1]&= 0xf0; - accDataConfig[1]|= ar.bitmask; + accDataConfig[1] &= 0xf0; + accDataConfig[1] |= ar.bitmask; mwPrivate.sendCommand(ACCELEROMETER, DATA_CONFIG, accDataConfig); } @@ -243,12 +293,12 @@ public String name() { @Override public void start() { - mwPrivate.sendCommand(new byte[] {ACCELEROMETER.id, DATA_INTERRUPT_ENABLE, 0x01, 0x00}); + mwPrivate.sendCommand(new byte[] { ACCELEROMETER.id, DATA_INTERRUPT_ENABLE, 0x01, 0x00 }); } @Override public void stop() { - mwPrivate.sendCommand(new byte[] {ACCELEROMETER.id, DATA_INTERRUPT_ENABLE, 0x00, 0x01}); + mwPrivate.sendCommand(new byte[] { ACCELEROMETER.id, DATA_INTERRUPT_ENABLE, 0x00, 0x01 }); } }; } @@ -271,12 +321,12 @@ public String name() { @Override public void start() { - mwPrivate.sendCommand(new byte[] {ACCELEROMETER.id, DATA_INTERRUPT_ENABLE, 0x01, 0x00}); + mwPrivate.sendCommand(new byte[] { ACCELEROMETER.id, DATA_INTERRUPT_ENABLE, 0x01, 0x00 }); } @Override public void stop() { - mwPrivate.sendCommand(new byte[] {ACCELEROMETER.id, DATA_INTERRUPT_ENABLE, 0x00, 0x01}); + mwPrivate.sendCommand(new byte[] { ACCELEROMETER.id, DATA_INTERRUPT_ENABLE, 0x00, 0x01 }); } @Override @@ -311,7 +361,7 @@ public float getRange() { @Override public Task pullConfigAsync() { return pullConfigTask.execute("Did not receive BMI270 acc config within %dms", Constant.RESPONSE_TIMEOUT, - () -> mwPrivate.sendCommand(new byte[] {ACCELEROMETER.id, Util.setRead(DATA_CONFIG)}) + () -> mwPrivate.sendCommand(new byte[] { ACCELEROMETER.id, Util.setRead(DATA_CONFIG) }) ).onSuccessTask(task -> { System.arraycopy(task.getResult(), 2, accDataConfig, 0, accDataConfig.length); return Task.forResult(null); @@ -320,12 +370,164 @@ public Task pullConfigAsync() { @Override public void start() { - mwPrivate.sendCommand(new byte[] {ACCELEROMETER.id, POWER_MODE, 0x01}); + mwPrivate.sendCommand(new byte[] { ACCELEROMETER.id, POWER_MODE, 0x01 }); } @Override public void stop() { - mwPrivate.sendCommand(new byte[] {ACCELEROMETER.id, POWER_MODE, 0x00}); + mwPrivate.sendCommand(new byte[] { ACCELEROMETER.id, POWER_MODE, 0x00 }); + } + + private class StepConfigEditorInner implements StepConfigEditor { + private final byte STEP_COUNTER_3_INDEX = 0x7; + byte param_250 = 0x00; // bit 0-7 + byte param_251 = 0x00; // bit 8-15 + byte watermark_level_0 = 0x00; // bit 0-7 + byte watermark_level_1 = 0b00; // bit 0-1 + byte reset_counter = 0b0; // bit 2 + + private byte[] getBitmap() { + int watermark_level_1_shifted = watermark_level_1 << 5; + int reset_counter_shifted = reset_counter << 4; + + return new byte[] { + param_250, + param_251, + watermark_level_0, + (byte) (watermark_level_1_shifted | reset_counter_shifted) + }; + } + + @Override + public StepConfigEditor trigger(final int trigger) { + if (trigger >= 1 && trigger <= 1023) { + int lower_8 = trigger & 0x00ff; + int higher_2 = (trigger & 0x0300) >> 8; + watermark_level_0 = (byte) lower_8; + watermark_level_1 = (byte) higher_2; + } + else { + watermark_level_0 = 0x00; + watermark_level_1 = 0b00; + } + + return this; + } + + @Override + public void commit() { + mwPrivate.sendCommand(ACCELEROMETER, FEATURE_CONFIG, STEP_COUNTER_3_INDEX, getBitmap()); + } } + @Override + public StepDetectorDataProducer stepDetector() { + if (stepDetector == null) { + stepDetector = new StepDetectorDataProducer() { + @Override + public StepConfigEditor configure() { + return new StepConfigEditorInner(); + } + + @Override + public Task addRouteAsync(RouteBuilder builder) { + return mwPrivate.queueRouteBuilder(builder, STEP_DETECTOR_PRODUCER); + } + + @Override + public String name() { + return STEP_DETECTOR_PRODUCER; + } + + @Override + public void start() { + mwPrivate.sendCommand(new byte[] { ACCELEROMETER.id, FEATURE_INTERRUPT_ENABLE, (byte) 0x80, (byte) 0x00 }); + mwPrivate.sendCommand(new byte[] { ACCELEROMETER.id, FEATURE_ENABLE, (byte) 0x80, (byte) 0x00 }); + } + + @Override + public void stop() { + mwPrivate.sendCommand(new byte[] { ACCELEROMETER.id, FEATURE_INTERRUPT_ENABLE, (byte) 0x00, (byte) 0x80 }); + mwPrivate.sendCommand(new byte[] { ACCELEROMETER.id, FEATURE_ENABLE, (byte) 0x00, (byte) 0x80 }); + } + }; + } + return stepDetector; + } + + @Override + public StepCounterDataProducer stepCounter() { + if (stepCounter == null) { + stepCounter = new StepCounterDataProducer() { + private StepConfigEditorInner configEditor = new StepConfigEditorInner(); + + @Override + public StepConfigEditor configure() { + configEditor = new StepConfigEditorInner(); + return configEditor; + } + + @Override + public void reset() { + configEditor.reset_counter = 0b1; + configEditor.commit(); + configEditor.reset_counter = 0b0; + } + + @Override + public Task addRouteAsync(RouteBuilder builder) { + return mwPrivate.queueRouteBuilder(builder, STEP_COUNTER_PRODUCER); + } + + @Override + public String name() { + return STEP_COUNTER_PRODUCER; + } + + @Override + public void start() { + mwPrivate.sendCommand(new byte[] { ACCELEROMETER.id, FEATURE_INTERRUPT_ENABLE, (byte) 0x02, (byte) 0x00 }); + mwPrivate.sendCommand(new byte[] { ACCELEROMETER.id, FEATURE_ENABLE, (byte) 0x02, (byte) 0x00 }); + } + + @Override + public void stop() { + mwPrivate.sendCommand(new byte[] { ACCELEROMETER.id, FEATURE_INTERRUPT_ENABLE, (byte) 0x00, (byte) 0x02 }); + mwPrivate.sendCommand(new byte[] { ACCELEROMETER.id, FEATURE_ENABLE, (byte) 0x00, (byte) 0x02 }); + } + }; + } + return stepCounter; + } + + @Override + public AsyncDataProducer activityDetector() { + if (activityDetector == null) { + activityDetector = new AsyncDataProducer() { + @Override + public String name() { + return ACTIVITY_DETECTOR_PRODUCER; + } + + @Override + public Task addRouteAsync(final RouteBuilder builder) { + return mwPrivate.queueRouteBuilder(builder, ACTIVITY_DETECTOR_PRODUCER); + } + + @Override + public void start() { + mwPrivate.sendCommand(new byte[] { ACCELEROMETER.id, FEATURE_INTERRUPT_ENABLE, (byte) 0x04, (byte) 0x00 }); + mwPrivate.sendCommand(new byte[] { ACCELEROMETER.id, FEATURE_ENABLE, (byte) 0x04, (byte) 0x00 }); + } + + @Override + public void stop() { + mwPrivate.sendCommand(new byte[] { ACCELEROMETER.id, FEATURE_INTERRUPT_ENABLE, (byte) 0x00, (byte) 0x04 }); + mwPrivate.sendCommand(new byte[] { ACCELEROMETER.id, FEATURE_ENABLE, (byte) 0x00, (byte) 0x04 }); + } + }; + } + + return activityDetector; + } } diff --git a/library/src/main/java/com/mbientlab/metawear/module/AccelerometerBmi270.java b/library/src/main/java/com/mbientlab/metawear/module/AccelerometerBmi270.java index 0c2247c..f73d1f9 100644 --- a/library/src/main/java/com/mbientlab/metawear/module/AccelerometerBmi270.java +++ b/library/src/main/java/com/mbientlab/metawear/module/AccelerometerBmi270.java @@ -27,13 +27,8 @@ import com.mbientlab.metawear.AsyncDataProducer; import com.mbientlab.metawear.ConfigEditorBase; import com.mbientlab.metawear.Configurable; -import com.mbientlab.metawear.data.CartesianAxis; -import com.mbientlab.metawear.data.Sign; -import com.mbientlab.metawear.data.TapType; -import java.util.Arrays; import java.util.HashMap; -import java.util.Locale; /** * Extension of the {@link Accelerometer} interface providing finer control of the BMI270 accelerometer @@ -168,4 +163,53 @@ interface ConfigEditor extends Accelerometer.ConfigEditor { */ @Override ConfigEditor configure(); + + /** + * Configuration editor for the step detection algorithm + */ + interface StepConfigEditor extends ConfigEditorBase { + /** + * Sets the watermark level of the step counter The Step-counter will trigger output every time this number of steps are counted. + * Holds implicitly a 20x factor, so the range is 0 to 20460, with resolution of 20 steps. If 0, the output is disabled. If 1, it will count to 20 steps. + * + * @param trigger Number of steps + * @return Calling object + */ + StepConfigEditor trigger(int trigger); + /** + * Write the configuration to the sensor + */ + void commit(); + } + /** + * Interrupt driven step detection where each detected step triggers a data interrupt. This data producer + * cannot be used in conjunction with the {@link StepCounterDataProducer} interface. + */ + interface StepDetectorDataProducer extends AsyncDataProducer, Configurable { } + /** + * Get an implementation of the StepDetectorDataProducer interface + * @return StepDetectorDataProducer object + */ + StepDetectorDataProducer stepDetector(); + /** + * Accumulates the number of detected steps in a counter that will send its current value on request. This + * data producer cannot be used in conjunction with the {@link StepDetectorDataProducer} interface. + */ + interface StepCounterDataProducer extends AsyncDataProducer, Configurable { + /** + * Reset the internal step counter + */ + void reset(); + } + /** + * Get an implementation of the StepCounterDataProducer interface + * @return StepCounterDataProducer object + */ + StepCounterDataProducer stepCounter(); + + /** + * Get an implementation of the AsyncDataProducer interface for detected activity + * @return AsyncDataProducer Object for detected activity + */ + AsyncDataProducer activityDetector(); }