I think due to the use of a Lepton sensor, those who are interesting in integrating thermal imaging into some kind of product or for some kind of static PC-connected setup are just waiting for FLIR to officially release the Lepton 3 for sale. We already know PureEngineering/GroupGets has their hands on a Lepton 3 module and developed a USB <-> Lepton 3 interface board. https://vine.co/v/edPq5ZD9aO7
Also I can't see much use in connecting the FLIR One Gen 2 to a PC... with the Seek we could use it to increase image quality, fix the gradient, and get raw data out. However with the FLIR One I doubt there's much to be improved on in terms of IQ (due to FLIR's already heavy image processing done in-module) and it also saves radiometric JPEGs with full raw data.
Lepton 2 is the 80x60 one we currently have access to. Lepton 3 is the new 160x120 one. They appear to be just internal FLIR names, so you won't find them on the FLIR website. See this document.
At least groupget's previous breakout boards were all sold pre-soldered and ready to use. See: https://groupgets.com/manufacturers/groupgets-labs/products/flir-lepton-breakout-board-v1-4
Here is the full datasheet for Lepton 3: http://media.wix.com/ugd/53cdb6_5191be73d1c943d78d2e1a095cb7f3b8.pdf
According to that, the Lepton 3 does output 160x120 data over voSPI (page 38), so the upscaling must be done in the app or the hardware of the FLIR One.
To be honest, I don't understand your issue with the upscaling. While it's true that native resolution data is the most optimal, if we consider the issues with sharpness on the Lepton's lens (see the FLIR One G2 actual resolution thread) making the sensor only resolve details similarly to a ~120x90 sensor (even though the sensor really is 160x120) and the fact that the Lepton's temperature accuracy is ±2 degrees C, I doubt the upscaling makes any appreciable difference to the observed accuracy of the temperature readings.
I literally just said that no soldering is required (at least for the previous boards they've shipped).
I don't have a skills to write a driver, and I also don't have access to the FLIR One gen 2 at this time.
I think your best bet is to wait until Groupgets release their USB interface board. I'm sure it will come with sample software that you can use.
50 views and no replies? I think there was an entire thread, several pages long, regarding 3rd party drivers for the Seek Thermal imager. I'd think this would be a hot topic, but nobody has replied yet.
50 views and no replies? I think there was an entire thread, several pages long, regarding 3rd party drivers for the Seek Thermal imager. I'd think this would be a hot topic, but nobody has replied yet.
The only reason why there is no discussion, is the lack of a Flir One G2 camera.
In Germany no trader can deliver this part.
The combination of a thermal camera with an automatic shutter and a real camera is a nice feature for robotics, spy cams and another hacker toys.
You can download the Flir One SDK and study the interesting docs and the code sample.
There is a precompiled flir library for Arm7 CPU. This is the only part which needs reverse engineering
Until now we only know the similar USB announcements from Flir and Seek cameras (iAP Interface)
see my post here
https://www.eevblog.com/forum/testgear/new-flir-products/msg750352/#msg750352
I believe it can be sold in Germany too, by the way.
PC, Mac, Linux, or Chromebook on Chrome Version 41+.
Note: ARC is no longer supported on 32-bit x86 platforms and those platforms will no longer receive updates after October 13, 2015. All ARM platforms will continue to work and receive updates.
There is some RE going on, just not as obvious as in the early E4 and Seek days.
Attached is a annotated dump of the first few seconds of usb activity between a F1G2 and the F1 Android app.
It's not finished.
There may be inaccuracies.
There is much more to uncover.
This is a long way from a stand alone PC program.
Your mileage may vary.
blah, blah, blah.
package com.flir.flironesdk.usb;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbRequest;
import android.util.Log;
import java.io.Closeable;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map.Entry;
public class UsbCommunicator
implements Closeable
{
private boolean IS_SIMULATED;
private Delegate mDelegate;
private volatile UsbDeviceConnection connection;
private static final String LOG_TAG = "UsbCommunicator";
private static final int SET_INTERFACE = 11;
private static final int CTRL_TRANSFER_TIMEOUT = 200;
static final int FRAME_READ_ENDPOINT_ID = 5;
static final int CONFIG_READ_ENDPOINT_ID = 1;
static final int CONFIG_WRITE_ENDPOINT_ID = 2;
static final int FILEIO_READ_ENDPOINT_ID = 3;
static final int FILEIO_WRITE_ENDPOINT_ID = 4;
static final int SUPPORTED_VENDOR_ID = 2507;
static final int SUPPORTED_PRODUCT_ID = 6550;
private UsbEndpoint configWriteEndpoint;
public static abstract interface Delegate
{
public abstract void onDataReceived(byte[] paramArrayOfByte, UsbCommunicator.ProtocolType paramProtocolType);
public abstract void onCommunicationAvailabilityChange(boolean paramBoolean);
}
public static enum ProtocolType
{
CONFIGURATION((byte)1), FILEIO((byte)2), FRAME((byte)4);
private final byte value;
private ProtocolType(byte value)
{
this.value = value;
}
byte getValue()
{
return this.value;
}
}
private UsbEndpoint fileioWriteEndpoint = null;
final UsbRequest frameReadRequest = new UsbRequest();
final UsbRequest fileioReadRequest = new UsbRequest();
final UsbRequest configReadRequest = new UsbRequest();
private UsbDevice mDevice;
volatile boolean connected = false;
volatile boolean expectFrameData = false;
volatile boolean expectFileData = false;
public static boolean deviceIsValid(UsbDevice device)
{
try
{
validateDevice(device);
return true;
}
catch (Exception e)
{
Log.e("UsbCommunicator", "Invalid device: " + e.getMessage());
}
return false;
}
public static void validateDevice(UsbDevice device)
throws Exception
{
if (device.getVendorId() != 2507) {
throw new Exception("Unsupported USB Vendor ID of " + Integer.toHexString(device.getVendorId()));
}
if (device.getProductId() != 6550) {
throw new Exception("Unsupported USB Product ID of " + Integer.toHexString(device.getProductId()));
}
int ifaceCount = device.getInterfaceCount();
HashMap<Integer, Boolean> inEndpointsFound = new HashMap();
HashMap<Integer, Boolean> outEndpointsFound = new HashMap();
inEndpointsFound.put(Integer.valueOf(3), Boolean.valueOf(false));
inEndpointsFound.put(Integer.valueOf(1), Boolean.valueOf(false));
inEndpointsFound.put(Integer.valueOf(5), Boolean.valueOf(false));
outEndpointsFound.put(Integer.valueOf(2), Boolean.valueOf(false));
outEndpointsFound.put(Integer.valueOf(4), Boolean.valueOf(false));
UsbInterface iface;
for (int i = 0; i < ifaceCount; i++)
{
iface = device.getInterface(i);
int endpointCount = iface.getEndpointCount();
for (int e = 0; e < endpointCount; e++)
{
UsbEndpoint ep = iface.getEndpoint(e);
if (ep.getDirection() == 128) {
inEndpointsFound.put(Integer.valueOf(ep.getEndpointNumber()), Boolean.valueOf(true));
} else {
outEndpointsFound.put(Integer.valueOf(ep.getEndpointNumber()), Boolean.valueOf(true));
}
}
}
HashSet<Map.Entry<Integer, Boolean>> allEndpointsFound = new HashSet();
allEndpointsFound.addAll(inEndpointsFound.entrySet());
allEndpointsFound.addAll(outEndpointsFound.entrySet());
for (Map.Entry<Integer, Boolean> endpoint : allEndpointsFound) {
if (!((Boolean)endpoint.getValue()).booleanValue()) {
throw new Exception("Unable to communicate with USB device, endpoint " + ((Integer)endpoint.getKey()).toString() + " not found");
}
}
}
public UsbCommunicator(UsbDevice device, UsbDeviceConnection usbConnection, Delegate delegate)
throws Exception
{
validateDevice(device);
this.mDelegate = delegate;
this.connection = usbConnection;
this.mDevice = device;
int ifaceCount = device.getInterfaceCount();
for (int i = 0; i < ifaceCount; i++)
{
UsbInterface iface = device.getInterface(i);
if (false == this.connection.claimInterface(iface, false))
{
Log.e("UsbCommunicator", "Unable to claim interface " + iface.getId() + ", close connection and try again.");
delegate.onCommunicationAvailabilityChange(false);
throw new Exception("Unable to claim USB interface");
}
int endpointCount = iface.getEndpointCount();
for (int e = 0; e < endpointCount; e++)
{
UsbEndpoint ep = iface.getEndpoint(e);
if (ep.getDirection() == 128) {
switch (ep.getEndpointNumber())
{
case 5:
this.frameReadRequest.initialize(this.connection, ep);
break;
case 1:
this.configReadRequest.initialize(this.connection, ep);
break;
case 3:
this.fileioReadRequest.initialize(this.connection, ep);
break;
case 2:
case 4:
default:
Log.w("UsbCommunicator", "Unknown USB_DIR_IN Endpoint on device!");break;
}
} else {
switch (ep.getEndpointNumber())
{
case 2:
this.configWriteEndpoint = ep;
break;
case 4:
this.fileioWriteEndpoint = ep;
break;
default:
Log.w("UsbCommunicator", "Unknown USB_DIR_OUT Endpoint on device!");
}
}
}
}
if (this.fileioWriteEndpoint != null)
{
stopCommunication(ProtocolType.FRAME);
stopCommunication(ProtocolType.FILEIO);
}
else
{
delegate.onCommunicationAvailabilityChange(false);
}
}
final int CONFIG_BUFFER_SIZE = 4096;
final int FRAME_BUFFER_SIZE = 131072;
final int FILE_BUFFER_SIZE = 1048576;
final ByteBuffer configBuffer = ByteBuffer.allocate(4096);
final ByteBuffer frameBuffer = ByteBuffer.allocate(131072);
final ByteBuffer fileBuffer = ByteBuffer.allocate(1048576);
private void pollEndpoints()
{
new Thread(new Runnable()
{
public void run()
{
Log.d("configReadRequest", "Starting configReadRequest poll loop...");
UsbCommunicator.this.configReadRequest.queue(UsbCommunicator.this.configBuffer, 4096);
while (UsbCommunicator.this.connected)
{
UsbRequest requestResult = UsbCommunicator.this.connection.requestWait();
synchronized (UsbCommunicator.this.configReadRequest)
{
if (!UsbCommunicator.this.connected) {
break;
}
if (requestResult == UsbCommunicator.this.configReadRequest)
{
byte[] data = new byte[UsbCommunicator.this.configBuffer.position()];
UsbCommunicator.this.configBuffer.flip();
UsbCommunicator.this.configBuffer.get(data);
if (data.length > 0) {
UsbCommunicator.this.mDelegate.onDataReceived(data, UsbCommunicator.ProtocolType.CONFIGURATION);
}
UsbCommunicator.this.configBuffer.clear();
UsbCommunicator.this.configReadRequest.queue(UsbCommunicator.this.configBuffer, 4096);
}
else if (requestResult == UsbCommunicator.this.frameReadRequest)
{
byte[] data = new byte[UsbCommunicator.this.frameBuffer.position()];
UsbCommunicator.this.frameBuffer.flip();
UsbCommunicator.this.frameBuffer.get(data);
if (data.length > 0) {
UsbCommunicator.this.mDelegate.onDataReceived(data, UsbCommunicator.ProtocolType.FRAME);
}
if (UsbCommunicator.this.expectFrameData) {
UsbCommunicator.this.frameReadRequest.queue(UsbCommunicator.this.frameBuffer, 131072);
}
UsbCommunicator.this.frameBuffer.clear();
}
else if (requestResult == UsbCommunicator.this.fileioReadRequest)
{
byte[] data = new byte[UsbCommunicator.this.fileBuffer.position()];
UsbCommunicator.this.fileBuffer.flip();
UsbCommunicator.this.fileBuffer.get(data);
UsbCommunicator.this.mDelegate.onDataReceived(data, UsbCommunicator.ProtocolType.FILEIO);
UsbCommunicator.this.fileBuffer.clear();
if (UsbCommunicator.this.expectFileData) {
UsbCommunicator.this.fileioReadRequest.queue(UsbCommunicator.this.fileBuffer, 1048576);
}
}
}
}
}
})
.start();
}
public UsbCommunicator(Delegate usbDelegate)
{
Log.i("UsbCommunicator", "Simulated device constructor");
this.mDelegate = usbDelegate;
this.IS_SIMULATED = true;
}
private void sendData(UsbEndpoint ep, byte[] data, int length)
{
Log.d("UsbCommunicator", "Sending " + length + " bytes on EP" + ep.getEndpointNumber());
synchronized (this.connection)
{
int offset = 0;
while (offset < length)
{
int result = this.connection.bulkTransfer(ep, data, offset, length - offset, 500);
if (result > 0)
{
Log.d("UsbCommunicator", "Bulk transfer sent " + result + " bytes on Endpoint " + ep.getEndpointNumber());
offset += result;
}
else
{
Log.w("UsbCommunicator", "Bulk Transfer Failed on Endpoint " + ep.getEndpointNumber());
}
}
}
}
native void sendConfigDataToSimulatedDevice(byte[] paramArrayOfByte);
public void sendDataToDevice(byte[] data, ProtocolType protocolType)
{
if (this.IS_SIMULATED)
{
switch (protocolType)
{
case CONFIGURATION:
sendConfigDataToSimulatedDevice(data);
break;
}
}
else
{
UsbEndpoint destinationEndpoint;
UsbEndpoint destinationEndpoint;
switch (protocolType)
{
case CONFIGURATION:
destinationEndpoint = this.configWriteEndpoint;
break;
case FILEIO:
destinationEndpoint = this.fileioWriteEndpoint;
break;
default:
Log.w("UsbCommunicator", "Attempted to send data on knosupported protocol!"); return;
}
UsbEndpoint destinationEndpoint;
sendData(destinationEndpoint, data, data.length);
}
}
public void sendConfigData(byte[] data)
{
sendDataToDevice(data, ProtocolType.CONFIGURATION);
}
public void sendFileioData(byte[] data)
{
sendDataToDevice(data, ProtocolType.FILEIO);
}
public void close()
{
if (this.connected)
{
pause();
this.connection.close();
this.mDelegate.onCommunicationAvailabilityChange(false);
Log.d("UsbCommunicator", "Connection closed.");
}
}
public void pause()
{
if (this.connected)
{
synchronized (this.configReadRequest)
{
stopFrames();
stopCommunication(ProtocolType.FILEIO);
this.connected = false;
this.configReadRequest.cancel();
this.configBuffer.clear();
this.fileioReadRequest.cancel();
this.frameReadRequest.cancel();
}
this.mDelegate.onCommunicationAvailabilityChange(false);
}
}
private void startFrames()
{
startCommunication(ProtocolType.FRAME);
}
private void stopFrames()
{
stopCommunication(ProtocolType.FRAME);
}
private void startCommunication(ProtocolType protocolType)
{
toggleCommunication(protocolType, true);
}
private void stopCommunication(ProtocolType protocolType)
{
toggleCommunication(protocolType, false);
}
private void toggleCommunication(ProtocolType protocolType, boolean startCommunication)
{
if (this.IS_SIMULATED == true) {
return;
}
switch (protocolType)
{
case FRAME:
int interfaceNumber = 2;
this.expectFrameData = startCommunication;
this.frameBuffer.clear();
if (this.expectFrameData) {
this.frameReadRequest.queue(this.frameBuffer, 131072);
}
break;
case FILEIO:
int interfaceNumber = 1;
this.expectFileData = startCommunication;
this.fileBuffer.clear();
if (this.expectFileData) {
this.fileioReadRequest.queue(this.fileBuffer, 1048576);
}
break;
case CONFIGURATION:
Log.w("UsbCommunicator", "Configuration Protocol cannot be started or stopped.");
default:
return;
}
int interfaceNumber;
int altSetting = startCommunication ? 1 : 0;
synchronized (this.connection)
{
int result = this.connection.controlTransfer(1, 11, altSetting, interfaceNumber, null, 0, 200);
Log.d("UsbCommunicator", "Result from control transfer: " + result);
}
}
public void connect()
{
if (!this.connected)
{
this.connected = true;
this.mDelegate.onCommunicationAvailabilityChange(true);
startCommunication(ProtocolType.FILEIO);
pollEndpoints();
}
}
}
So I believe that there is a performance issue with the Example App and if your phone/tablet doesn't have the horsepower it just crashes.
SDK 1.0.2
CHANGE LOG
Improves reliability of saving and loading frames.