CANJaguar "Server" - Open Source.

About a week ago, I was toying around with the idea of running our autonomous code in another task. I quickly had the thread itself up and running, and looked up synchronization in VxWorks, so as to properly control our own motion code from the task, resolving to a Mutex Semaphore. When it compiled and ran without instantly dying, i was elated.

I set about adding in some simple drive code, intending for the robot to move forward for a second, rotate, then move forward again. Simple enough, right? Well, not really as it turns out! When you’re using CANJaguars, there’s always something to trip you up, or so it seems. On her maiden voyage, Serenity (My Team’s Ultimate Ascent robot) lurched to life, and things seemed to work. So i disabled Serenity, dragged her back to the starting position, and enabled into Autonomous again. To my dismay, nothing happened. The Autonomous Task did indeed start, and the printfs were accurately timed, but no movement. (Despite synchronization being wrapped around every possible CAN message.)

For about three days, i tried to figure it out, each attempt yielding the same sketchy it-works-about-one-tenth-of-the-time result. Then, it struck me! What if the CANJaguar class is Task-initialization sensitive? (As in, you have to control it from the same task-context you’re going to use it in)

I moved the initialization code of our CANJaguar objects to the Autonomous Task main routine, just before drive code, and VOILA! It worked. SO, from what I can tell, You can only (consistently) use CANJaguars from the task you initialized the object in. As a result, i present to you, the CANJaguarServer class. It’s a handy little bit of code that runs a command loop for controlling CANJaguars, as well as offering nice things like CANBus bandwidth limiting and brown-out detection.

It’s currently in my team’s 2014 Test Code Repository. The relevant code is in the CANJagServer directory, and a usage example with our MecanumDrive class is shown in RobotMainTask.cpp. (There’s also plenty of other stuff you can ignore/peruse to your liking in there.)

The easiest way to use the CANJaguarServer is through the AsynchCANJaguar class, which inherits from SpeedController for ease of use.

EDIT: I’m finding plenty of bugs as i go, so if you’re using it please try updating to the current version before sending me a bug! Thanks. I’ll try to only push functioning commits once i have a robot to test them on.

I’m interested in fixing this behavior, and I’m a bit surprised that it exists. CTRE reported this several months ago as well (if I recall). I have not yet dug into why this might be happening. If you have any theories or any evidence that may make it easier to ascertain, I’d appreciate hearing about it.


My best theory, after doing a little digging of my own, is that there is some sort of message system silently failing. (whether that be ipc methods such as pipes or something like sockets, i don’t know) My reasoning for this is that while the calls to Set () were being made, and nothing seemed to be locking up, it must have just gone through the functional equivalent of a timeout. Your guess is probably better than mine as to what’s actually going on, but i think it’s got something to do with the FRC_NetworkCommunication and the associated CAN stuff, since, on examining the source for both the CANJaguar and the 2CAN Driver (Which is the CAN interface our team is using,) I can’t seem to see anything that should create this behavior. Both seem perfectly re-entrant.

EDIT: To be specific, i think it’s probably something in the functions who’s prototypes are defined in JaguarCANDriver.h

Interesting. I’ve ran into odd problems with CANJaguar in the past, but never actually root caused them. I could see this being the issue though.

A possible theory (created with zero investigation) could be that the other tasks are pre-empting the CANJaguar thread from running? Or perhaps too much time is spent in your main loop / other tasks combined?

Our code should not have been preempting anything else due to usage, as the only relevant running tasks were the FRC_UserProgram Task and the SHS_Autonomous Task, (Our separately-tasked autonomous,) neither of which should have been doing much. The Autonomous spends most of it’s time on

Wait ()

calls, and the AutonomousPeriodic function was empty.

Taking a look at the log file now, we were using about 7-10% of the CPU during autonomous runs. (Screenshot attached)

My theory stands that it’s got something to do with the workings of

void FRC_NetworkCommunication_JaguarCANDriver_sendMessage ( uint32_t messageID, const uint8_t *data, uint8_t dataSize, int32_t *status );

Although perhaps something is interrupting it improperly somehow. I do know that VxWorks can be tricky with ISRs, although that sounds like a bit of a long-shot.