How to detect missing CAN devices from Java?

We are heavily using CAN this year, but our test chassis does not have any CAN Talons or a CAN PDB on it (yet).

If we deploy our CAN-Talon-using code to a RoboRIO that does not have CAN Talons present, the robot gets “stuttery”; the PWM-based drive system keeps cutting out. We commented out all the code to get data out of the PDB and Talons (we’re doing a lot of data logging while we get things tuned), and things smoothed out. It appears the CAN Recieve timeouts are causing problems.

Is there a good way to determine in software if a CAN Talon or PDB is absent so that we can avoid the method calls that cause issues with missing hardware?

I know we can put in a compiled-in boolean, jumper detect, or dashboard toggle to avoid the code, but would really like to just have the software self-configure…

Hey Doug,
Most of the getters do not block regardless of CAN device presence, so this should not be happening. If you want, send me the code and I can take a look.
[email protected]

Getting

Robot Drive… Output not updated often enough.

when it stutters.

Figuring out way to send code; zip file of project is too large to be a CD attachment.

I suspect the problem may be that all the console messages are swamping the network pipe, so the RIO is missing some DS packets, periodic() doesn’t get called for a while, the drive object doesn’t get poked for a while, and the safety watchdog fires off. Just conjecture, but will turn off the safeties when I get the chassis back (students are driving…).

We can turn off the logging, but checking to see if we can turn it on and off based on whether the devices are present.

I think a getVersion for the CAN Talon will work to detect it’s presence or absence (if I see 0.0, I know it’s not there). No documented way to check for presence or version on the PDB.

took the safeties off; they were the cause of the stutter. Will try to figure out how keep them from getting activated…

If the robot drive is stuttering your robot, what cycle rate are you running your code at? If its below 60hz robot drive is probably just getting angry for you updating too slowly. Go up to 60hz (0.016sec delay minimum) and see if that helps with safety enabled. If not, dump the system time at the beginning and end of teleopPeriodic to see how long it takes to process all of your motor commands. Motor safeties aren’t a bad thing.

how do I change my rate? I thought periodic fired off whenever we got a packet from the DS?

…and getting back to the original question: the problem goes away if I can quiet down the console…

If you’re running IterativeRobot than the time between teleopPeriodic() calls should be fine. I don’t have any access to your code so it is a slight possibility that you have some stalling code… Are you doing any camera processing iteratively by any chance?

yes, I have stalling code; I’m trying to avoid it. Getting data from missing CAN devices seems to chew up time, which is why I was looking for a way to detect missing CAN devices so I can avoid trying to access them if they are missing.

Attached is a demo project that shows the issue. The code to fetch data from the CANTalon can take upwards of 100ms if the CANTalon is missing, which will definitely cause the watchdog to get grumpy.

Omar, while the code to fetch is non-blocking, is it possible that the error handling and reporting code does block (or is slow)?

Robot.java (1.76 KB)


Robot.java (1.76 KB)

I can reproduce the issue by simply calling lots of DriverStation.ReportError (no CAN devices whatsoever).

The cause of the delays appear to be the large amount of strings passed into standard error stream (stderr).

A similar issue can be reproduced in C++ due to the large amount of strings pass into standard output stream (cout).

After killing these two in WPILIB, I have reran your example and had good-rapid-control of the robot, even though the DS racked up lots of error messages.

I wrote up a tracker for this issue, but my recommendation is to not design your robot to produce tons of error information in its typical use. So if disconnecting a camera, i2c device, spi device, CAN device, etc. has the result of creating frequent DS messages (and therefore stderr/stdout streams) turn off the object in your code.

Doug I put together a quick class that will find any CTRE CAN devices on the bus so you can better switch between your test bed and robot. Create an object of this class type and call Find() at the beginning of your code. Use it’s output to determine if you should even bother polling out data from you CAN devices.

You could try using the getters you have now, but typically if a device “disappears” the getters will return the last good value and report an error to the DS, so they may not be best to serve your purposes.


package org.usfirst.frc.team3620.robot;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;

import edu.wpi.first.wpilibj.can.CANJNI;

public class DeviceFinder {

	private ByteBuffer targetID = ByteBuffer.allocateDirect(4);
	private ByteBuffer timeStamp = ByteBuffer.allocateDirect(4);

	/** helper routine to get last received message for a given ID */
	private long checkMessage(int fullId, int deviceID) {
		try {
			targetID.clear();
			targetID.order(ByteOrder.LITTLE_ENDIAN);
			targetID.asIntBuffer().put(0,fullId|deviceID);

			timeStamp.clear();
			timeStamp.order(ByteOrder.LITTLE_ENDIAN);
			timeStamp.asIntBuffer().put(0,0x00000000);
			
			CANJNI.FRCNetworkCommunicationCANSessionMuxReceiveMessage(targetID.asIntBuffer(), 0x1fffffff, timeStamp);
		
			long retval = timeStamp.getInt();
			retval &= 0xFFFFFFFF; /* undo sign-extension */ 
			return retval;
		} catch (Exception e) {
			return -1;
		}
	}
	/** polls for received framing to determine if a device is present.
	 *   This is meant to be used once initially (and not periodically) since 
	 *   this steals cached messages from the robot API.
	 * @return ArrayList of strings holding the names of devices we've found.
	 */
	public ArrayList<String> Find() {
		ArrayList<String> retval = new ArrayList<String>();

		/* get timestamp0 for each device */
		long pdp0_timeStamp0; // only look for PDP at '0'
		long ]pcm_timeStamp0 = new long[63];
		long ]srx_timeStamp0 = new long[63];
		
		pdp0_timeStamp0 = checkMessage(0x08041400,0);
		for(int i=0;i<63;++i) {
			pcm_timeStamp0* = checkMessage(0x09041400, i);
			srx_timeStamp0* = checkMessage(0x02041400, i);
		}

		/* wait 200ms */
		try {
			Thread.sleep(200);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		/* get timestamp1 for each device */
		long pdp0_timeStamp1; // only look for PDP at '0'
		long ]pcm_timeStamp1 = new long[63];
		long ]srx_timeStamp1 = new long[63];
		
		pdp0_timeStamp1 = checkMessage(0x08041400,0);
		for(int i=0;i<63;++i) {
			pcm_timeStamp1* = checkMessage(0x09041400, i);
			srx_timeStamp1* = checkMessage(0x02041400, i);
		}

		/* compare, if timestamp0 is good and timestamp1 is good, and they are different, device is healthy */
		if( pdp0_timeStamp0>=0 && pdp0_timeStamp1>=0 && pdp0_timeStamp0!=pdp0_timeStamp1)
			retval.add("PDP 0");

		for(int i=0;i<63;++i) {
			if( pcm_timeStamp0*>=0 && pcm_timeStamp1*>=0 && pcm_timeStamp0*!=pcm_timeStamp1*)
				retval.add("PCM " + i);
			if( srx_timeStamp0*>=0 && srx_timeStamp1*>=0 && srx_timeStamp0*!=srx_timeStamp1*)
				retval.add("SRX " + i);
		}
		return retval;
	}
}


1 Like

thanks for digging into this and doing the analysis (and providing the device-detection code). We’ll use this to avoid polling non-existent CAN hardware; we still need to dig into non-existent cameras…

This is our first experience with moving our closed-loop control out to a controller. CTRE’s documentation and online support are making this a positive learning experience for our students…

Same here on programmatically getting a list of the cameras recognized by the RoboRio.