rwaldron / johnny-five

JavaScript Robotics and IoT programming framework, developed at Bocoup.
http://johnny-five.io
Other
13.3k stars 1.77k forks source link

Support the Photon Weather Shield #912

Closed BrianGenisio closed 9 years ago

BrianGenisio commented 9 years ago

This is the device: https://www.sparkfun.com/products/13630

First pass, I plan to support

Anything else that should be supported in the first pass?

BrianGenisio commented 9 years ago

At first I had thought that this shield had a unifying I2C interface, but the more I read it, I realize that is not the case. Really, there are two chips just connected to the I2C bus:

So, in reality, the devices should be for these chips specifically:

var humidity = new five.Humidity({
  controller: "HTU21D"
});

var temperature = new five.Temperature({
  controller: "HTU21D"
});

We can alias them as PHOTON_WEATHER_SHIELD to beef up the user experience... but really, this is all it is.

For new five.Temperature({ controller: PHOTON_WEATHER_SHIELD'}), we'll have to decide which temperature reading to take. It is probably an arbitrary decision, but I'd probably go for HTU21D, because that is the one that is called out for having Temperature in the product on the website.

rwaldron commented 9 years ago

There are lots of altimeter and humidity components that are commercially available, so let's go ahead and create two new component classes:

Other Multi breakouts:

* The existing Barometer class can also be aliased as Pressure, if we think that will be intuitive.

4math2379 commented 9 years ago

Hi ,

Waou ! Thanks for this information. I will take a look .

Vesuviian

Le 30 sept. 2015 à 15:52, Rick Waldron notifications@github.com a écrit :

There are lots of altimeter and humidity components that are commercially available, so let's go ahead and create two new component classes:

Altimeter BMP180 (implemented, just need to expose data) MS5607 can be implemented add more... Hygrometer (I don't mind if we also export an alias called Humidity *) SHT15 RHT03 via I2C backpack add more... Other Multi breakouts:

BME280

  • The existing Barometer class can also be aliased as Pressure, if we think that will be intuitive.

— Reply to this email directly or view it on GitHub https://github.com/rwaldron/johnny-five/issues/912#issuecomment-144407965.

dougseven commented 9 years ago

The Photon weather shield is fairly expandable, with built in ports for wind and rain sensors, and pin-outs labeled for soil moisture, soil temp and more. I thin focusing on the specific devices on the shield (HTU21D, MPL3115A2) is probably the right decision. This also means you won't have to make a decision about which temperature to use for the controller.

BrianGenisio commented 9 years ago

Cool. I just ordered the AdaFruit standalone version of those two sensors so I can test them separately. I'll be hacking a bit tomorrow, so if the shield shows up, I might have some good progress to show.

dougseven commented 9 years ago

I was looking through some of my stuff and I found the SparkFun Weather Shield for Arduino (https://www.sparkfun.com/products/12081). It has the same humidity and barometer sensors as the SparkFun Weather Shield for Photon - plus it has a ALS-PT19 light sensor. This confirms for me that the controllers should be the sensor ID (i.e. HTU21D, MPL3115A2, ALS-PT19).

BrianGenisio commented 9 years ago

Making progress on the HTU21D support here: https://github.com/BrianGenisio/johnny-five/tree/photon-weather-shield

I'm able to get relative humidity and temperature, but I can't seem to get them both at the same time. I need to toggle between reading the temperature register and the humidity register using i2cReadOnce but the act of toggling makes the values unreliable. If I hold the register the same, then I can read the values successfully, but when I toggle, the values are bad.

I think it must be some sort of timing bug... not sure what it is, though. I'm going to move on to the MPL3115A2 to clear my brain. #cc @dougseven

This is the code I'm using to test. I didn't check it into the J5 repo because it is an example using the photon:

var five = require("johnny-five");
var Particle = require("particle-io");
var board = new five.Board({
  io: new Particle({
    token: process.env.PARTICLE_TOKEN,
    deviceId: process.env.PARTICLE_DEVICE_ID
  })
});

board.on("ready", function() {
  var temperature = new five.Temperature({
    controller: "HTU21D"
  });

  var hygrometer = new five.Hygrometer({
    controller: "HTU21D"
  });

  temperature.on("change", function() {
    console.log("temperature");
    console.log("  fahrenheit   : ", this.fahrenheit);
    console.log("--------------------------------------");
  });

  hygrometer.on("change", function() {
    console.log("humidity");
    console.log("  relative humidity : " + this.relativeHumidity + "%");
    console.log("--------------------------------------");
  });
});
dougseven commented 9 years ago

Why would you create two objects using the HTU21D controller? Couldn't you just create one that exposes properties for each of the data types: var hygrometer = new five.Hygrometer({ controller: "HTU21D" });

hygrometer.on("change", function() { console.log("temperature"); console.log(" fahrenheit : ", this.fahrenheit); console.log("humidity"); console.log(" relative humidity : " + this.relativeHumidity + "%"); console.log("--------------------------------------"); });

rwaldron commented 9 years ago

Why would you create two objects using the HTU21D controller? Couldn't you just create one that exposes properties for each of the data types:

Because temperature readings are the exclusive domain of the Temperature class. Once @BrianGenisio is done, he will wrap all of these in an instance of the Multi class that exposes each of the single purpose sensor objects as properties. Take a look at IMU. I also just realized that I need to write up docs for Multi

dougseven commented 9 years ago

That makes sense. How will the Multi class handle the fact that there are two temperature values - one from each sensor. From a usage standpoint I have seen people take the values from each and average them to account for any variances.

var temp = (hygrometer.F + barometer.F) / 2;

dougseven commented 9 years ago

is this an example of what the implementation would look like?

var weather = new five.IMU({
    controller: "PHOTON_WEATHER_SHIELD"
});

// hF, hC, bF, bC are holder variables for the fahrenheit and celsius values from the
// hygrometer and barometer respectively.
var hF, hC, bF, bC, relativeHumidity, pressure;

weather.on("data", function() {
    hF = this.hygrometer.temperature.fahrenheit;
    hC = this.hygrometer.temperature.celsius;
    bF = this.barometer.temperature.fahrenheit;
    bC = this.barometer.temperature.celsius;
    relativeHumidity = this.relativeHumidity;
    pressure = this.pressure;
});

var fahrenheit = (hF + bF)/2;
var celsius = (hC + bC)/2
dougseven commented 9 years ago

This is the example I am building for our workshop to use the Photon Weather Shield. Since I am working on the documentation and samples in parallel to @BrianGenisio and @rwaldron I want to verify that this is correct (I have made some assumptions here).

https://github.com/dougseven/Demos/blob/master/photon/IoTHub/weather.js

rwaldron commented 9 years ago

is this an example of what the implementation would look like?

Close... There are actually two different "multiple sensor modules" on this shield and I'm not sure how to resolve the competing temperature sensors to consolidate the two into one. We've been around the block re: "shields as a class" and it always comes up short.

rwaldron commented 9 years ago

https://github.com/dougseven/Demos/blob/master/photon/IoTHub/weather.js

With the exception of the temperature properties appearing on instances of classes that they don't exist on, this makes sense.

rwaldron commented 9 years ago

That makes sense. How will the Multi class handle the fact that there are two temperature values - one from each sensor. From a usage standpoint I have seen people take the values from each and average them to account for any variances.

var temp = (hygrometer.F + barometer.F) / 2;

Meh. It might be time to revisit an older argument discussion about Shield...

dougseven commented 9 years ago

If each of the sensors (Multi) in the shield has its own temperature, why wouldn't you make temperature a property of the Mulit sensor within the shield?

        hC = this.hygrometer.temperature.celsius;
        bF = this.barometer.temperature.fahrenheit;

        relativeHumidity = this.hygrometer.relativeHumidity;
dougseven commented 9 years ago

Isn't a Shield just an Mulit of one or more Sensors or Multi's? Can an Mulit have a Mulit as a property?

    var weatherShield = new five.Shield({
        controller: "PHOTON_WEATHER_SHIELD"
    });

    weatherShield.on("data", function() {
        // hygrometer is an Multi within the weatherShield 
        // and temperature is a sensor with the hygrometer
        hF = this.hygrometer.temperature.fahrenheit;
    }
dougseven commented 9 years ago

For the record, I am also fine with instantiating the individual sensors and working with them instead of the shield.

    var baro = new five.Barometer({
        controller: "MPL3115A2",
    });
rwaldron commented 9 years ago

If each of the sensors (Multi) in the shield has its own temperature, why wouldn't you make temperature a property of the Mulit sensor within the shield?

Because the HTU21D and MPL3115A2 are the Multi components, each contain instances of other classes.

Isn't a Shield just an IMU of one or more Sensors or IMUs?

Sorry, this will sound obnoxious and pedantic, but I don't want you to be misinformed: an "IMU" is an "inertial measurement unit" and refers specifically to multi-sensor modules that serve that purpose. We're only talking about generic multi-sensor modules.

@BrianGenisio I think we can have a Multi controller that just initializes other Multi instances.

rwaldron commented 9 years ago

For the record, I am also fine with instantiating the individual sensors and working with them instead of the shield.

We need a way to make it easy to group these things, it's a challenge, but it's not impossible—we'll figure it out.

@BrianGenisio wdyt?

  // This would be a new controller definition inside lib/imu.js
  PHOTON_WEATHER_SHIELD: {
    initialize: {
      value: function(opts) {
        var state = priv.get(this);

        state.hygrometer = new Multi(
          Object.assign({
            controller: "HTU21D",
            board: this.board,
          }, opts)
        );

        state.barometer = new Multi(
          Object.assign({
            controller: "MPL3115A2",
            board: this.board,
          }, opts)
        );
      }
    },
    components: {
      value: ["barometer", "hygrometer"]
    },
    barometer: {
      get: function() {
        return priv.get(this).barometer;
      }
    },
    hygrometer: {
      get: function() {
        return priv.get(this).hygrometer;
      }
    }
  },

I think it will actually work, even with the component list, because IIRC the Multi instances satisfy the requirements here:

  if (this.components && this.components.length > 0) {
    this.components.forEach(function(component) {
      if (!(this[component] instanceof Emitter)) {
        return;
      }

      this[component].on("change", function() {
        this.emit("change", this, component);
      }.bind(this));
    }, this);
  }
rwaldron commented 9 years ago

Of course it requires Multi controllers for HTU21D & MPL3115A2, then it's just recursive Multi controllers. Zany shit!

BrianGenisio commented 9 years ago

Yes, @rwaldron. That is mostly what I'm thinking, though I think it can make for a somewhat awkward interface... calling weather.barometer.barometer.

Here is what i think the consumption of the components should look like:

We need to support the standalone components on the chips:

HTU21D

var hygrometer = new five.Hygrometer( { controller: 'HTU21D' });
hygrometer.on('change', function() {
  console.log(this.relativeHumidity);
});
var temperature = new five.Temperature( { controller: 'HTU21D' });
temperature.on('change', function() {
  console.log(this.F);
});

MPL3115A2

var barometer = new five.Barometer( { controller: 'MPL3115A2' });
barometer.on('change', function() {
  console.log(this.hPa);
});
var temperature = new five.Temperature( { controller: 'MPL3115A2' });
temperature.on('change', function() {
  console.log(this.F);
});

Then we need to support the fact that the chips have multiple components:

HTU21D

var htu21d = new five.Multi({ controller: 'HTU21D' });
htu21d.hygrometer.on('change', function() {} );
htu21d.temperature.on('change', function() {} );

MPL3115A2

var mpl3115a2 = new five.Multi({ controller: 'MPL3115A2' });
mpl3115a2.barometer.on('change', function() {} );
mpl3115a2.temperature.on('change', function() {} );

And finally, since the shield has both, it should look like this:

var weather = new five.Multi({ controller: 'PHOTON_WEATHER_SHIELD'});
weather.htu21d.hygrometer.on('change', function() {} );
weather.htu21d.temperature.on('change', function() {} );
weather.mpl3115a2.barometer.on('change', function() {} );
weather.mpl3115a2.temperature.on('change', function() {} );

When you have a shield, ANY of the above interfaces should work for you. When you have a standalone chip, any of them (except for the shield, of course) should work. The only thing I'm not crazy about is calling out the chips by name on the shield. If we call them by component type, then it becomes awkward:

weather.hygrometer.hygrometer.on();
weather.hygrometer.temperature.on();

I can see pros and cons for both ways... perhaps we alias them so they can work either way?

rwaldron commented 9 years ago

@briangenisio I think that works for me. Another alternative: implement just the chip Multi controllers and then make a plugin module that wrapped them up neatly. We can try both

BrianGenisio commented 9 years ago

Ok, good news @dougseven . I worked the HTU21D from scratch, and now I have a fully functioning humidity and temperature sensor working (on my branch). Plus, I've tested it with a stand-alone chip. It works that way too... I might get that one wrapped up as a PR before moving on to the Barometer.

Barometer/Temp (MPL3115A2) is ... tricky. Need to spend some focused time on it. But, progress!

dougseven commented 9 years ago

Ok, good news @dougseven . I worked the HTU21D from scratch, and now I have a fully functioning humidity and temperature sensor working (on my branch). Plus, I've tested it with a stand-alone chip. It works that way too... I might get that one wrapped up as a PR before moving on to the Barometer.

That is great news! We can do our workshops and Azure Tour with just the HTU21D - although it would be awesome to get both sensors working. Thanks @BrianGenisio !!!!!

BrianGenisio commented 9 years ago

@dougseven Good plan then. I've started a new branch just for the HTU21D PR, and I have an initial PR ready to go: https://github.com/rwaldron/johnny-five/pull/916

It still needs unit tests and a diagram, but it is otherwise ready for you to play with. Let me know how it works for you!

Note that the examples in that PR are for vanilla Arduino. You'll need to modify them to use the particle-io layer, but that is just at the board creation step. The API to program against the HTU21D doesn't change at all. I've tested this code with the Particle/Weather Shield as well.

rwaldron commented 9 years ago

@dougseven

You'll need to modify them to use the particle-io layer, but that is just at the board creation step.

Let me make myself useful here. I made this to try out @BrianGenisio's work:

var five = require("johnny-five");
var Photon = require("particle-io");

var board = new five.Board({
  io: new Photon({
    token: process.env.PARTICLE_TOKEN,
    deviceId: process.env.PARTICLE_PHOTON_REDBOARD_1
  })
});

board.on("ready", function() {
  var multi = new five.Multi({
    controller: "HTU21D"
  });

  multi.on("change", function() {
    console.log("Thermometer");
    console.log("  celsius           : ", this.temperature.celsius);
    console.log("  fahrenheit        : ", this.temperature.fahrenheit);
    console.log("  kelvin            : ", this.temperature.kelvin);
    console.log("--------------------------------------");

    console.log("Hygrometer");
    console.log("  relative humidity : ", this.hygrometer.relativeHumidity);
    console.log("--------------------------------------");
  });
});
rwaldron commented 9 years ago

@dougseven how much more time is there?

BrianGenisio commented 9 years ago

I think I'll have a Barometer POC in a day or so. The AdaFruit library is much easier to implement in J5 than the SparkFun approach. Assuming no snags, pressure, altimeter, and temp will be pretty straight forward. Will not be as bad as I had thought. In general, the AdaFruit examples have been much better for both of these devices. Glad I looked. On Sun, Oct 4, 2015 at 4:10 PM Rick Waldron notifications@github.com wrote:

@dougseven https://github.com/dougseven how much more time is there?

— Reply to this email directly or view it on GitHub https://github.com/rwaldron/johnny-five/issues/912#issuecomment-145383984 .

BrianGenisio commented 9 years ago

@dougseven More good news! I have the hard part written for the MPL3115A2: https://github.com/BrianGenisio/johnny-five/tree/mpl3115a2

So far, it is properly exposing pressure and temperature. I'm also successfully harvesting altimeter data... we just don't have a component to expose it yet. That is the next step.

Everything from here is cake. It will still take some time to round it out, but the hard part is done.

dougseven commented 9 years ago

@rwaldron Our first Azure Tour stop is in Philadelphia on Oct-13. We will lock down the content for that lab this week. We have a workshop in Amsterdam 2-days later. Then we have a couple weeks before the next one.

dougseven commented 9 years ago

@BrianGenisio Thank you!!!

I may not know enough about developing node modules to know how to test this. I cloned your HTU21D branch and I am referencing it locally. I checked the node_modules folder where my test app is and the new hygrometer.js file is there (as a spot check). When I run the code I get the this error:

Dougs-MacBook-Pro:WeatherShield dougseven$ node demo.js
1444030934015 Device(s) particle-io  
1444030935198 Connected particle-io  
1444030935280 Repl Initialized  
>> /Users/dougseven/Development/gh/johnny-five/lib/imu.js:62
        io.i2cConfig(opts);
           ^
TypeError: undefined is not a function
    at EventEmitter.Drivers.HTU21D.initialize.value (/Users/dougseven/Development/gh/johnny-five/lib/imu.js:62:12)
    at Object.Drivers.get (/Users/dougseven/Development/gh/johnny-five/lib/imu.js:452:12)
    at Hygrometer.Controllers.HTU21D.initialize.value (/Users/dougseven/Development/gh/johnny-five/lib/hygrometer.js:12:36)
    at new Hygrometer (/Users/dougseven/Development/gh/johnny-five/lib/hygrometer.js:72:10)
    at IMU.Controllers.HTU21D.initialize.value (/Users/dougseven/Development/gh/johnny-five/lib/imu.js:560:28)
    at new IMU (/Users/dougseven/Development/gh/johnny-five/lib/imu.js:687:10)
    at Board.<anonymous> (/Users/dougseven/Development/gh/Demos/photon/WeatherShield/demo.js:12:15)
    at Board.emit (events.js:104:17)
    at process._tickDomainCallback (node.js:381:11)

My code is the same as @rwaldron code above, but I point to my local johnny-five directory (I also did an npm install ):

var five = require("../../../johnny-five");
var Photon = require("particle-io");

var board = new five.Board({
  io: new Photon({
    token: process.env.PARTICLE_KEY,
    deviceId: process.env.PARTICLE_DEVICE
  })
});

board.on("ready", function() {
  var multi = new five.Multi({
    controller: "HTU21D"
  });

  multi.on("change", function() {
    console.log("Thermometer");
    console.log("  celsius           : ", this.temperature.celsius);
    console.log("  fahrenheit        : ", this.temperature.fahrenheit);
    console.log("  kelvin            : ", this.temperature.kelvin);
    console.log("--------------------------------------");

    console.log("Hygrometer");
    console.log("  relative humidity : ", this.hygrometer.relativeHumidity);
    console.log("--------------------------------------");
  });
});
BrianGenisio commented 9 years ago

@dougseven What version of particle-io is installed? If you haven't updated since Friday, you probably have an old version that doesn't implement I2C. Please make sure your particle-io version is 0.10.0.

BrianGenisio commented 9 years ago

@dougseven Here's a little teaser for you... reading both sensors simultaneously from the particle shield.

Gotta get back to the day job this AM, but I am setting a goal of having both PRs complete by Friday night, but hopefully before that.

dougseven commented 9 years ago

What version of particle-io is installed? If you haven't updated since Friday, you probably have an old version that doesn't implement I2C. Please make sure your particle-io version is 0.10.0.

@BrianGenisio that was it. Got it to run, but it didn't seem like the data event handler was firing. I had to go to the office for the day - but I will check it later tonight.

dougseven commented 9 years ago

@BrianGenisio @rwaldron - I tested this with the Photon and the SparkFun Weather Shield - works great! Thank you.

BrianGenisio commented 9 years ago

@dougseven Both sensors have landed as of 0.8.98. I'd still like to add a Multi controller for this shield which contains all the sensors. But this is not an Arduino compatible shield, so examples require particle-io and I don't think this project has a precedent for including external IO modules in the examples.

Before I toss it and build it as a separate module, I'm going to test something. Off the side of this board are some pinouts for +/- and SDA/SCL pins. I wonder if we can connect an Arduino to the shield that way, and then we have a case for adding it to J5 directly. I'll give it a shot tonight. But this is all frosting now... the core support for both chips is in J5 proper, and ready to use.

https://github.com/rwaldron/johnny-five/blob/master/docs/hygrometer-htu21d.md https://github.com/rwaldron/johnny-five/blob/master/docs/temperature-htu21d.md https://github.com/rwaldron/johnny-five/blob/master/docs/multi-htu21d.md

https://github.com/rwaldron/johnny-five/blob/master/docs/barometer-mpl3115a2.md https://github.com/rwaldron/johnny-five/blob/master/docs/altimeter-mpl3115a2.md https://github.com/rwaldron/johnny-five/blob/master/docs/temperature-mpl3115a2.md https://github.com/rwaldron/johnny-five/blob/master/docs/multi-mpl3115a2.md

dougseven commented 9 years ago

I'd still like to add a Multi controller for this shield which contains all the sensors. But this is not an Arduino compatible shield, so examples require particle-io and I don't think this project has a precedent for including external IO modules in the examples.

@BrianGenisio SparkFun makes a weather shield for Arduino with the same two sensors plus light sensors - https://www.sparkfun.com/products/12081

rwaldron commented 9 years ago

SparkFun makes a weather shield for Arduino with the same two sensors plus light sensors

I've put together a plugin that wraps all three sensors into a simplified API: https://github.com/rwaldron/j5-sparkfun-weather-shield

var Particle = require("particle-io");
var five = require("johnny-five");
var Weather = require("j5-sparkfun-weather-shield")(five);
var board = new five.Board({
  io: new Particle({
    token: process.env.PARTICLE_TOKEN,
    deviceId: process.env.PARTICLE_PHOTON_DEVICE
  })
});

board.on("ready", function() {
  var weather = new Weather({
    variant: "PHOTON",
    freq: 200
  });

  weather.on("data", function() {
    console.log("celsius: %d°C", this.celsius);
    console.log("fahrenheit: %d°F", this.fahrenheit);
    console.log("kelvin: %d°K", this.kelvin);
    console.log("pressure: %d kPa", this.pressure);
    console.log("feet: %d\"", this.feet);
    console.log("meters: %d", this.meters);
    console.log("relativeHumidity: %d RH", this.relativeHumidity);
    console.log("lightLevel: %d%", this.lightLevel);
    console.log("----------------------------------------");
  });
});
BrianGenisio commented 9 years ago

Love it. I think that means we can close this issue, then? @dougseven has full support now ;)

rwaldron commented 9 years ago

Awesome :)

Let's hold it open until we resolve the breakout vs shield discrepancy that I mentioned in gitter.

BrianGenisio commented 9 years ago

Just tried the plug-in with the photon and the shield. Looks perfect. Nice job.

rwaldron commented 9 years ago

Seems safe to close this!