Master-slave protocol for multiple arduinos on one line

For stand-alone microprocessors

Master-slave protocol for multiple arduinos on one line

Postby Doug Coulter » Fri Feb 06, 2015 1:32 pm

I've been working on a "Lan of things" for the homestead. I ain't gettin' any younger, and whatever I can automate, I want to do. Further, if you have wood heat, as I (mostly) do, and plumbing, you'd like to know when the plumbing was about to freeze, the woodstove needs feeding and so forth - without hiking through the snow to another building to find out. Further, even in this man-cave building, which is huge thermal mass, well insulated - a woodstove, even for a person with decades of experience, requires a knack for feeding it correctly. In this building, the fire can go completely out - and by the time you notice, you have to start fresh. If you try and stay a bit ahead of it, then you wind up wasting wood and having to open windows to keep from sweating - even in 15f temperatures. The time constants involved in the building and stove just don't match human attention span very well. So I'd like to grab sone data, and develop some way of having one of the (many) computers around here just tell me - go put in a stick...Things like that.

As I'm fully off-grid and on solar power - things that are "always on" are a serious issue. As I described in another thread, for this reason (and others) I've chosen Raspberry Pi's as hubs, and Arudino UNOs as nodes for the realtime data acq parts.
This is in large part due to their popularity. As a computer expert - I know there are better choices for mips/watt and so on - but these guys will be around for a long time, like I hope to be myself, and I can afford to have hot spares, take advantage of and share with their large communities (I'm the open source type) and so on - it outweighs the advantages I'd have from picking "just the right stuff" from a pure technical point of view. Things that aren't widely adopted tend to stop being sold. I don't want to have to start over from scratch because of a failure...

Now, most might be happy to hook in an arduino or few with USB - it is, after all, the easiest way, and how I program them. There is also the issue if you're using the a/d on the arduino that the supply from USB is much more likely to dritt than the regulator on the arduino - power it with that instead, since the 5v is the a/d reference. But in "real life" - Pi's don't have tons of USB jacks, I have other uses for those, I don't want to power a hub (never seen one yet that isn't somewhat flakey on top, now and then), so I needed another way. Going back in time, I had developed some master-slave protocols for a customer when I ran C-Lab, so I have a handle on this sort of thing, and came up with a super simple version for this job. In fact, it's so simple it's a case of "beware what isn't there" as if you go to "improve it" you'll probably find that you've created more error situations. Robust protocol design and implementation is not for beginners, and while I can make it look deceptively simple, it isn't.

I decided on a wired-OR async serial situation as the physical layer. With this, we don't use the RS-232 levels, but 0-5v ones, allowing for the wired-OR situation on one of the wires. I'm starting with 115200k baud, as this seems like it will work over the required distances - around 50 feet max, with good cable termination and attention to reflection issues and drive levels. I'm using a raspi as the hub, and it's TX output is amplified both voltage and current to 5v and up to 2 amps(!) via a transistor and complimentary mosfet inverter. It will have a nice large fan-out to drive unmodified Arduino Unos and overwhelm their 1k resistor to the FTDI chip easily. When the pi speaks, they all listen. Period. In fact, this interface can even power the arduino via the protection diodes on its serial port...one has to be careful!

Going back to the pi, we have one wire for input. Each arduino has a two transistor circuit (NPNs) and a pullup, so that any one of them can drive this wire low (the start condition, stop is high). Fairly easy, and doesn't have to be quite as robust as the pi drive, so stuff like 2n3904's will do here. I could design for max fan(in)out - but I don't think I'll need more than a few Arduinos on any one pi anyway. More important will be using impedances that more closely match the cable I use so that reflections won't cause errors, and managing ground bounce. There will be more on how I did this - once I have.

OK, the good part. Here's a sample arduino sketch with just the protocol in it (and a couple dumb commands for testing, easy to strip out). In real life, the command state machine mostly just does a return when called, so most of the arduino compute bandwidth is still available. The pi can send commands to do various things (as defined by you or me), which might or might not return anything at all - yes, we could add checksums and acking and nacking, but there's really no need for this kind of work, and it takes space and MIPs on both ends. Instead, the arduino simply ignores malformed commands, or ones that don't finish being sent in a timeout timeframe - this avoids issues when, for example, the Pi puts out all kinds of garbage at boot (Uncompressing the kernel...Done!), and a 32uS glitch whenever the pi software attaches the pi's serial port - and this is despite doing all the stuff to turn off any spurious output, as mentioned here: http://elinux.org/RPi_Serial_Connection
As luck would have it - this protocol handily and simply ignores all that junk, as long as you do turn off the other use of the pi port as a TTY - if linux has grabbed it, then we can't from userland, and we need it.
Here's the goodies, in "take this code and add your own junk" form.
Slave.zip
Uno sketch implementing protocol.
(2.06 KiB) Downloaded 297 times


The basic idea is that the hub (a Pi in this case) simply goes around, perhaps every 10 seconds or so, and tells each arduino to "report" whatever data it's been collecting. For the moment, I'm having that happen in human readable ascii, and one line per report. If we miss one (due to a line error, say), it's not the end of the world. The UNO might take 10 samples (or more) during the polling interval, and will simply low-pass the results and present the latest output from that when queried.
For most things (temperatures - in (several) and outside, humidity, barometric pressure, woodstove temperature, cistern level, wind speed and direction) that should be fine. If I need a valve opened, or a servo driven, the Pi can send the appropriate command to the UNO with the hardware for that, and it'll work - the movie demo on the "Lan of things" thread shows me just blinking a light - that's not the blink sketch - it's the pi turning the lights on and off, and testing the "report" command, which in this trivial example, simply counts a counter and reports the value - proof it works.

But wait! There's more! Eye candy...
BenchTesting.JPG
My chaotic bench for developing and testing all this.

As mentioned elsewhere (I'll come back and edit this with some more cross-links to other parts of this project on the board, till then - search and you will find), I made up a nice box for a Pi (B+ at present, like everyone, I can't wait for the B-2 but they're all sold out). It has an HDMI display and board, a switching power supply to get to 5.1v for the pi, and of course, the pi itself, which just might become a B-2 when they can be had. It's running raspimjpeg, apache, mysql, a full dev enviornment for various things (mainly, pi, perl, arduino), and is a nice, if slow, general purpose computer and web server. Slow is relative - you might notice the computer in the back, a fast i5 NUC - is showing video streamed from the pi. It actually does this faster, with more resolution, higher frame rate, and less latency than I can manage using another NUC (also i%) and a webcam...the GPU on the pi is actually quite decent...

Here's the arduino kludge with some sensors I used during a recent severe cold snap (well for us it was severe - around 3f) to watch the plumbing and so on. This is when it became obvious that NO| WAY should you go for internet of things. This "meta data" tells all - I can tell whether I went over there to take a dump - or wash dishes or didn't go at all, just from this, and this isn't exactly super-intrusive data collection...Don't fall for that! I don't mind spying on myself, understand, it's just not something for public consumption. All by itself - no camera, this thing collected all too much (very interesting) data, and I plan to max out about 2 of these doing various things. The pi will merely manage it all, and be built into a wall for display and control, as well as web-serving it all on my LAN (only) so I can do things from anywhere else on the 'stead.
ArduinoWithSensors.JPG
Already working arduino with sensors. The long wire was, well, a test of "do long wires work with DHT22 sensors" and yes, they do.


I've developed a water level detector based on an old idea National Semiconductor first put forth (decades ago). You can't use DC current around water - things etch and corrode. But water, pure or otherwise, has a dielectric constant of 81 or so.
This makes any two wires put in it (I'm using titanium) a good capacitor. So, one puts in one big wire, and drives the snot out of that with say, a 50khz 12v square wave - AC coupled. Any other wire touching that water picks up a fair amount of that, and using an AC coupled voltage multiplier (doubler in this case) with an appropriate load (100k in this case) you find you can get a nice analog voltage out from around zero if no water is touching one of the wires, to a bit over 5v (at low current, no danger) if both are submersed. This circuit "triggers" above 2.5v output or so (actually it's the software reading an a/d channel doing the triggering) when more than about 3/16" of any of the sensor drivers is touching water. With 4 channels of input, I can have a decent idea of how much water is in my cistern, at the level of - "is there room for another batch from the rain collector?" or "should I be conserving water use?" which is what I need here. Since I wanted 12v signals, the easiest and cheapest way was to use a simple 555 timer to make those, and just some standard silicon diodes, .1uf caps, and 100k resistors for the "sensors". It works a charm in tests, we'll just have to see how well and how long in real life. But Ti wire doesn't corrode very easily...so I expect that barring false alarms from condensation or something, this will live nearly forever, and it doesn't draw squiddly power.
WaterLevelDetector.JPG
Water detector board


Yes, I will be filling all this in with schematics and more code and so on as I get to it - I intend all this to be public domain. It's just a question of time available, syncing with youtube and that business, and getting timely reports out at all. As was once said "I'll be back!".
Posting as just me, not as the forum owner. Everything I say is "in my opinion" and YMMV -- which should go for everyone without saying.
User avatar
Doug Coulter
 
Posts: 3515
Joined: Wed Jul 14, 2010 7:05 pm
Location: Floyd county, VA, USA

Re: Master-slave protocol for multiple arduinos on one line

Postby Doug Coulter » Tue Feb 10, 2015 4:27 pm

For now, as developed this protocol goes like this, byte wise.
First byte - slave ID number. For this I'm starting at 1 (ascii). Reasoning being that Raspberry Pi spits out some text at boot and no numbers, so...we just give that a miss.
Second byte - command, and for now I'm using ascii for that too. No particular reason except a personal bias to make things human-readable. I'm using r for "report". So 1r<lf> means send me your data back unit 1. I'm supposed to know what that format will be already. It's generally going to be a line of ascii, tab delimited numbers - followed by either a <lf> or a <cr><lf> (which is what arduino defaults to, like dumb windows). It's pretty easy to ignore that spurious carriage return in the master.
Any other bytes, up to 31 total = data if the previous command byte needs data.
<lf> - end of command. "Just do it!"

A slave will time out if it takes too many milliseconds between the first and last bytes. It will also ignore too-long commands. Further, it will ignore any command it doesn't understand, or that doesn't begin with its unit ID number. Whoever is the master has to get it right. If the master expects a reply, it should come back quick, as the slave code doesn't do anything to create long pauses (state machines that return quickly to loop(), not poll-till-ready in tight loops!). If a master doesn't get a reply, it's expected the master will simply try again. The slave will always give the latest data input (perhaps somewhat low-passed in slave software) so the master's time stamp (we're going to be putting this into a mysql database) will be valid, despite possibly variable delays.
Posting as just me, not as the forum owner. Everything I say is "in my opinion" and YMMV -- which should go for everyone without saying.
User avatar
Doug Coulter
 
Posts: 3515
Joined: Wed Jul 14, 2010 7:05 pm
Location: Floyd county, VA, USA

Re: Master-slave protocol for multiple arduinos on one line

Postby Doug Coulter » Wed Feb 11, 2015 12:31 pm

Just a quickie showing going from raw prototype to a build that will get installed, for the arduino part of this project.
http://youtu.be/gbC4ne_Hiew


The Adafruit "Screw Shield" for arduinos is pretty cool for this. I like the screw terminals vs the little stakes for things I might have to go repair someday. And I got a little room to build and test the two-transistor buffer for the arduino transmit line on the proto board (with lots of room left).

Here is the schematic for the wired-OR for arduino serial. This, combined with a beefy driver for the Pi's transmit, and a protocol I developed, allows for one Pi to connect to many arduinos over the same cable - RX, TX, Gnd. The Pi drives all the arduino RX's from it's TX, the arduinos are wired OR from their TX's to the Pi's RX, with a pullup on each device. So far, running 115200 baud seems workable even over 40-50 ft distances.
ArduinoWiredOR.gif
Schematic for the Arduino end of things

This is not a really picky circuit, just build it small and tight. I will publish the one on the Pi end, but I plan to make some changes to it - it's not perfect enough yet as is. I used a pretty large (though in micro-8) complementary FET as the output, with a similar single transistor to get the other inversion off the pi, hooked to 5v for full drive on the wire. It works, but even with a 1k pullup on the FET input, it slew rate limits one of the edges due to miller capacity in the fet. I think I'll wind up with more manageable sized fets and just use two of those in series...one danger, if it is one, is that the FET pair I'm using can do 3+ amps and will sort of power up a bunch of arduinos via their protection diodes. I might have to put a series resistor at each one to make them safe from frying if they in turn have heavy power loads from sensors...Looking into that now.
Posting as just me, not as the forum owner. Everything I say is "in my opinion" and YMMV -- which should go for everyone without saying.
User avatar
Doug Coulter
 
Posts: 3515
Joined: Wed Jul 14, 2010 7:05 pm
Location: Floyd county, VA, USA


Return to Embedded software

Who is online

Users browsing this forum: No registered users and 2 guests

cron