Skip to content
This repository has been archived by the owner on Aug 5, 2022. It is now read-only.

[Sensor] Initial implementation of the Generic Sensor API #353

Merged
merged 9 commits into from
Nov 14, 2016

Conversation

jimmy-huang
Copy link
Contributor

Initial implementation for the W3C Generic Sensor API and add
support for the Accelerometer and Gyroscope sensors using
the BMI160 inertia sensor built-in on the Arduino 101,
the current implementation is polling from the ARC and channeling
the data back to X86.

Also adding two sample JS code testing both APIs.

Signed-off-by: Jimmy Huang [email protected]

@@ -322,6 +324,7 @@ static void handle_glcd(struct zjs_ipm_message* msg)
glcd = device_get_binding(GROVE_LCD_NAME);

if (!glcd) {
PRINT("failed to initialize Grove LCD\n");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems unrelated

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I forgot to add it to the glcd when I am implementing sensor, so I am thinking to include it as part of the patch, but I'll remove it and throw in another patch.


if (old_state == SENSOR_STATE_ACTIVATING &&
state == SENSOR_STATE_ACTIVATED) {
func = zjs_get_property(obj, "onactivate");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great that you are actively following the spec, this was just added two days ago :-)

w3c/sensors@a85830b


static struct nano_sem sensor_sem;

enum sensor_state {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe these might become internals: Check

w3c/sensors#104 (comment)

(that part is not fixed in spec yet, ie the checkbox isnt checked yet)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We discussed these changes back in the W3C TPAC meeting in Lisbon and they are slowly making it into the spec

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might do away with "states" (internal slots, so not exposed to developers) , but right now its not in the spec yet.

// For now, frequency is ignored, we just report new event
// as soon as we detect a value change.
if (option_freq <= 0) {
PRINT("zjs_sensor_create: invalid frequency, default to 50hz\n");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

defaulting

bool option_gravity;
if (zjs_obj_get_boolean(options, "includeGravity", &option_gravity) &&
option_gravity) {
// TODO: find out if BMI160 can be configured to include gravity
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the math to add it manually wouldn't be too hard

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's one possible way to do it

#endif

#ifdef BUILD_MODULE_SENSOR
struct sensor_data {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For external sensors (ie DHT22 etc) we would probably need something like "controller" here so that I can do

new Hygrometer( { controller: "dht22" } ) which would hook up to the right Zephyr driver

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I was just thinking the same, since I need to add the ambient light sensor later, and if sensor is external, then I might need to pass in like the I/O pin it's connected to as well.

@@ -104,6 +104,11 @@ if [ $? -eq 0 ] && [[ $MODULE != *"BUILD_MODULE_BUFFER"* ]]; then
>&2 echo Using module: Buffer
MODULES+=" -DBUILD_MODULE_BUFFER"
fi
sensor=$(grep -E AccelerometerSensor\|Gyroscope $SCRIPT)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought the Sensor part was being removed... I will ask Riju to look at this PR

Copy link

@riju riju Nov 3, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we have removed the "sensor" part. https://w3c.github.io/accelerometer/#accelerometer

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I'll update accordingly, I was wondering that too since it's odd that the one has, but one does not.

// Test code to use the AccelerometerSensor (subclass of Generic Sensor) API
// to communicate with the BMI160 inertia sensor on the Arduino 101
// and obtaining information about acceleration applied to the X, Y and Z axis
console.log("Acclerometer test...");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spelling error

// Test code to use the Gyroscope (subclass of Generic Sensor) API
// to communicate with the BMI160 inertia sensor on the Arduino 101
// and monitor the rate of rotation around the the X, Y and Z axis
print("Gyroscope test...");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Riju, do you have tests from Blink that we can reuse?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have the AmbientLightSensor tests here -> https://cs.chromium.org/chromium/src/third_party/WebKit/LayoutTests/sensor/ambient-light-sensor.html?q=ambient&sq=package:chromium&l=1

Gyro and Accelerometer test cases will be upstreamed along with their implementation soon.


sensor.onchange = function(event) {
print("rotation (rad/s): " +
" x=" + event.reading.rotationRateX +
Copy link

@riju riju Nov 3, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rotationRateX is now x, changed recently
https://w3c.github.io/gyroscope/#gyroscopereading

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll update accordingly.

jerry_value_t z_val = jerry_create_number(z);
jerry_value_t reading_obj = jerry_create_object();
if (channel == SENSOR_CHAN_ACCEL_ANY) {
zjs_set_property(reading_obj, "accelerationX", x_val);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

accelerationX -> x, changed recently.
https://w3c.github.io/accelerometer/#accelerometerreading

zjs_set_property(reading_obj, "accelerationY", y_val);
zjs_set_property(reading_obj, "accelerationZ", z_val);
} else if (channel == SENSOR_CHAN_GYRO_ANY) {
zjs_set_property(reading_obj, "rotationRateX", x_val);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here


sensor.onchange = function(event) {
console.log("acceleration (m/s^2): " +
" x=" + event.reading.accelerationX +
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

accelerationX -> x , changed recently
https://w3c.github.io/accelerometer/#accelerometerreading

};

sensor.onerrorchange = function(error) {
console.log("error: " + error);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit : sensor.onerror = event => console.log(event.error.name, event.error.message);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a limitation of the jerryscript engine, it doesn't support this syntax, it's ES6 right? It only supports ES5 for now.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good to know that. iirc, its better to use event, instead of error object. not sure, right now.

sensor.onerror = function(event) {
   console.log(event.error.name, event.error.message);

console.log("Acclerometer test...");


var sensor = new AccelerometerSensor({
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AccelerometerSensor -> Accelerometer
Just a heads up, very soon, we will split the Accelerometer interface to 2 interfaces, one for Accelerometer and another for LinearAccelerometer. In that case includeGravity will not be needed. You may want to change this when it is reflected in the spec.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I'll keep that in mind, thanks for the heads up.

console.log("state: " + event);
};

sensor.onerrorchange = function(error) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sensor.onerror = event => console.log(event.error.name, event.error.message);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my comment above.

#define TYPE_SENSOR_START 0x0031
#define TYPE_SENSOR_STOP 0x0032
#define TYPE_SENSOR_EVENT_STATE_CHANGE 0x0033
#define TYPE_SENSOR_EVENT_VALUE_CHANGE 0x0034
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit : TYPE_SENSOR_EVENT_READING_CHANGE ? we have this notion of SensorReading.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's fine with me

struct sensor_data {
enum sensor_channel channel;
uint32_t frequency;
double value[3];
Copy link

@riju riju Nov 3, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you prefer it to split this struct into 2 ? Like we have for SensorReading (frequency&timestamp) as base struct, and say concreteSensor's reading (which contains the actual value) like https://w3c.github.io/gyroscope/#gyroscopereading.
Also no timestamp ? like performance.now() equivalent .

When you implement the next sensor, say AmbientLightSensor/Proximity , there is only 1 double for "value".
If you implement Uncalibrated Magnetometer or Uncalibrated Gyro, then double value[6].

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok yeah, this is the internal implementation, and only specific to Arduino 101 where we have to channel the data, so we can fix it up however we want, but that's a good idea.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@riju I updated patch to use a union to hold the reading values depending on the sensor type

int32_t id;
enum sensor_channel channel;
enum sensor_state state;
double sensor_value[3];
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fine for now, but not generic enough. See 294d36b#r86344326

@haoxli haoxli mentioned this pull request Nov 7, 2016
@jimmy-huang jimmy-huang force-pushed the sensor-api branch 3 times, most recently from e200030 to 968b8ac Compare November 7, 2016 23:04
Copy link
Contributor

@grgustaf grgustaf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've gone through 2/3, I'll give you this much so far. :)

CONFIG_BMI160_NAME="bmi160"
CONFIG_BMI160_SPI_PORT_NAME="SPI_1"
CONFIG_BMI160_SLAVE=1
CONFIG_BMI160_SPI_BUS_FREQ=88
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jimmy-huang, @jprestwo: This reminds me, we should do something like analyze.sh for arc too. However, maybe we should have two things we can do:

  • build arc generically with all features enabled, so you don't have to keep updating, and
  • build arc optimally for a particular application

We could have a make arc-generic as opposed to make arc perhaps. Or maybe make arc continues to be generic, but make all does the optimized arc version, that's kinda cool.

If ARC is completely unused by an app, we could even disable ARC entirely. I wonder if that saves power?

(Not requesting this in this patch, but you could look at it for a follow-on.)

@@ -388,6 +388,250 @@ static void handle_glcd(struct zjs_ipm_message* msg)
ipm_send_reply(msg);
}

#ifndef DEBUG_BUILD
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you meant #ifdef? This is used only in DEBUG_BUILD below AFAICT. Bad Jimmy, didn't build with DEBUG! :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, lol

zjs_ipm_send(MSG_ID_SENSOR, &msg);
}

#define ABS(x) ((x) > 0) ? (x) : -(x)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

>= perhaps?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, i'll change it

result = (double)val2 * (-0.000001);
} else {
result = val1 + (double)val2 * (-0.000001);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens in this block if val1 > 0 and val2 < 0, doesn't it do the wrong thing?

It kind of seems like this logic could be simplified to be more obviously correct. I'm still not sure what it's doing but will wait for your response to this before digging deeper. :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if val1 > 0, then it falls into the first case, and if val1 > 0, then val2 can't be < 0 as the sign bit is in val1. But anyway, this is taken from the the zephyr sample how they checked.

dval[2] = convert_sensor_value(&val[2]);

if (dval[0] == 0 && dval[1] == 0 && dval[2] == 0) {
// FIXME: BUG? why sometimes it reports 0, 0, 0 on all axis
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

axis -> axes. Did you report that to Zephyr folks? Maybe open a JIRA?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this will be fixed with the trigger, that's why I wasn't looking into this.


if (ABS(dval[0] - gyro_last_value[0]) > 0 ||
ABS(dval[1] - gyro_last_value[1]) > 0 ||
ABS(dval[2] - gyro_last_value[2]) > 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If your threshold for difference is 0, then isn't this the same as dval[0] != gyro_last_value[0]?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, i was going to add a threshold here intead of 0, but I don't really know what value to put in for now, so i left it as 0. I'll update it.

#endif
#ifdef BUILD_MODULE_SENSOR
#define MSG_ID_SENSOR 0x04
#endif
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can go ahead and define all of these all the time, without the ifdefs. And actually I think elsewhere you use them without being in an ifdef so that would break the build anyway. Maybe review your usage of MSG_ID_* everywhere to make sure it makes sense.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, i'll revert them.

#define TYPE_AIO_OPEN 0x0000
#define TYPE_AIO_PIN_READ 0x0001
#define TYPE_AIO_PIN_ABORT 0x0002
#define TYPE_AIO_PIN_CLOSE 0x0003
#define TYPE_AIO_PIN_SUBSCRIBE 0x0004
#define TYPE_AIO_PIN_UNSUBSCRIBE 0x0005
#define TYPE_AIO_PIN_EVENT_VALUE_CHANGE 0x0006
#endif
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Defining macros like this that are never used (if the corresponding module isn't built) doesn't really hurt anything, so I doubt we need these #ifdefs?

@@ -69,13 +96,14 @@ typedef struct zjs_ipm_message {
uint32_t error_code;

union {
// AIO
#ifdef BUILD_MODULE_AIO
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even defining structs that don't get used shouldn't really hurt anything. A few microseconds faster built time but more clutter in the code.

SENSOR_STATE_IDLE,
SENSOR_STATE_ACTIVATING,
SENSOR_STATE_ACTIVATED,
SENSOR_STATE_ERRORED,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this non-word Errored something you're inheriting from elsewhere? :)

@grgustaf
Copy link
Contributor

grgustaf commented Nov 8, 2016

Trying out Accelerometer.js, when my board is sitting flat it reports something like:
acceleration (m/s^2): x=-0.92958 y=0.25773799999999997 z=11.353628

We would expect x=0, y=0, z=9.81; so y is understandable but x and z seem surprisingly off I think?

@grgustaf
Copy link
Contributor

grgustaf commented Nov 8, 2016

It would be very nice if the output of the sample could be printed with a fixed width, so the y and z values line up as they're spewing by, then you could watch them go up and down without having to pause the stream.

@grgustaf
Copy link
Contributor

grgustaf commented Nov 8, 2016

I can confirm the calibration made the flat on desk case more accurate. If I tip it up on short end or long end it shows x=-8.3 though or y=-8.3, where i'd expect -9.81. But flipped the opposite way where gravity is positive they are around 9.8. Not sure if it can get better, especially without a dynamic user involved calibration?

} sensor_handle_t;

static sensor_handle_t *accel_handle = NULL;
static sensor_handle_t *gyro_handle = NULL;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe plurals would be better here to remind us these are lists?

static void zjs_sensor_signal_callbacks(sensor_handle_t *handle,
union sensor_reading reading)
{
if (!handle)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't needed because your loop will be skipped if it's NULL.

@grgustaf
Copy link
Contributor

grgustaf commented Nov 8, 2016

Jimmy and I found that the auto calibration and error handling commits (at least) segfault if you try to run AIO.js. If you run the BTGrove demo, it does the same once you start to connect.

frequency: 50
});

sensor.onchange = function(event) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jimmy-huang : Some changes were made for the Sensor Reading
Now

accleromterSensor.onchange = function () {
console.log("acceleration (m/s^2): " +
        " x=" + accleromterSensor.reading.x +
        " y=" + accleromterSensor.reading.y +
        " z=" + accleromterSensor.reading.z);
}

just a heads up, you can make this changes in subsequent PR also, upto you.

" z=" + event.reading.z);
};

sensor.onstatechange = function(event) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

onstatechange is also removed


sensor.onchange = function(event) {
console.log("rotation (rad/s): " +
" x=" + event.reading.x +
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please see the comment above for accelerometer

@jimmy-huang
Copy link
Contributor Author

this needs to be rebased once #415 is merged

Initial implementation for the W3C Generic Sensor API and add
support for the Accelerometer and Gyroscope sensors using
the BMI160 inertia sensor built-in on the Arduino 101,
the current implementation is polling from the ARC and channeling
the data back to X86.

Also adding two sample JS code testing both APIs.

Signed-off-by: Jimmy Huang <[email protected]>
Also fixed a bug where we still report change events
after error is reported

Signed-off-by: Jimmy Huang <[email protected]>
Signed-off-by: Jimmy Huang <[email protected]>
Signed-off-by: Jimmy Huang <[email protected]>
Signed-off-by: Jimmy Huang <[email protected]>
@jimmy-huang
Copy link
Contributor Author

@grgustaf, updated pr and rebased, please review

@grgustaf grgustaf merged commit f9791d9 into intel:master Nov 14, 2016
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants