New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add arduino library to Firmata or Johnny-Five #285

Closed
scottkellie opened this Issue Jan 1, 2014 · 35 comments

Comments

Projects
None yet
9 participants
@scottkellie
Copy link

scottkellie commented Jan 1, 2014

Not sure if issues or just me!

I am currently trying to connect some I2C devices, DHT and onewire temp sensors etc to one of my projects. I can use Johnny-Five to read and set analog and digital pins and it works well. I don't seem to be able to find any docs on adding an Arduino library e,g, RCswitch to my program.

I also cannot seem to find an example of sending an I2C setup message, sending a request and reading the response. I have a wireless bridge that uses I2C also. The support is in Firmata but I would like to stay within the usual Johnny Five instance and can't seem to find anything, is there anything that can point me in the right direction?

one of the sensors I am using is a popular one wire temp sensor the Dallas DS18B20
Not sure on support for this it uses the onewire library http://playground.arduino.cc/Learning/OneWire

@rwaldron

This comment has been minimized.

Copy link
Owner

rwaldron commented Jan 1, 2014

Firmata, which Johnny-Five is built ontop of, provides i2c read, write and config methods.

@rwaldron

This comment has been minimized.

@rwaldron rwaldron closed this Jan 2, 2014

@scottkellie

This comment has been minimized.

Copy link
Author

scottkellie commented Jan 2, 2014

Ah I was just coming back to reply to this! Thanks for the pointers I managed to actually find a working code sample within the firmata github pull requests for the DS18B20 added board.firmata.xx I'm just having issues getting configurable Firmata to compile the DHT11 code. Other than that I'm back on track.
firmata/firmata.js#48

@rwaldron

This comment has been minimized.

Copy link
Owner

rwaldron commented Jan 2, 2014

@scottkellie please keep us posted with your progress here? Thanks!

@scottkellie

This comment has been minimized.

Copy link
Author

scottkellie commented Jan 2, 2014

The code linked to above by @achingbrain is a good base to work from and worked on first run, This title is misleading now!
The code below should search for and return the 1st onewire DS18B20 sensor and then read it's values converting to Celsius and Farenheit. It can hopefully be expanded. I shall pop back when I dig through it more.

var five = require('johnny-five'), board

// 1-wire devices are on pin 20 on Mega
var pin = 20;
var board = new five.Board();

board.on("ready", function ()
            {
  board.firmata.sendOneWireConfig(pin, true);
  board.firmata.sendOneWireSearch(pin, function(error, devices) {
    if(error) {
      console.error(error);
      return;
    }

    // only interested in the first device
    var device = devices[0];

    var readTemperature = function() {
      // start transmission
      board.firmata.sendOneWireReset(pin);

      // a 1-wire select is done by ConfigurableFirmata
      board.firmata.sendOneWireWrite(pin, device, 0x44);

      // the delay gives the sensor time to do the calculation
      board.firmata.sendOneWireDelay(pin, 1000);

      // start transmission
      board.firmata.sendOneWireReset(pin);

      // tell the sensor we want the result and read it from the scratchpad
      board.firmata.sendOneWireWriteAndRead(pin, device, 0xBE, 9, function(error, data) {
        if(error) {
          console.error(error);
          return;
        }
        var raw = (data[1] << 8) | data[0];
        var celsius = raw / 16.0;
        var fahrenheit = celsius * 1.8 + 32.0;

        console.info("celsius", celsius);
        console.info("fahrenheit", fahrenheit);
      });
    };
    // read the temperature now
    readTemperature();
    // and every five seconds
    setInterval(readTemperature, 5000);
  });
});
@rwaldron

This comment has been minimized.

Copy link
Owner

rwaldron commented Jan 2, 2014

This is great, I'll order some of these sensors and we'll see what we can come up with together.

@bmwinstead

This comment has been minimized.

Copy link

bmwinstead commented May 25, 2014

Is there a trick to getting this to work with multiple sensors on one bus? I've tried just about everything I can think of and it always seems to experience timeouts when trying to read from the second sensor. Thanks in advance.

@rwaldron

This comment has been minimized.

Copy link
Owner

rwaldron commented May 25, 2014

