I2C For Everyone

There was a time in the distant past (in my case this morning) when running I2c natively in Node-Red on anything other than a genuine Raspberry Pi was a nightmare. Not any more.

So in a recent blog I’ve covered a pair of Node-Red nodes which allow GPIO access in Node-Red on a variety of boards using the underlying ONOFF library (which loads automatically when you get the nodes). And that’s all sorted now – GPIO for all – lovely. But what of the exceedingly useful I2c?

Well, if you go looking at Brian Cook’s work - https://github.com/fivdi?tab=repositories  you will see the onoff library – but you might also notice the I2c-bus library.

So off I went and installed it as per the trivial one-line instructions… in my .node-red directory I simply typed…

npm install i2c-bus

Doddle. Of course that leaves the obvious question – what to do next. You can’t REQUIRE libraries in Node-Red the way you would if you were writing some node-js code – fortunately there is a way around this. If you’ve ever messed with your Node-Red settings file you’ll know there is a way to include stuff.

At the start of my /home/pi/.node-red/settings.js file … I have…

var fs = require("fs");
var i2c = require("i2c-bus");

I just added in that last line. The name i2c for the var is entirely arbitrary. Further down in that same file I have:

functionGlobalContext: {
os:require('os'),
moment:require('moment'),
fs:require('fs'),
i2c:require("i2c-bus"),
mySettings:mySettings
},

I’ve just added in that i2c line second from the end.

That means (after saving and restarting Node-Red) that I now have a global variable called i2c which is an object that can be used for I2c control.

The above setup is now in the latest version of “the script”.

In Node-Red itself I opened up a function, pointing an inject at it for testing…

Image636370230037174371

Of course it does not matter here what’s in that timestamp – it’s merely a way to trigger the function.  I have one of those cheap Chinese port expanders – I2c – which you simply fire a byte at along with address 0x20 (32 decimal) and get a byte back – so you get 8-bit port expansion and you can read the inputs of you’d set them as “1” as outputs. Crude but it works – kind of open collector outputs which can act as inputs if you short them to ground.

So – I simply took the example for I2c-bus and simplified it…

var i2c=global.get("i2c");
i2c1 = i2c.openSync(1);

(function () {
i2c1.readByteSync(32, 255);
i2c1.closeSync();
}());

Port ExpanderWith the port expander plugged into the NEO PLUS2 (as an example) I ran the code.

Not a chance – no permissions.  Doesn’t that drive you MAD.  Ok so I took at look at the directory the error message pointed to and it said that it was part of the i2c group.  My user pi – which Node-Red is running as – was not part of that group – ooh, err.

So I made user pi part of that group – and when that failed – I rebooted the processor – remembering you have to do that sometimes when you mess with groups. PROBLEM SOLVED. As a member of the i2c groups – Node-Red could now talk to that chip.

Except that it could not. Timeouts this time.  I considered just going to the pool at this point – but then I remembered… look, there’s  a “1” at the top of that opensync function. Didn’t I have that problem with Python at first as my I2c is I2c port 0  not 1?  I changed the number to 0 and VOILA.

So with the second line changed to i2c.openSync(0) all was well. In the readByteSync function, the 32 is the address of the board – 255 is the value I put out – and that 255 is 8 ‘1’s… setting all the outputs of the expander board to weak HIGH… 254 would set the least significant bit to 0,  sending value 0 would make them ALL zero etc… you get the general idea.

So – from there – it is just a matter of knowing what to send to your particular device – usually a series of control and data bytes.  And that is over to you. The problem of actually using I2c on these non-Pi devices would now seem to be history.   There’s an SPI library from that same writer.. but perhaps we’ll leave that for another day.

Quite made my day, that! Clearly I now want to use this new I2c ability to work with my incredibly useful NANO-based i2c peripheral – you’ll find that on the blog and the latest code here https://bitbucket.org/snippets/scargill/kRg5o – I developed this for use with the ESP8266 it allows for a number of PWM outputs, analog inputs and digital I/O as well as servo control -  all just leveraging basic Arduino capability in a board costing maybe £1.50 – cheaper than most peripheral boards – and now I can plug it into a NEO, or Orange Pi or whatever so that for less than a couple of quid I can add A/D, greatly expand PWM capability and generally add inputs and outputs to my NEO Plus2 and similar boards which I have noticed are not that keen on heavy loads… so – shove it all off onto a cheap NANO!

Lovely!

SO – as there are some arguments to be sent – I need to be able to put stuff into a simple array and that is not as it might seem in Javascript as normal arrays are 16-bits, not 8-bits – but that's easy to resolve once you are aware that it could be an issue. Here’s Node-Red controlling a light on the peripheral.

Nano peripheral

So on the left – a couple of injects-  injecting a simple number in the payload – 1 or 0… and here’s the user function…

var i2c=global.get("i2c");

i2c1 = i2c.openSync(0);
var buffer=new Buffer([6,0]);
buffer[1]=msg.payload;
i2c1.writeI2cBlockSync(9, 1, 2, buffer);
i2c1.closeSync();

As you can see it starts as before – opening up the I2c channel. I then create a 2 byte buffer, the first byte is the port I want to control in my peripheral, the second the value – which is then filled in from the payload.   Finally the buffer is sent to device 9, command 1 for controlling ports, 2 bytes long and here’s the buffer.  I then close the I2c connection.

Similarly for PWM output – only difference being that “1” in the second last line becomes a 4.

PWM

Works a treat.  Change that 4 to 12 on the latest version of the peripheral and you have FADING PWM – so 3 channels – hooked to 3 MOSFETS to run huge lengths of RGB LED strip and the Pi can now smoothly control the colours. Just tested that right now.

The sky, it would seem is the limit. See other features of that peripheral that you can now access directly on these boards thanks to the i2c library and Node-Red.

There’s a video making it’s way to YouTube right now  - should be ready sometime after midnight- just a short one to give you a visual on the above…  https://youtu.be/C0sF3N7SK6c

Here’s the bonus!

I just tested this on the Orange Pi Zero – and it worked a treat.  I also noted that they refer to the I2c channel as I2c channel 0. Hold on a minute – there’s another I2c pair of pins… channel 1. Remember what I said further up about channel 1?  YUP – sure enough by changing the init back to 1 – I can gain access to the SECOND I2c channel. Some day I’ll think of a use for it – but for now – nice to know it works!

Facebooktwittergoogle_pluspinterestlinkedin

13 thoughts on “I2C For Everyone

    1. Firstly please do NOT expect people to reply ASAP in here - we are all doing this for free and for fun. Sometimes you may get a quick answer, sometimes none. On the subject of I2c pull-ups, it depends on the board. Raspberry Pi appears to have pull-ups already. Some I2c boards (port extenders for example) have pull=ups (which should really be removed on all but one board). I don't think the NEO boards have them and so I used 2k2 pullups to Vcc - it could be said that these should go to 3v3 so no guarantee that this is good for your NEO - but that works for me. Others may disagree.

  1. Sorry sir, but i am using ESP8266 test board can i connect pull-ups. i checked i2c pins out put in CRO
    SDA is giving Data but SCL not giving pulse.

Leave a Reply

Your email address will not be published. Required fields are marked *