Go Down

Topic: Multi-node CAN bus: a progress report (Read 23910 times) previous topic - next topic


CAN update 2013_10_20

I've not posted for quite a while, and though I've made some progress, it's not been as fast as I'd like.  I'll try to not be too verbose here (but will probably fail at that) - change details are summarized at the ends of the sketches.

DisplayNode: I finally put together a prototype display node using a cheap 1.8" TFT board (Sainsmart) after getting tired of waiting for Adafruit to re-stock a 2.2" higher-resolution board.  In the end, the 1.8" is actually quite nice in this application, and I haven't even had any problems with full-speed SPI despite the resistor voltage shifting for the SD card used on this board.  I will probably change to another supplier's board, however, as this one doesn't give me access to the backlight.  The screen is slow - about 200msec to retrieve and paint a full-screen bitmap, and, as doubling SPI speed (from half to full speed for both the screen and SD card) and tripling the buffer for bmp data (from 20 to 60 pixels) reduces paint time by less than 2%, it is almost surely the controller chip that's the hangup.  Still, it's satisfactory; resolution is quite adequate for what I want to show, and the image is good enough for all but bright sun.

CAN handling for this node differs in one major respect from that in the others.  Use of the display is entirely dispensable, indeed for a simpler chair than my daughter's one might not want one at all, and a display failure is in no case driving-critical.  The only side-effect would be loss of fault logging so a variant system might have an SD card and no display.  Thus, messages are sent to this node without any request for confirmation.  To be sure that the bus continues working even if the display were simply not there, filters in one of the other nodes also accepts and sends an ack for these non-essential messages.  (As current Roboteq motor controller software doesn't provide any real message filtering, just lets you ignore uninteresting messages loaded in it's 12 FIFO message registers, it will ack all messages anyway.)

I started out with Adafruit's libraries and examples, but had a lot of trouble not exceeding both flash and ram limits.  I then switched from using the SD library (that comes with the Arduino download) to using SdFat.  This freed up >6000 bytes of progmen and >700 bytes of RAM, as well as allowing the SD card to go to low-power mode when idle.  The SD library not only has some flaws, it is also a resource hog.

MasterNode and Joystick/SpeedPot calibration: I stripped the electronics out of an old Dynamic DX Attendant Control Unit that Continental Airlines had destroyed a few years ago to see how things behave with a real inductive joystick and decent speed pot instead of the trimmers on the breadboard.  Works great with much less noise, but I did find out why that unit gave a fault after the chair was bounced over on the tarmac.  The Vcc=5V joystick has two outputs per channel: 1 to 4V signal and a 4V to 1V mirror.  The mirror outputs were AFU, and the direct X & Y outputs, though not as far off as the mirrors, didn't match each other and were offset (differently) from 2.5V at mechanical zero.  Even a new joystick would surely have some deviation from ideal and will drift with age.  Moreover, there are many models of inductive and Hall-effect joysticks out there, with each retail supplier perhaps having just 1 or 2 available.  I therefore wrote a little calibration sketch to store neutral, minimum and maximum values in EEPROM and re-wrote Master to use these rather than hard-coded numbers.  While I was at it, I made that sketch also store min and max speedpot values as well.  (The spans for these signals are deliberately less than 0 to Vcc so that a broken wire can be detected as an out-of-range 0 or Vcc.)  While I was at it, I did some clean-up of the deadband, scaling and curving code and provided for the fact that one joystick model might be Left=low, Right= high and another might be Left=high and Right=low etc.