@bmwinstead I honestly haven't looked at this since January. Hopefully we can get @scottkellie to help :)

@scottkellie

This comment has been minimized.

Copy link
Author

scottkellie commented May 25, 2014

Hi guys I never got too much further with this. I broke away from arduino for my project and used the gpio's on my raspberry pi may well revisit this so keep me up to date. @achingbrain may be able to expand on his code to add another sensor? This line looks like this li e is the starter for 10 :)
// only interested in the first device var device = devices[0];

@bmwinstead

This comment has been minimized.

Copy link

bmwinstead commented May 26, 2014

Thanks guys for your input. I guess I'll just keep trying until something falls out.

@achingbrain

This comment has been minimized.

Copy link
Contributor

achingbrain commented May 26, 2014

Do you have an example of the code you are using that doesn't work?

As something of a disclaimer I never actually had the need to connect more than one device to the bus but from memory the array of devices you get back contains the IDs of all devices attached to the bus so it should just be a case of using them instead.

@bmwinstead

This comment has been minimized.

Copy link

bmwinstead commented May 26, 2014

You would think that wouldn't you? I don't have any of the exact code I used but I can outline what I've tried. I modified your original readTemperature() method a little (this still works with 1 device):

var readTemperature = function() {
            if(debug) console.time("readTemperature");
            board.io.sendOneWireReset(pin);
            board.io.sendOneWireWrite(pin, devices[0], 0x44);
            board.io.sendOneWireDelay(pin, 1000);
            board.io.sendOneWireReset(pin);
            board.io.sendOneWireWriteAndRead(pin, devices[0], 0xBE, 9, function (error, data) {
                if(error) {
                    return;
                }
                var tempF =(((data[1] << 8) | data[0]) / 8.888888) + 32.0;
                var now = new Date();
                dbInsert(tempF, now);
                if(debug) console.timeEnd("readTemperature");
            });       
        };

and tried wrapping it into a for loop to iterate through the devices array.

var readTemperature = function() {
   for(var i = 0; i < devices.length; i++) {
...
}
}

I tried adding two sequential reads inside the readTemperature method, and I tried making a readTemperature2 method that just reads from devices[1]. All have the same results. I can give more direct info tomorrow (I'm gone all day and my arduino is currently disconnected, so I can't access it remotely).

I THINK that it's a matter of timing. Like the master device is busy when the Arduino issues the temp conversion request and something, somewhere in the chain doesn't handle the timeout well. I can say that both temp sensors are working when I just used pure Arduino code.

@scottkellie

This comment has been minimized.

Copy link
Author

scottkellie commented May 26, 2014

Each device has a hardware address burnt in so you should be able to use the one wire method once you know the address you can read write specifically to each sensor.
I think with the code structure you will need to close and reopen to send to each device unless you can broadcast or add the value and read the return using the hardware address. I have a couple of one wire sensors and will be getting some more ds18b20's to use so will fire up one of the arduinos and have a tinker :)

@westinpigott

This comment has been minimized.

Copy link

westinpigott commented Nov 2, 2014

I went ahead made a quasi-recursive loop which is non-blocking based on the example above. It works for at least five devices, which is all I have available to test with. Pretty good performance, with the entire cycle taking about 3/10 of a second with 5 devices.

The read loop is independent of the retrieval of the temperatures, so you can retrieve the latest temp for a device at any point, and the loop just keeps updating them as frequently as possible.

I am trying to make this into a usable module, but am struggling to get it working. You can see my progress at https://github.com/westinpigott/one-wire-temps. If anybody wants to fork it and help me out, it would be awesome.

var five = require('johnny-five'), board

var pin = 3;
var board = new five.Board();

// Need a place to store the temperatures
var temps = [];

// Dumps the array of temps after converting each to Fahrenheit
var outputFTemps = function() {
    var out = [];
    for (var i = 0, len = temps.length; i < len; i++) {
        var celsius = temps[i] / 16.0;
        out[i] = (celsius * 1.8 + 32.0).toFixed(2);
    }
    console.log(out);
}

