Tuesday, December 21, 2010

Setting Inventory Search Mode with the Java LTK

Inventory Search Mode is an Impinj custom extension to LLRP, so setting it with the Java LTK can be a little tricky. You could compile your own version of the Java LTK JAR file with Impinj custom extensions built in. That process is covered here. However, some developers want to use the stock Java LTK JAR file. Setting custom parameters like Inventory Search Mode is still possible using the CUSTOM and CUSTOM_MESSAGE classes built into the base library. You won't get niceties like named enumerations and constants, but it works just the same.

Before sending any custom messages to the reader, you must send the IMPINJ_ENABLE_EXTENSIONS message. Here is an example of how to do that.
public void enableImpinjExt()
{
BytesToEnd_HEX data = new BytesToEnd_HEX();
CUSTOM_MESSAGE msg = new CUSTOM_MESSAGE();
msg.setVendorIdentifier(new UnsignedInteger(25882));
msg.setMessageSubtype(new UnsignedByte(21));
data.add(new SignedByte(0));
data.add(new SignedByte(0));
data.add(new SignedByte(0));
data.add(new SignedByte(0));
msg.setData(data);
reader.send(msg);
}

The magic number 25882 is the vendor ID for Impinj. The message subtype for IMPINJ_ENABLE_EXTENSIONS is 21.

Once you enable the custom extensions, you can create and send the ROSpec. This sample code shows how add a custom parameter (Inventory Search Mode) to an ROSpec.
// Create a new inventory parameter
InventoryParameterSpec inventoryParam =
new InventoryParameterSpec();
// We're reading Gen2 tags.
inventoryParam.setProtocolID
(new AirProtocols(AirProtocols.EPCGlobalClass1Gen2));
inventoryParam.setInventoryParameterSpecID
(new UnsignedShort(1));

// Create a custom parameter (Inventory Search Mode)
Custom searchMode = new Custom();
BytesToEnd_HEX data = new BytesToEnd_HEX();
// Set the data to 0x0001 (Single target)
// Dual target = 0x0002
data.add(new SignedByte(0));
data.add(new SignedByte(1));
// 25882 is the vendor ID for Impinj
searchMode.setVendorIdentifier(new UnsignedInteger(25882));
// Parameter 23 = ImpinjInventorySearchType
searchMode.setParameterSubtype(new UnsignedInteger(23));
searchMode.setData(data);

// Set the session to Session 2
// Session is a standard LLRP setting
C1G2InventoryCommand invCmd = new C1G2InventoryCommand();
invCmd.setTagInventoryStateAware(new Bit(0));
C1G2SingulationControl c1g2SingCtrl = new C1G2SingulationControl();
TwoBitField session = new TwoBitField();
session.clear(0);
session.set(1);
c1g2SingCtrl.setSession(session);
// Tag population
c1g2SingCtrl.setTagPopulation(new UnsignedShort(32));
// Tag transit time
c1g2SingCtrl.setTagTransitTime(new UnsignedInteger(0));
invCmd.setC1G2SingulationControl(c1g2SingCtrl);
// Add our custom setting to the C1G2InventoryCommand
invCmd.addToCustomList(searchMode);

// Apply these settings to all four antennas
for (int i=1; i<=4; i++)
{
AntennaConfiguration antennaConfig = new AntennaConfiguration();
antennaConfig.setAntennaID(new UnsignedShort(i));
antennaConfig.addToAirProtocolInventoryCommandSettingsList(invCmd);
inventoryParam.addToAntennaConfigurationList(antennaConfig);
}

// Add the inventory parameters to the AISpec
aispec.addToInventoryParameterSpecList(inventoryParam);
// Add the AISpec to the ROSpec
roSpec.addToSpecParameterList(aispec);