b]Power control:[/b] I found that my idea of simply turning non-Master node DC-DC converters on and off based on the value of (CAN-hi+CAN-lo)/2 was not adequate.  With just two nodes (and even with 3 - mostly), with Master off and the non-Master transceiver in standby (CAN-hi+CAN-lo)/2 drops low enough to turn off the non-Master converter.  With four nodes (one off, and 3 in standby) it does not.  I have therefore added an NPN low-side switch to the control line in the non-Master converter that gets turned on by a digital pin of the Nano.  As before, (CAN-hi+CAN-lo)/2 turns the converter on, but when the non-Master gets the CAN message to shut down, the converter's control pin gets pulled low.  (A small capacitor is also needed so that the shutdown is not so brief that the Arduino just goes into a brown-out reset.)  I also wired up an old 18.5V computer brick so that I didn't have to go down to my "dirty-work" shop's 24V batteries to test this (which I hadn't been doing routinely enough).  Now, the DC-DC converter's are always the primary power source and I should, hopefully, spot screw ups when they happen rather than a few weeks later.

Revision organization: To keep track of what I'd been doing, I sequentially numbered each node's sketches as they got modified, and archived the older ones.  Then I discovered a bug, I don't even recall now what it was, and had to backtrack through that archive to find what change had caused it.  BUT, I had no decent way to know which revision number of NodeX was really concurrent with NodeY, and mismatched programs might not work for reasons having nothing to do with the bug.  I've therefore changed how I'm tracking things, but really don't know if there isn't a yet better way to do things - suggestions would be welcome.  What I'm doing now is that each day that I work on software I copy all elements of the previous version into a fresh, dated folder.  If I then discover a problem and have to figure out when it got introduced, I will at least have coherent sets of nodes.

High current circuitry: This mostly has naught to do with Arduino, except that some functions will be handled by the AuxNode as the Roboteq doesn't have quite enough pins.  Nevertheless, I've included schematic and PCB layouts in hopes that you can spot weaknesses.  A couple notes are in order: (1) safe use of the Roboteq requires addition of a high-current contactor, fuse, pre-charge resistor etc.; I'll also have a plug connected to a wrist lanyard as an emergency stop device, (2) in order to use IR motor compensation at low outputs accurate motor currents are needed, but the Roboteq estimates motor current as (battery current*PWM on fraction) so gets less and less accurate as current goes down.  I've therefore included Allegro Hall-effect current sensors that have some rather tight PCB requirements.  (3) I've also included smaller current sensors for the brake coils, but don't plan on mounting them in hopes that the Roboteq battery current parameter will have enough resolution to detect coil faults.  (4) To control seat lift and tilt actuators I've used SPDT automotive relays in H-bridge, self-braking configuration.  Both physical limit switches and current drain monitoring will be used to detect end-of-travel and alter chair speed for less-stable CG conditions.  If more than two actuators are wanted, another relay board would have to be added.  (5) the contactor and actuator relays have snubber resistors rather than diodes to avoid compromising contact break time, (the Roboteq digital outputs are 1 Amp continuous low-side MOSFETS) (6) For LED lights, I've chosen STM Pentawatt-V power switches that provide control of moderate currents without needing added components.  (7) The circuits are sprinkled with transient voltage suppressors - I hope enough of them and with the right specs.

To do: An RTC has to be added for time-stamping the log file.  I have a  DS3231 board on order, but fear that it's been caught in the Hong Kong and China post decision to X-ray everything and send anything that might contain a Li cell back to the sender.  We'll see.
   I have to get an accelerometer to add to the Aux node and port the code I now have running in a free-standing Uno on Rachi's chair to provide shock/stability sensing.  This, along with the actuator limit switches/current monitoring will trigger the Stop, VerySlow, Slow and full-speed states of SpeedSlow already included in the sketches.
   I have to place orders for boards and components.  I've not found a single supplier who stocks everything, but between Mouser, Digikey, RS, Farnell (possibly) and e-bay I think I have a BOM.  e.g. RS.it (but not RS.uk) has a good price for the contactor, IF I'd buy a minimum of 25 of them, so I paid a higher unit price to Mouser to buy one rather than 25, but Mouser doesn't stock decent joysticks.
   One thing still not decided is connectors.  For the breadboard, I've just used the usual male & female headers, but on the deployed version I intend to connect the small stacked boards with male headers only, soldered to the boards.  For the few off-board connections (joystick, CAN, 24V) I'll use some robust Molex variant (and add a dab of Goop to each; from experience a useful backup).  Higher current connections will be ring terminals, Anderson SB50, power-pole and XT connectors as appropriate.

