I’m reading the 2013 C++ WPILib and noticed that it calls the CAN driver’s receive (source not available) with a 0.0 second timeout to discard stale ACKs. That only makes sense if the CAN driver buffers messages, so I hope it does.
Anybody know how async messages are buffered in the CAN driver? Is it deep enough to reliably see ACKs from several Jaguars? Hello parallel CAN requests!
If it’s a per-node buffer, can it reliably buffer Jaguar restart announcements?
It may be possible to detect Jaguar brown-outs by looking for those announcements with a 0.0 second timeout. That’s much much cheaper than probing for a power cycle. Last year, Jaguars would reset very rarely, but we never solved it.
This year we’re using a 2CAN instead of the serial gateway if that matters to the answer.
In industry, a good CAN stack will usually do one of the following:
-Use an ISR or other interrupt-like task to deal with messages as they come. This is usually done for the rare protocols which use a single ID for all of their messages (CCP is like this).
-Store the latest message in a buffer. There are several variants to this implementation. Depending on how tightly the OS and application are integrated, the buffer either stores the latest message by ID (one message per ID deep) or it depacketizes them into the values within which are stored as variables, and other software reads the variables to get the values when it wants the data.
The ideal way to do CAN is purely isochronously. Every loop, in an ideal world, the Jaguar would send out all of it’s status data at a frequency which is fixed (100hz seems good for a 1M CAN bus). Every loop, the application code on the cRio would send out all important dynamic Jaguar parameters in a single message, using all 8 bytes to save packet overhead. Less important data gets sent less frequently, e.g. 10hz or 50hz or 20hz, to reduce bus load.
For some reason, the IMHO REALLY BAD DESIGN current CAN stack waits for an Ack on everything by default (the Set can act isochronously, but nothing else does). It should just spam the Jaguar with a configuration packet at 20hz and setpoint/speed commands at 100hz and be done, and the Jaguar should pack it’s messages more efficiently (e.g. full 8-byte utilization, with sub-8bit parameters packed tightly). This would allow you to send all of the non-static configuration parameters fast enough to not care if it resets and not monitor for it.
So, to answer your original question, a well-designed CAN stack would buffer exactly one of each message ID, the latest one, but the FRC CAN system isn’t exactly ‘well-designed’.
If the cRIO CAN driver buffers messages by node ID, what you describe is possible in user code (the WPILib C++ we have source code for). Isochronous status reporting by the Jaguar needs a firmware update, right?
What I’d like to do is register each C++ CANJaguar object with a background task that listens for hardware resets and user configuration changes, and then keeps the Jaguars in sync with the CANJaguar objects. If it works better to just spam configuration messages that would be easier, but there may be several messages per Jaguar and that will chew up a lot of bandwidth.
Tomorrow I can spend some quality time with a test bench, but I’m still hoping somebody will tell us how the CAN driver works.
I can’t seem to find a CAN message spec for the Jaguar, so all of this is based on reading the LV CAN implementation and trying to figure out the messages.
It looks like almost message contains only one data field. So, if you were to send every data configuration field at 5hz you would probably be good, but there’s a lot of overhead (a Complete CAN frame is almost 100 bits for a mere 16 bits of data, you could package an additional 6 bytes for only their space in the message (e.g. adding 6 more bytes to the frame without additional header space) aside from bit stuffing, and the protocol would be more efficient, but unfortunately for most messages it is not efficient.
It looks like there is a Message ID which is OR’d with the jaguar ID to find the CAN message ID.
It looks like the only exception to this is the scheduled status frames. 4 messages (Periodic Status Config 0-3) are built with the 8 bytes corresponding to the byte ID of the data requested. Multibyte data has multiple byte ID’s, one per bit. Another transaction (with Ack) is done to set the speed, using Periodic Status Enable 0-3 and a time in milliseconds.
The Jaguar would then return the status data at the frequency you request. The Periodic Status Data 0-3 messages contain 8 bytes corresponding to the byte ID’s requested.
It looks like the black box code only stores one message per ID. There are several cases where the comments say ‘Skip any previous acks that were ignored’ and they do this by executing a Read for the Ack with a timeout of 0 and ignoring the response.
So yeah, if you built a purely isochronous system and sent the configs out at 5hz-20hz, periodic status at varying speed (maybe 100hz for status 0, 50hz status 1, etc. for more important data) and sent the Ackless motor command at 100hz, and never cared about the Ack message, you would be able to use a single thread and monitor them based on their Periodic Status messages in a single thread. They would configure themselves when they booted up within 200ms for the 5hz stuff and 50ms for the 20hz stuff, with no additional code to detect and reconfigure a reset Jaguar.
The ideal solution would be to store most of the config in NV memory so they don’t have to be reset all the time. PID gains and control mode are the only exception, ideally you could use a single 8-byte packet for the setpoint + gains and code the control mode into the message ID (there are only a few control modes, this would be OK).
But, since the Jaguar protocol isn’t exactly designed for isochronous communication and packet efficiency, sending a bunch of individual config packets is as close you can get without modifying the CAN protocol on the Jaguar side.
OP did say he was using a 2CAN, which does support higher data rates than the serial bridge.
The Jaguar stuff from TI is still available. It is included in the free StellarisWare development kit download. The BDC-COMM source code included in that is useful. The RDK-BDC24 development PDF (google “7870.SW-RDK-BDC24-UG-7243.pdf”) is also useful.
I think you’d run into bandwidth issues with the serial bridge with that scheme.
Definitely. It’s even a bit much for the 2CAN if we want to preserve some capacity for CAN errors and web dashboard diagnostics. Jaguar firmware wasn’t designed with this control scheme in mind.
What looks feasible is to catch power cycles and only reconfigure when necessary. Delaying all the CAN output commands to the end of the periodic code allows us to easily catch changes to Jaguar state. It might not even need a separate VxWorks task.