// Don't do anything until the board is ready for communication.
board.on("ready", function () {

    // Find all the devices on the 1-wire bus.  Reset, then send command search.
    board.io.sendOneWireConfig(pin, true);
    board.io.sendOneWireSearch(pin, function(error, allDevices) {
        if(error) {
          console.error(error);
          return;
        }

        // Parse out only the temperature probes (DS18B20)
        var devices = [];
        for (var i = 0, len = allDevices.length; i < len; i++) {
            if(allDevices[i][0] == 0x28) {
                devices.push(allDevices[i]);
                }
        }

        // Method to read a single temperature probe and store it in the temps array.
        var readSingle = function(deviceNum) {

            // If we reached the end of the list of devices, then start again by calling readTemperatures in a non-blocking way.
            if(!(deviceNum in devices)) {
                setTimeout(readTemperatures,0);
                return;
            }

            // Send the reset on the bus so it is ready for next command.
            board.io.sendOneWireReset(pin);

            // tell the sensor we want the result and read it from the scratchpad
            board.io.sendOneWireWriteAndRead(pin, devices[deviceNum], 0xBE, 9, function(error, data) {
                if(error) {
                  console.error(error);
                  return;
                }

                // Convert the data into a raw integer.
                var raw = (data[1] << 8) | data[0];
                temps[deviceNum] = raw;

                // We have read this temp, lets read the next one.
                setTimeout(readSingle,0,deviceNum+1);
            }.bind(deviceNum));     
        }

        // Method to sent CONVERT command to all devices and then start the reading chain.
        var readTemperatures = function() {
        console.log('==================== Read Cycle Start ==========================');

            // Reset and start convert on all probes.
            for (var i = 0, len = devices.length; i < len; i++) {
                board.io.sendOneWireReset(pin);
                board.io.sendOneWireWrite(pin, devices[i], 0x44);
            }

            // Read the first device temperature.
            setTimeout(readSingle,0,0);       
        };

        // Output the temps every 1/10 second.
        setInterval(outputFTemps,100);

        // Start the loop to continually read the temps.
        readTemperatures();
    });
});
@rwaldron

This comment has been minimized.

Copy link
Owner

rwaldron commented Nov 2, 2014

Let's work on making a nice class for this :)

@westinpigott

This comment has been minimized.

Copy link

westinpigott commented Nov 2, 2014

I agree. You can see in https://github.com/westinpigott/one-wire-temps/blob/master/OneWireTemps.js that I have it started. In fact, I thought the whole thing would work but it seems the data returned is always NULL. Any help on the class would be appreciated.

@rwaldron

This comment has been minimized.

Copy link
Owner

rwaldron commented Nov 2, 2014

Are you using the appropriate version of Firmata on the board? StandardFirmata has no OneWire support. You need to use https://github.com/firmata/arduino/tree/configurable

@westinpigott

This comment has been minimized.

Copy link

westinpigott commented Nov 2, 2014

Yes. I am using ConfigurableFirmata. The whole process works when I run it as the script I posted above. When I try doing the same thing with the class I wrote (based on the script) it wont return any values from the sensor.

@rwaldron

This comment has been minimized.

Copy link
Owner

rwaldron commented Nov 2, 2014

Oh got it, sorry I misunderstood that. I can take a look

@westinpigott

This comment has been minimized.

Copy link

westinpigott commented Dec 7, 2014

I was able to get it working. The package is published on NPM with the name "one-wire-temps". I will be making some additional features for it (configuration, additional events, etc), but the underlying base will stay the same.

@BrianGenisio

This comment has been minimized.

Copy link
Collaborator

BrianGenisio commented Dec 12, 2014

@westinpigott I'm actually working on a Temperature abstraction right now that will support several temperature sensors. I will add support for this device. You mind if I base the "device" portion of the abstraction off your "one-wire-temps" repo?

@westinpigott

This comment has been minimized.

Copy link

westinpigott commented Dec 12, 2014

@BrianGenisio, by all means. Throw an attribution in there somewhere, and then feel free to use any and all of it!

@BrianGenisio

This comment has been minimized.

Copy link
Collaborator

BrianGenisio commented Dec 13, 2014

@westinpigott Nice.

