State switching time lag for mmc.setProperty

I am using an Arduino as a shutter controller in MicroManager to coordinate image acquisition on a Raptor Ninox, trigger an external camera controlled by different software (ThorCam), and send TTL pulses during certain frames to an external device. It works surprisingly well with the following exception: I found that switching the Arduino to a different state and then back (necessary for triggering the other external camera) using mmc.setProperty(“Arduino-Switch”,“State”,#) takes about 42ms. Because I need to switch twice per image to generate a TTL pulse, my minimum time interval is 84ms. I would like to record at faster acquisition speeds. Is there a way to reduce the switching time to speed up acquisition rates?

// clear all previous acquisitions
if (mmc.isSequenceRunning(mmc.getCameraDevice())){
	mmc.stopSequenceAcquisition();
}
gui.closeAllAcquisitions();
gui.clearMessageWindow();

// Parameters
stimFrame = 20; // Enter stim frame here
acqSettings = gui.getAcquisitionSettings();
nrFrames = acqSettings.numFrames;
frameInterval = acqSettings.intervalMs;
acqName = acqSettings.prefix;
rootDirName = acqSettings.root;
camera = mmc.getCameraDevice();
nrChannels = nrSlices = nrPositions = 1;
	
gui.openAcquisition(acqName, rootDirName,
   nrFrames, nrChannels, nrSlices, nrPositions,
   /* show */ true,
   /* save */ true);

width = (int) mmc.getImageWidth();
height = (int) mmc.getImageHeight();
bytesPerPixel = (int) mmc.getBytesPerPixel();
// bitDepth = (int) mmc.getImageBitDepth();
// bitDepth = int 14;

gui.initializeAcquisition(acqName, width, height, bytesPerPixel, 14);

mmc.startSequenceAcquisition(nrFrames, frameInterval, true);
frame = 0;
exposureMs = mmc.getExposure();
mmc.setProperty("Arduino-Switch","State",8); //Set arduino state to turn on despeckler (binary 8 = pin 11)
mmc.setProperty("Arduino-Shutter","OnOff", 1); // Open arduino "shutter"
now = System.currentTimeMillis();

while (mmc.getRemainingImageCount() > 0 || mmc.isSequenceRunning(mmc.getCameraDevice())) {
   if (mmc.getRemainingImageCount() > 0) {
		if (frame==stimFrame){
			mmc.setProperty("Arduino-Switch","State",40); // Set arduino state to high on pin 11 (despeckler) and pin 13 (to Master 8). binary 8+32 = pins 11 and 13
			gui.sleep(5);
			mmc.setProperty("Arduino-Switch","State",8); //Set arduino state to turn on despeckler (binary 8 = pin 11)
		}
      now = System.currentTimeMillis();
		// Collect Ninox Image
      img = mmc.popNextTaggedImage();
      // Trigger ThorCam
      mmc.setProperty("Arduino-Switch","State",24); // Set arduino state to high on pin 11 (despeckler) and pin 12 (to Thorcam). binary 8+16 = pins 11 and 12
      // Add Ninox image to display
      gui.addImageToAcquisition(acqName, frame, 0, 0, 0, img);
      // Set ThorCam trigger to LOW while leaving despeckler on
      mmc.setProperty("Arduino-Switch","State",8); //Set arduino state to turn on despeckler (binary 8 = pin 11)
      // The above mmc.setProperty ("Arduino... >> appears to be a frame rate limiting step
      frame++;
      // Determine interval timing
      itTook = System.currentTimeMillis() - now;
      int timeDiff = frameInterval - itTook;
      if (timeDiff>0) {
	      gui.sleep(timeDiff);
	      itTook = System.currentTimeMillis() - now;
      }
      gui.message(""+timeDiff);
      gui.message("Frame: "+ frame + " | Interval: " + itTook+ " ms");
      
   }
   else {
      mmc.sleep(Math.min(0.5 * exposureMs, 20));
   }
}

mmc.stopSequenceAcquisition();
mmc.setProperty("Arduino-Switch","State",0); //Set all arduino pins to 0
mmc.setProperty("Arduino-Shutter","OnOff", 0); // Close arduino "shutter"
gui.message("Acquisition Complete");

Are you sure the delay is caused by the Arduino? I’ve seen issues with Thorcam sometimes where there is a minimum acquisition time that’s a property of the camera. I wrote an updated Thorlabs device adapter (which I think is now part of the nightly builds - let me know if you need me to double-check) that solves this issue when the camera is controlled through micro-manager.
Best,
Dan

1 Like

Thanks for the response! The problem seems to be independent of ThorCam. I tried some dummy switching of the Arduino (just cycle True False for the On/Off state), and find that each cycle of the Arduino state, even if it’s not controlling anything, adds 42ms to each frame acquisition time. If I remove all Arduino state switching, the resulting acquisition interval drops to 50ms, which is what I set in the Multi-D window (instead of 50+42=94ms if I have a Arduino switch in the loop).
One workaround I will try soon is to have the Ninox camera output signal (which is a TTL pulse that coincides with the start and finish of an acquisition) directly trigger the Thorlabs camera over hardware. However, ideally getting the Arduino to work on reasonable timescales would be better for syncing the other hardware.

Going through the (corrected: USB interface) will always introduce some additional overhead and latency. If you can run the thorlabs camera off of triggers from the other, that is likely to be your best bet. For other hardware, if you need more deterministic timing control you should look into the sequencing capabilities of the Arduino device adapter:
https://micro-manager.org/wiki/Arduino#Usage_Notes
A digital pulse provided to pin 2 can then trigger a pre-programmed pattern of digital outputs with significantly lower latency.
This example script looks fairly straightforward:
https://valelab4.ucsf.edu/svn/micromanager2/trunk/scripts/sequencePropTest.bsh
This should achieve latency on the order of 50 microseconds based on the device adapter page. This seems to suggest that the DAC is sequenceable as well, but it’s not clear how from the example script.

2 Likes

Thanks! This seems tractable. I’ll try implementing it this week and test the hardware.

Just to add in case any miss-perceptions may arise: The latency observed by @JTravisDO not so much arises from latencies in the Core (the Core is more of a switch-center and execution times likely are in the ns range), as by latencies caused by USB traffic. Although data throughput through USB can be high, sending (and receiving) individual messages takes time. Each command involves sending a message through the USB-serial to the Arduino, parsing the command, executing the desired action, and sending an acknowledgement back to the computer. 10 ms for each message involving the USB bus does not sounds out of the ordinary to me, so ~40ms sounds reasonable and to be expected. As @pavak_shah explained, a reasonable way to speed this is up would be to by-pass communication with the PC, and have an autonomous device provide the trigger signals.

1 Like

Sorry for the mic-perception Nico, I added a correction to my post =)