Schematics, pcb and program files are in the two zipped attachments.



Good morning,

Revision organization: To keep track of what I'd been doing, I sequentially numbered each node's sketches as they got modified, and archived the older ones.  Then I discovered a bug, I don't even recall now what it was, and had to backtrack through that archive to find what change had caused it.  BUT, I had no decent way to know which revision number of NodeX was really concurrent with NodeY, and mismatched programs might not work for reasons having nothing to do with the bug.  I've therefore changed how I'm tracking things, but really don't know if there isn't a yet better way to do things - suggestions would be welcome.  What I'm doing now is that each day that I work on software I copy all elements of the previous version into a fresh, dated folder.  If I then discover a problem and have to figure out when it got introduced, I will at least have coherent sets of nodes.

This looks as if you were searching for an automatic revision management system (GIT, SVN, CVS etc.). The choice of the moment is probably GIT, and as you are working in an open-source logic, you should take a look at github.com .

Then I went quickly over the schematics, and I learned from it. Thank you for sharing.
One question: where do you source the annealed copper bus bar, can you give any references ? (I failed lately to find such material with the distributors.)

Congratulations for the big progress made since your last writing,


Hi Bernt,

Then I went quickly over the schematics, and I learned from it. Thank you for sharing.
One question: where do you source the annealed copper bus bar, can you give any references ? (I failed lately to find such material with the distributors.)

Ha, ha.  A piece of plumbing tubing, cut, flattened and annealed.  Copper is easy to anneal, heat to cherry red and let cool (cooling speed is unimportant).  I usually dunk the hot metal into cold water because it helps clean it.  Flatten some more, clean up with a rubber abrasive bit in a Dremel, flux and tin.  You can buy (hard temper) busbar material from McMaster-Carr, but they no longer ship outside the U.S., it is expensive and, not being annealed, very hard to bend.  It's slightly higher purity, but I'm using far more cross section than needed.  Copper work hardens very quickly, so if you have to work it you may also have to re-anneal it.

I'll take a look at GITHUB, but if there's a significant learning curve to using it I probably won't.  To me, the progress feels very slow.



Apr 13, 2014, 11:10 am Last Edit: Apr 13, 2014, 11:20 am by LROBBINS Reason: 1
It's been quite some time since I've posted an update.  At this point I have a working prototype that awaits only purchase of a Roboteq HDC2450 and its programming to actually try it out in a semi-real-world test.  A lot of work has been done on both the hardware and software, but for now I'll post mostly photos and a (relatively) few words and leave the program and PCB files for another time.

First, an overview of the system:

There are four CANbus nodes: Master (with joystick, speed pot and some switches), Display (that also has a micro SC card and a real time clock), Aux (housed with the power distribution boards) and Roboteq emulator (which is not in a box as it will disappear in the real version).  Each node contains three boards: an Arduino Nano compatible microcomputer, a CAN controller/transceiver board and a DC-DC converter board.

The fourth box is a switch interface that provides three isolated outputs for each of five switches.  One set goes to the WC controller (DB9 cable), one to the PC (USB cable) and one, for any other use, to a set of 3.5mm jacks.  It also has a relay with latching logic that is here plugged into two of the 3.5 mm jacks.

The Master node:

There are three switches and several connectors: momentary contact pushbuttons for Power and Mode and a rocker to switch between Attendant (joystick) and Driver (switch) control.  The 4 wire CAN bus cable (CAN-hi. CAN-lo, 24V and 0V) uses USB connectors, and the switches (in my use, the switch multiplexer box) use a DB9.  There's also a 3.5mm jack for an attendant's stop switch - this is not an emergency stop that opens the Roboteq contactor, but a normal stop that toggles with a remote button in case Rachi is going where she shouldn't.  It's a rather crowded little box from a defunct Dynamic ACU.