Basically, I am working on adding IMU support to J5. The particular device I am adding has Accelerometer, Temperature, and Gyro support. It requires that Accelerometer and Gyro be refactored to support different devices, including a "breakout" device like the MPU6050. It also requires that a new Temperature class needs to be written, so this was a very useful thread. It is one of the temperature sensors I have in my "lab", so I figured I'd get it working 😄.

When all is said and done (hopefully within the next week or so), you'll be able to create a new Temperature component like this:

var temperature = new five.Temperature({
  pin: 2,
  device: "DS18B20"
});
@westinpigott

This comment has been minimized.

Copy link

westinpigott commented Dec 13, 2014

That sounds like a pretty slick implementation. How do you identify which device on the one-wire protocol? Ie. You can have up to a 127 wired in parallel, and they can all be indepently queried. Seems like you would need an addition parameter for which device on the one-wire bus. This is what my package was solving for, which is how to read all the different devices on the single bus independently.

@BrianGenisio

This comment has been minimized.

Copy link
Collaborator

BrianGenisio commented Dec 13, 2014

@westinpigott Agreed. At this point, I've punted on the multiple device per pin concept. Mostly because I only have one of these sensors right now :)

But it does introduce a wrinkle in the J5 architecture. I mean, it is pretty easy for us to add an option (address: 0x1234) and use it if it exists. Default it to the first one in the family if it doesn't. The thing I'm grappling with is how does the person using it know what the address is?

Perhaps, this device exposes the address as a read-only property on the Temperature object? Then you can plug it in and read the address, then unplug it and read the address of the next? That might be the best way to go about it.

If I implement that behavior, would you mind testing it with multiple temperature sensors?

@BrianGenisio

This comment has been minimized.

Copy link
Collaborator

BrianGenisio commented Dec 13, 2014

@westinpigott OK, I've added the ability to address a device, and get the address out of the device.

The first time you use it, you need to find the address. You can create the Temperature without an addres. We'll default to the first device it finds in the family code, so it needs to be isolated in order to get it:

var temperature = new five.Temperature({
  pin: 2,
  device: "DS18B20"
});

temperature.once("data", function() {
  console.log('Address', this.address.toString(16));
});

When I did that, I got an address of 0x05DCA60E. So now I can address it directly:

var temperature = new five.Temperature({
  pin: 2,
  device: "DS18B20",
  address: 0x05DCA60E
});

I can't test that it works for more than one device because I don't have more than one to test. But assuming that the OneWire Firmata works as expected, it should work. If you get the chance, you can pull down my IMU fork and try it with multiple temperature sensors.

Thanks for the feedback!

@rwaldron

This comment has been minimized.

Copy link
Owner

rwaldron commented Dec 13, 2014

In the Led.Matrix class, user code can provide address: ... or addresses: [...], so this fits nicely.

@r3sk

This comment has been minimized.

Copy link

r3sk commented Mar 15, 2015

I am trying to set 2 ds18b20 sensors on the same pin like:

var temperature1 = new five.Temperature({
        address: 0x44eea77ff,
        controller: "DS18B20",
        pin: 8,
    });

     var temperature2 = new five.Temperature({
        address: 0x44ef135ff,
        controller: "DS18B20",
        pin: 8,
    });

temperature1.once("data", function (err, data) {
        console.log(data.celsius + "°C");
    });
temperature2.once("data", function (err, data) {
        console.log(data.celsius + "°C");
    });

but i get this error:

Component pin: 8, controller: DS18B20, address: 18504275455 is already in use 
/home/resk/dev/arduino/node_modules/johnny-five/lib/temperature.js:119

                this.emit("error", err);
                     ^
TypeError: undefined is not a function
    at /home/resk/dev/arduino/node_modules/johnny-five/lib/temperature.js:119:22
    at null._onTimeout (/home/resk/dev/arduino/node_modules/johnny-five/node_modules/firmata/lib/firmata.js:1141:5)
    at Timer.listOnTimeout (timers.js:110:15)

am i doing something wrong?

@kbarre123

This comment has been minimized.

Copy link

kbarre123 commented Mar 30, 2015

Hey @r3sk , I'm getting the same errors as you. We're discussing a fix over on Gitter if you want to follow along and help test.

@JerksonS