Optionally, you can load the ROSpec from file using Util.loadXMLLLRPMessage rather than creating it programatically. This is the equivalent ROSpec XML for the code above.
<?xml version="1.0" encoding="UTF-8"?>
<llrp:ADD_ROSPEC xmlns:llrp="http://www.llrp.org/ltk/schema/core/encoding/xml/1.0"
Version="1" MessageID="0">
<llrp:ROSpec>
<llrp:ROSpecID>123</llrp:ROSpecID>
<llrp:Priority>0</llrp:Priority>
<llrp:CurrentState>Disabled</llrp:CurrentState>
<llrp:ROBoundarySpec>
<llrp:ROSpecStartTrigger>
<llrp:ROSpecStartTriggerType>Null</llrp:ROSpecStartTriggerType>
</llrp:ROSpecStartTrigger>
<llrp:ROSpecStopTrigger>
<llrp:ROSpecStopTriggerType>Null</llrp:ROSpecStopTriggerType>
<llrp:DurationTriggerValue>0</llrp:DurationTriggerValue>
</llrp:ROSpecStopTrigger>
</llrp:ROBoundarySpec>
<llrp:AISpec>
<llrp:AntennaIDs>0</llrp:AntennaIDs>
<llrp:AISpecStopTrigger>
<llrp:AISpecStopTriggerType>Null</llrp:AISpecStopTriggerType>
<llrp:DurationTrigger>0</llrp:DurationTrigger>
</llrp:AISpecStopTrigger>
<llrp:InventoryParameterSpec>
<llrp:InventoryParameterSpecID>1</llrp:InventoryParameterSpecID>
<llrp:ProtocolID>EPCGlobalClass1Gen2</llrp:ProtocolID>
<llrp:AntennaConfiguration>
<llrp:AntennaID>1</llrp:AntennaID>
<llrp:C1G2InventoryCommand>
<llrp:TagInventoryStateAware>0</llrp:TagInventoryStateAware>
<llrp:C1G2SingulationControl>
<llrp:Session>2</llrp:Session>
<llrp:TagPopulation>32</llrp:TagPopulation>
<llrp:TagTransitTime>0</llrp:TagTransitTime>
</llrp:C1G2SingulationControl>
<llrp:Custom>
<llrp:VendorIdentifier>25882</llrp:VendorIdentifier>
<llrp:ParameterSubtype>23</llrp:ParameterSubtype>
<llrp:Data>0001</llrp:Data>
</llrp:Custom>
</llrp:C1G2InventoryCommand>
</llrp:AntennaConfiguration>
<llrp:AntennaConfiguration>
<llrp:AntennaID>2</llrp:AntennaID>
<llrp:C1G2InventoryCommand>
<llrp:TagInventoryStateAware>0</llrp:TagInventoryStateAware>
<llrp:C1G2SingulationControl>
<llrp:Session>2</llrp:Session>
<llrp:TagPopulation>32</llrp:TagPopulation>
<llrp:TagTransitTime>0</llrp:TagTransitTime>
</llrp:C1G2SingulationControl>
<llrp:Custom>
<llrp:VendorIdentifier>25882</llrp:VendorIdentifier>
<llrp:ParameterSubtype>23</llrp:ParameterSubtype>
<llrp:Data>0001</llrp:Data>
</llrp:Custom>
</llrp:C1G2InventoryCommand>
</llrp:AntennaConfiguration>
<llrp:AntennaConfiguration>
<llrp:AntennaID>3</llrp:AntennaID>
<llrp:C1G2InventoryCommand>
<llrp:TagInventoryStateAware>0</llrp:TagInventoryStateAware>
<llrp:C1G2SingulationControl>
<llrp:Session>2</llrp:Session>
<llrp:TagPopulation>32</llrp:TagPopulation>
<llrp:TagTransitTime>0</llrp:TagTransitTime>
</llrp:C1G2SingulationControl>
<llrp:Custom>
<llrp:VendorIdentifier>25882</llrp:VendorIdentifier>
<llrp:ParameterSubtype>23</llrp:ParameterSubtype>
<llrp:Data>0001</llrp:Data>
</llrp:Custom>
</llrp:C1G2InventoryCommand>
</llrp:AntennaConfiguration>
<llrp:AntennaConfiguration>
<llrp:AntennaID>4</llrp:AntennaID>
<llrp:C1G2InventoryCommand>
<llrp:TagInventoryStateAware>0</llrp:TagInventoryStateAware>
<llrp:C1G2SingulationControl>
<llrp:Session>2</llrp:Session>
<llrp:TagPopulation>32</llrp:TagPopulation>
<llrp:TagTransitTime>0</llrp:TagTransitTime>
</llrp:C1G2SingulationControl>
<llrp:Custom>
<llrp:VendorIdentifier>25882</llrp:VendorIdentifier>
<llrp:ParameterSubtype>23</llrp:ParameterSubtype>
<llrp:Data>0001</llrp:Data>
</llrp:Custom>
</llrp:C1G2InventoryCommand>
</llrp:AntennaConfiguration>
</llrp:InventoryParameterSpec>
</llrp:AISpec>
<llrp:ROReportSpec>
<llrp:ROReportTrigger>Upon_N_Tags_Or_End_Of_ROSpec</llrp:ROReportTrigger>
<llrp:N>1</llrp:N>
<llrp:TagReportContentSelector>
<llrp:EnableROSpecID>0</llrp:EnableROSpecID>
<llrp:EnableSpecIndex>0</llrp:EnableSpecIndex>
<llrp:EnableInventoryParameterSpecID>0</llrp:EnableInventoryParameterSpecID>
<llrp:EnableAntennaID>0</llrp:EnableAntennaID>
<llrp:EnableChannelIndex>0</llrp:EnableChannelIndex>
<llrp:EnablePeakRSSI>0</llrp:EnablePeakRSSI>
<llrp:EnableFirstSeenTimestamp>0</llrp:EnableFirstSeenTimestamp>
<llrp:EnableLastSeenTimestamp>1</llrp:EnableLastSeenTimestamp>
<llrp:EnableTagSeenCount>0</llrp:EnableTagSeenCount>
<llrp:EnableAccessSpecID>0</llrp:EnableAccessSpecID>
</llrp:TagReportContentSelector>
</llrp:ROReportSpec>
</llrp:ROSpec>
</llrp:ADD_ROSPEC>

Once you're up and running, I recommend pulling the ROSpec to verify that your application has configured the reader correctly.