Display node:

Another even smaller box that houses the three CAN node boards, a 1.8" TFT board with micro SD card holder modified with addition of a P-channel MOSFET so that the backlight can be controlled from the processor, and a highly accurate real time clock with backup battery.  More about what the display shows later.

To  be continued ...


Apr 13, 2014, 11:32 am Last Edit: Apr 13, 2014, 11:39 am by LROBBINS Reason: 1
Aux node and power distribution boards:

The usual three boards are underneath, alongside the smaller power board.  The top, larger, power board has the contactor and battery connections, and H-bridge relay pairs for seat lift and tilt.  The lower board has motor current sensors and solid-state switches for lights.  It also connects to logic-level circuits of both the Roboteq and the Aux node boards.  Connection to the boards is by crimp-and-solder ring connectors, or direct soldering for light gauge outputs.  The heavy Roboteq wires will be directly connected without other connectors, but battery motor, brake, light and actuator lines will connect with a variety of connectors suited to the currents involved.  I have a variety on hand (SB50, Anderson power-pole, XT60, EC3, 4mm SupraX) and will use different ones for different functions to try to keep me from plugging the wrong wires together.

The contactor is a heavy (130A continuous, 300A breaking current), normal (not latching) relay which, in normal use, opens whenever no Roboteq outputs are called for and the brakes have been set.  Thus, it will be switching quite frequently, but with only the ca. 100 mA quiescent current of the Roboteq across the contacts.  In an emergency, it will be opened either by the Roboteq's runaway protection algorithm or by pulling the shorting plug from the box.  In non-runaway fault conditions, such as a joystick failure, Master first tells the Roboteq to stop everything, then opens the contactor.

The DC-DC converter on Master is directly controlled by the power switch, while the DC-DC converters in the other nodes are controlled via the CAN bus - no extra wires are needed.  Power to the Roboteq's computer is controlled by the Aux node via a reed relay.  Off is really off - only microamps of parasitic load, and standby load (except for the Roboteq that doesn't have a sleep function) will be only a few mA.

Some examples of display images:
For demo purposes, I've programmed some visible marker dots into the display.  They won't be there in actual use and I didn't fuss much over programming them, so bits of the main image get erased as they move about.  Voltage is measured in Master by interrogating the Roboteq only when there is no drain, and an average of several such measures is sent to the display.  Ahr is accumulated from Roboteq battery amps, and reset to full any time the system starts up just after being charged.  This will be misleading in two situations: if the charger is unplugged for some time before the controller is turned on the gauge won't go to full and if the batteries haven't been fully charged when the charger is disconnected it will show full even though it's not.  Not completely satisfactory, but it's the best I can do as I have no way to monitor current replaced using a basic charger.  There is no warning icon for low voltage.  Instead, Master reduces maximum output by stages to a limp-home level if the voltage gets too low.  Of course, the Roboteq can be configured to shut things down completely if one persists beyond all reason.  Several levels of drive inhibit - slow, very slow and stop - can be triggered by limit switches, accelerometer or what have you.

I'll stop here.   Though I could go on for pages and pages, I think this is enough to show the state of the project.  I have no wish to become a wheelchair controller manufacturer.  Rather, I'd like to provide a starting point for those who want to have a controller that can be personalized to the individual rather than the other way around.  For me it's been an intensive learning experience.   I'd never before done programming of embedded micro-computers. Indeed, although I've written data analysis programs in a several languages, this is the first time I've used C or C++. Most of my learning of electronics dates back to vacuum tube days, though I've done a bit with semiconductors and ICs - the switch interface box in the pictures is just a miniaturized version of the one that's been on Rachi's chair for years. I'd never worked with SMD components before. I'd done some fairly complicated hand wiring, but never laid out a PCB more complex than what can be etched in a pyrex baking dish. I knew what CAN is (vaguely), but had never actually worked with any communications protocol. I've never dealt with high currents, and even now I sure wouldn't want to design the motor controller itself. Virtually all of this is new to me and there is a certain amount of satisfaction just in being able to learn so many different things and put them together.