This comment has been minimized.

Copy link

JerksonS commented Aug 6, 2015

Hey everyone, i was beginning to use the temperature class in a project I'm working on and now that I'm ready to add a second 1-wire device on the bus I landed here :)

I have followed the gitter conversation but saw no resolution to this question there so I figured I'd revive this old thread.Is there a supported way of addressing multiple 1-wire devices on a single pin using the temperature class yet, maybe by passing an array of addresses as a parameter in the constructor or by instantiating multiple temperature objects with different addresses on the same pin (which still caused an error for me when I tired like r3sk above)

Even though over here - https://github.com/BrianGenisio/johnny-five/blob/multi-DS18B20/eg/temperature-dual-ds18b20.js - it shows exactly the same method (two temperature objects sharing the same pin but with different addresses) This generates an error when the second temperature object is defined:

"Component pin: 12, controller: DS18B20, address: 1190065684735 is already in use"

Thanks for all your work on Johnny-Five

@westinpigott

This comment has been minimized.

Copy link

westinpigott commented Aug 6, 2015

@JerksonS, I have not tested the version which is supposed to be integrated into johnny-five. It is based on the class I wrote which has johnny-five as a dependency, although I don't believe it is non-blocking IO the way my class is. You can see mine at https://github.com/westinpigott/one-wire-temps. it works for multiple sensors. I have tested it up to 5 as thats all I had available.
Not completely clean as a johnny-five module, but it works.

@JerksonS

This comment has been minimized.

Copy link

JerksonS commented Aug 6, 2015

@westinpigott Thanks for the reply.

I was looking through the Johnny five source and saw the credit to your one-wire-temps in temperature.js.

I had actually started using your one-wire-temps before I learned that a temperature class was added into Johnny-five itself. Until now I have only been playing with one temp sensor but now want to expand to 5 and then I hit this roadblock. Perhaps I'll try out your way again while i wait for it to be 'corrected' or validated in johnny-five natively.

@rwaldron

This comment has been minimized.

Copy link
Owner

rwaldron commented Aug 6, 2015

Even though over here - https://github.com/BrianGenisio/johnny-five/blob/multi-DS18B20/eg/temperature-dual-ds18b20.js - it shows exactly the same method (two temperature objects sharing the same pin but with different addresses) This generates an error when the second temperature object is defined:

"Component pin: 12, controller: DS18B20, address: 1190065684735 is already in use"

This is just a bug in the over-eager pin validation/protection code :\

I'll work on it as soon as I can

@r3sk

This comment has been minimized.

Copy link

r3sk commented Aug 6, 2015

@kbarre123 last time i played if this sensors they were working, but the "Component pin: 8, controller: DS18B20, address: 18504275455 is already in use " message continue appearing, but it didn't break the code anymore. I'll test my code again when get home.

@JerksonS

This comment has been minimized.

Copy link

JerksonS commented Aug 7, 2015

@r3sk, @kbarre123 and @rwaldron, Sorry guys..it looks like I jumped the gun here. Even though I get an error message about the pin already being in use, it does actually work. I'm polling two DS18B20's (one waterproof and one bare sensor) on the same bus/pin successfully and reliably.

I simply saw the error message and decided not to go any further and write an .on "data" function for the second sensor assuming it was not initialized correctly when I saw the error. Here is the code that works:


temp1 = new five.Temperature({
  controller: "DS18B20",
  pin: "12",
  address: 0x6873a44, //waterproof
  freq:  0.1 * 60 * 1000
});

temp2 = new five.Temperature({
    controller: "DS18B20",
    pin: "12",
    address: 0x115159e93ff, // rj45 sensor
    freq:  0.1 * 60 * 1000
});

temp1.on("data", function(err, data) {
    console.log("Read water temp " + data.fahrenheit + " from 0x" + this.address.toString(16));
});
temp2.on("data", function(err, data) {
    console.log("Read air temp " + data.fahrenheit + " from 0x" + this.address.toString(16));
});

Perhaps the page - http://johnny-five.io/examples/temperature-ds18b20/ - could be updated to include an example of multiple sensors on one pin. I always try to refer to the docs before google searching and bothering others ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment