My “nano peripheral” started life as a simple attempt to expand the ESP8266 by making use of the IO on a really cheap Chinese board, an Arduino Nano equivalent Chinese £1.20 rip-off (After all, any Arduino is basically little more than an Atmel chip with reset and power circuitry and a “bootloader”).
UPDATE: Now working on IR – receiver already tested – see experimental version further down…
The idea was to build a dirt-cheap I2c peripheral, suitable for use with my ESP8266 code but now, with improved access to i2c on various boards such as the Orange Pi Zero (2 i2c channels), FriendlyArm NEO, M3, Raspberry Pi and others (see blog entry on i2c) it makes sense to look at this for general use – and with a widened range of capabilities.
Regular readers may recall some time ago I was buying all sorts of parts – A/D converters, port expanders etc. to help with my solar monitoring – and so some time ago I had a shot at making an I2c peripheral using an Arduino Nano…
I even went as far as putting a display on the board – which I never ended up using and it occurred to me that it might be better to have the extra pins for general IO – that applies more than ever as my own ESP8266 now caters for the likes of the SSD1306 OLEDs as does widely available software for the likes of the NanoPi NEOs (also covered in the blog). Hence this new version of the NANO section of my home control diagram – but this is stand-alone – as long as you know how to use I2c you can use this.
What is i2c? Well, it’s a simple serial protocol involving 2 wires – one for the “clock” and the other for “data”. The “master” unit controls the clock and either sends data to “slaves” or receives data from them – or both.
As well as for the ESP8266, this makes a useful peripheral to control from one of the many single board computers such as Nanos, Raspberry Pi etc. Some of these boards do not like any significant load putting on their GPIO and let's face it, PWM on most of these boards is not that clever due to the operating system not really being real-time. Most have ONE proper PWM channel if you can figure out how to use it – yet for VERY little money you can have so much more. I've referred to i2c in another blog - it is now easy to use on most of these boards WITHOUT root access (sadly some folk can't see past this and keep producing stuff that needs root access).
I recall trying PWM on a NEO to soft-fade a LED and while I could get one hardware supported PWM output, the software PWM was atrociously noisy. So immediately here, instead, you get half a dozen perfectly useful 8-bit PWM outputs on a little board costing less than a beer. Not only that but I’ve added a soft-fade version of PWM so that for example with RGB lighting you can fade from one colour to another without any other overhead or delays.
So above, in one scenario, you see where it fits in the scheme of things (the green block entitled “Nano Expansion” – my general controller is an ESP8266 board with a boatload of properties – one of them being able to talk via I2c to various devices.
So the idea of this device is a “jack of all trades” - unfeasibly cheap yet having useful specs – for example the A/D converter inputs are 10-bit – perfectly adequate for measuring battery voltages (you might need resistive dividers as the Arduino A/D input is 1.1v max in this case for value 1023).
This new device then is (by default) device 9 on the i2c bus though you can change that (stored in EEPROM). Some pins are useful – others not so much – here below is an image of this “peripheral” which, hardware wise is nothing more than a programmed up Chinese £1.50 “Nano” board.
So as you can see (and bear in mind that not all NANO devices have the same pin-out) I’m making reasonable use of the pins. With I2c you can set outputs, read inputs, read analog inputs and set PWM or servo outputs as well as sending a message out through the serial port. You could of course get up to 14 PWM pins by using the Arduino Mega256 - but then the cost of that, even from China puts it way more expensive than a dedicated PWM peripheral - the magic of the Nano from China is that it is cheaper than most peripheral chips.
Don’t forget however if you are using this device on it’s own with no other I2c devices – you may need resistive pull-ups on SDA and SCL - certainly if driving this from an ESP8266. I use 2k2. If driving from a Raspberry Pi, well that has it's own pull-ups. And of course you may want more than one device – if so, one at a time you’ll want to reprogram their number from the original 9, taking care to avoid numbers that might be used in other i2c peripherals you plan to add. Alterations to the device number are stored in EEPROM. Changing device numbers is as simple as any other command.
If using this with my ESP8266 control software you need version 1.91 or better (updated 11/06/2017) as there was a timing error with earlier versions which prevented ADC reading.
I’m connecting this to the 5v supply on a WEMOS D1 (this Nano has it’s own 5v regulator) and no conversion of voltage levels is needed. Similarly I’ve plugged this into various NEO/Orange-Pi-Zero type boards with no ill effect (indeed up to now I’ve not even needed the pull-ups on these but I’m sure that’s a fluke with some – the Raspberry Pi DOES have pullups on the two I2c pins).
The Home control manual has been updated so you have the commands in there – and the source code for the peripheral is on BitBucket and is likely to grown in the future,
I added in servos after an almost expensive learning exercise. You can control servos on any of the pins 2-13 - that’s a lot of servos – but as I found out – you can’t just run servos off the supply, say USB to the processor board. These things can take a fair bit of current (not control signal but power) so make sure they have adequate power – just one of them I tried was using 250ma when moving from A to B and that was just a little £4 toy servo (I’m talking power to the servo, not signal here). Consumption when still was nothing significant.
I learned a lot doing this – a quick look through the web showed that Arduino supports only 2 servos at once – and that PWM works – well both of those are WRONG. PWM does not work and made one of my servos get very hot… and the info out there about how many servos the Arduino can handle was also out a date – a simple trip to the Arduino site itself shows that the standard servo library can handle lots of them. So PWM is about the ratio between the ON time and the OFF time – servo driving is about the actual ON period.
So - I made servo control available on all of the pins up to and including 13 – so that’s 2-13 – not just the pins that handle PWM (use ANY servo and you lose PWM on pins 9 and 10 – no big deal there).
I’ve also made use of the Arduino TONE command, letting you play simple tones either continuously or for a defined period. Be aware that you cannot use the PWM on pins 3 and 13 when using TONE as they share the same interrupt to do the job. This is a function of the Arduino code, not mine.
On wiring for servo use – and this will vary from servo to servo – I found (despite not finding this on the web) that of the 3 wires – brown, red and orange – the brown was ground, red was +5v and the orange was a signal wire I could put straight onto a pin on the nano.
One of the annoying features of some Nano boards is lack of power and ground spares. Check this one out -
I love these chip – pretty easy to understand and long ago I figured a way around the delay as long as you don’t need to SEARCH – so that’s one chip per GPIO – generally. For a particular project – my hot tub solar heating, I needed two temperature sensors – in and out – as it happens and decided that was as good a reason as any to add this to the Nano project.
Well, did I have some learning to do – even with the fast speed I found that the i2c requestEvent was occurring before the receiveEvent was finished. We’ll cut a long story of dead ends here – but suffice it to say I decided that as this only affected the few commands that RETURN info – ie INPUT and now DALLAS – I would add a third byte to the return value reflecting the STATE of the receiveEvent – done merely by setting a byte to 1 at the start of that event – and resetting it at the end. The requestEvent (ie sending data back) could not look at that FLAG.
Here you see this in action in Node-Red on a Raspberry Pi – this is the simple INPUT command – ensuring you’re getting the data from the current command and not the previous one..
i2c1 = i2c.openSync(1);
var buf2=new Buffer([2,8,0]);
i2c1.i2cWriteSync(9, 2, buf2);
} while (buf2==1);
Lots going on above- the WriteSync command sends a 2 byte buffer to device 9 – and in that buffer we have 2 (command 2) and pin number (8). We then look at the return 3 bytes until the third byte is TRUE… i.e flag says we’re done. In the case of this command that will be REALLY fast. We then output the data in this case a 1 or a 0.
and here it is in action for the DALLAS chip.
i2c1 = i2c.openSync(1);
var buf2=new Buffer([15,14,0]);
i2c1.i2cWriteSync(9, 2, buf2);
i2c1.i2cReadSync(9, 3, buf2);
} while (buf2==1);
msg.payload=(Math.round( (result/16) * 10 ) / 10) +"c";
Here I send command 15 to input 14 (A0). Again, when READING the return data I read 3 bytes – the last one has the flag as above – so I can check if the processing event is still going on and loop until it isn’t. This all happens instantly for all practical purposes – we’re not talking hundreds of milliseconds or anything near it.
So how does this work? I issue the command to get one of up to 2 Dallas chips (on different, selectable inputs) – my Dallas command reads the state of the chip – THEN starts the conversion process – which means the first reading is always naff – BUT because I have the pin stored in EEPROM – the very first thing I do in the setup is to read one or two Dallas chips- so their conversion has already started by the time the Nano is ready to go – so you never see the duff result except when CHANGING pins – as the working pins are stored in this case in EEPROM.
You are of course reading the temperature as it was at the LAST reading but on the assumption that the chip is read – lets say every 10 seconds or every minute or 5 minutes – whatever – I can’t imagine that being an issue.
In the case of simple inputs – the flag assures that you are getting the CURRENT state of the input.
Use with Node-Red on an Orange Pi Zero, NEO or similar:
Assuming you have i2c running as per my blog entry on the subject…
Current commands are:
|SET_OUTPUT||1||port and value||value 0 or 1|
|READ_INPUT||2||port||3 bytes read… byte 2 is the value, byte 3 is the flag – 1 means busy **|
|READ_INPUT_PULLUP||3||port||3 bytes read… byte 2 is the value, byte 3 is the flag – 1 means busy|
|SET_PWM||4||port and value||value 0-255 (corrected for vision)|
|READ_ANALOG||5||port||returns 16 bit value (10 bits used)|
|SET_ADDRESS||6||new 8-bit add|
|SEROUT||10||Up to 126 bytes||add zero at end of string|
|SERVO||11||port and value|
|FADE||12||port and value|
|TONE||13||port and value||value 16 bit, optionally add 16-bit timeout in ms|
|NOTONE||14||port||only used if no timeout used above|
|DALLAS1||15||port||Should reset after making a change if the first reading is important. 3 bytes – the first two represent the word value of temperature *16, the third byte is a flag – read until zero **|
|DALLAS2||16||port||Should reset after making a change if the first reading is important. 3 bytes – the first two represent the word value of temperature *16, the third byte is a flag – read until zero **|
i2c1 = i2c.openSync(1);
var buffer=new Buffer([1,11,0]);
i2c1.i2cWriteSync(9, 3, buffer);
Here, 3 bytes are sent to device 9 – 1 for command, output 11 and in the next line we set the output value to whatever is coming into the Node-Red function.
Getting info BACK for example from the A/D inputs…. that’s command 12
i2c1 = i2c.openSync(0);
var buf2=new Buffer([5,14]);
i2c1.i2cWriteSync(9, 2, buf2);
} while (buf2==1);
Note this time I use the simpler basic i2c commands… these could also be used in the previous example.. instead of sending a command with a buffer (a command is just another byte)…
This Node-Red code is thanks to an i2c NodeJS library added into “the script”. If you don’t use this – look at the i2c blog article as you can add it to your own installation.
In the above example we are retrieving a 16-bit value from the A/D (0-1023).
Latest version stored here -https://bitbucket.org/snippets/scargill/kRg5o
The internal Nano code above is ALL that is needed to return the temperature to a reasonable degree of accuracy (10th degree) – note the code has been checked for negative values – testing with freezer spray to –20c.
I’ve added this for up to 2 sensing pins. A simple read will pull in the temperature to 0.1 degree repeatability – the reading value was calculated after the LAST read or after power up..
With VERY little extra effort - but considerably more money at nearly £7 inc postage - you could use a mini-MEGA board to get a LOT more IO - https://goo.gl/ZyTmt6 - I put that there as it is a neat board but the price really does make the difference between worth doing and not worth doing. IMHO the little Nano rip-off boards do take some beating for the above purpose.
Timers: Bear in mind when considering the use of PWM, the beepers and servos… there are only so many timers on the Nano and similar boards and so you can’t do everything at once:
Timer 0 is used by the millis() function – necessary for loads of timing operations – for example I use this to control the fade-out of PWM LEDs.
Timer 1 is used for the servo library
Timer 2 is used by the tone library
When using PWM – timer 0 is needed for pins 5 and 6 – this does not affect millis(). Timer 1 is used for pins 9 and 10 and timer 23 is used for pins 11 and 3. So bear this in mind when considering what you want to do with the board.
The Experimental Version – I’m currently working on an experimental version which includes infra-red - the IR receiver side is tested, working and does not interfere with PWM – but I’ve not tested the IR TX (or documented it) – the new version has a 6-byte return package (last byte status) to handle the IR 32 bit number. Returns NEC and similar formats no problem – I’ve stopped it returning repeats (ffffffff). Of all my IR remotes the only one it does not currently return is the TCL TV (probably as it is cheap).
Temporary link in case you want to have a look… https://bitbucket.org/snippets/scargill/yR5nkB
and here’s how this one looks…
I’m about to go AWOL for a couple of days so I thought I’d just park that there for now. Way better instructions will follow if I don’t hit any snags.
The magical 1284 - for more pins you could look at the Atmega1284 - a lovely chip which some might argue is even more useful than the Atmega 2560 – check out this link for driving from Arduino - https://github.com/MCUdude/MightyCore and here is my all time favourite article on this chip – including pin-outs - https://maniacbug.wordpress.com/2011/11/27/arduino-on-atmega1284p-4/
I have the code running on a 1284 right here – trivial changes to some array sizes and pin numbers and it works just fine – note that the 1284 (which of course is more expensive than a little Nano) is available both surface mount and as a 40 pin DIL package – and has 8 analog inputs, 7 PWM outputs and in total 31 usable pins!!! D16 and 17 are used for I2c (no change to i2c code). I only made this work as I have lots of these boards lying around.
And now for something completely different…
Unfinished experiment which may never finish!
Meanwhile we have in here a discussion about the STM32 boards and there is indeed a very cheap version which would make a superb peripheral. The board is as cheap as £1.30 with free postage in the UK – sadly the postage in Spain is a further £1.31 which kind of kills that off though buying several at a time would offset that. I should stress at this point that i2c slave software is not yet working properly, awaiting library improvements – but as the boards are so cheap I’ve included mention of them.
Here is a diagram to show you what it can, in theory, do.
Getting anywhere with this board has proven to be an up-hill struggle with some STM-Arduino libraries not even mentioning I2c slave – and lots of dialog out there as to how it does not work. Well, as of tonight – I have Arduino environment 1.83 and the “official” STM library which includes the “Bluepill” board we see here and I’ve managed to get i2c RECEIVE working – and have sent the results of a multi-byte package to the serial port.
You’ll notice here the same issue that other drawings have – the SDA and SCL lines appear to show 3 sets of i2c – two of them with the same pin names – that needs resolving. For now… I have 0v and 3v3 going to that edge connector at the top end. I have an FTDI firing out 3v3 and ground to that. The FTDI is also firing serial signals to serial1 TX and RX - and this is referred to as “Serial1” in the Arduino info for sending data – i.e. Serial1.print etc. That works. The green light (the rightmost of the two in the drawing above) is called PC13 – and can be referred to that way in digitalWrite(PC13,0) etc.
Meanwhile I’m using the SDA and SCL lines referred to as PB_6 and PB_7. For programming you can put a new boot in there but to keep things simple, see the two yellow links above. With the LEFT one moved UP, you can program the board after reset using the serial mode of connection… the board starts up after programming but after root goes back to programming mode. With that link removed or DOWN as shown, reset forces a restart – consider it like GPIO0 on the ESP8266 !! Hmm, a button would be better. Press before reset to program….
Anyway I have I2c data going OUT to the board… next job is to get data BACK to Node-Red. Right now the Bluepill callback to send data back is not even being called even though a NANO version of exactly the same code works… and there is no working solution as confirmed by people who did the software – indeed one suggested I might consider implementing that myself – but that requires more knowledge of these particular chips than I care to acquire.
As for Roger Clark’s Arduino repository – we can forget that for i2c slave. I wrote on the subject and the latest post in there is “Closed as there are no plans to support this feature”. At the end of play today he is suggesting someone else write this – so I think we can write this one off for the purposes of this article.
It is all down the official repository now….
But it gets worse – if you look at the STM diagram above – most of those pins which are supposedly PWM – are not in the official software for the Bluepill board (which apparently isn’t official yet) – so while I got some of them to work (PA7 for example) – others don’t.
Here we have a “fix” for the slave RX/TX issue
I very carefully added that and made the changes to my sketch – made no difference whatsoever and it still crashed.
All seems a little messy. Between that and the PWM, I’m parking this chip – last time I parked it, it took a year to get back to this and still not a lot further forward so don’t get too excited – for now, the NANO solution works perfectly (as do variations with other Arduino-type boards) and there is more to come.