Whether this will be anything more than a personal exercise will depend on whether anyone else uses this as a starting point. It may be just another instance of Lenny tilting at windmills, as I've done more than once in my life.



You've certainly done a lot of work, well done.

Rob Gray aka the GRAYnomad www.robgray.com


So I'm way late to this thread and it sounds like you're mostly done building, but a couple of points:
- CANopen costs money only if you want to register a vendor ID, have your product tested for compatibility and label it as such.  The protocol documentation is free to download and use for private purposes.  DeviceNet on the other hand does cost a lot to get hold of.  CanFestival runs on AT90CAN devices (and linux) but I haven't seen it ported to mega328+MCP2515 yet.
- For application-layer protocols for vehicles, look at SAE J1939 (sadly not free).
- CAN is obviously a distributed system, and it's impossible to overestimate how difficult this is to do safely if you haven't worked in distributed systems before.  You need to at least figure out what happens if any particular node or message is lost or any one conductor (on a sensor or output device) is broken or shorted and how that affects the behaviour of the system.
- For connectors, I would look at automotive-style plugs like Molex - the style of connector you see in an engine bay wiring harness.  They're pretty reliably in cars, which are a very hostile (temperature, EMI and vibration) environment.


Dear Polyglot,

Working on very similar hardware (http://wiki.splashelec.com/index.php/CANinterfacer), we took a look at CANopen about a year ago. We finally abandoned that idea, because of:
- lack of open documentation
- lack of use in open source projects (no code to reuse)
(These two come probably from too limited "openness".)
- the limited performance of our 8-bit micro-controller platform is not suited to
  host-less bus configuration using CANopen.

As for higher level protocols, I would be really interested in J1939, especially because of its use in the marine NMEA2000 protocol. But these protocols are so closed that we can not even read the specification without paying. (My bet is that someone will release open code permitting to passively read from these networks, permitting to get data into open source buses.)

Our solution is to develop our own higher level protocol. This is work in progress, and not yet usable, but promising, it will solve our problem. I recently spent time to explain that work to a new student group, they are now up to date and started coding. New code will show up shortly at "https://github.com/splashelec/splashelec/tree/master/software/libraries/SimpleCAN".


PS to Lenny: I'm still following your progress, you are now close to get it on a real chair, great!
I have "remote power on/off + emergency shut off without extra wiring" on my todo list for the next version of the CANinterfacer. For the moment I have no immediate need for a next version, but with what I learned from you in this thread, I think I must try to get that feature in. There are two 'NC' pins on the Arduino Micro pin-out used for the CANinterfacer, these could become connections for passive on/off and emergency stop buttons.


Hi Bernt,

It seems to me that CANfestival is open source and does CANopen, though I don't know if that existed when you were starting out.   I haven't ported it to a 2515 yet.  I would like to keep CANfestival compatibility because it runs on linux and seems to be quite mature; I intend to have a linux (beagleboard black - includes CAN!) head node which will run all the CANopen management daemons that are too memory-hungry to put on an AVR.  My AVRs will be simple slave IO devices with very little internal logic.

If you would like the full CANopen documentation and CiA are ignoring you, I can send you a copy.


After several years of successful use, and with a variety of refinements made to the hardware and software, I've just uploaded a revised version of the complete system, hardware and software, to GoogleDrive.  I've also included an analog version of the Roboteq script for those who might want to try it (but, warning, the non-CAN version is mostly untested so do be cautious). The files are at:


Go Up