Welcome To Our Support Portal

Browse Impinj resources for help with FAQ, downloads, quick links, and more.


Submit a request

Hello LLRP (Low-Level Reader Protocol)

Follow


This post will cover the "Hello World" LLRP application in C# .NET Framework 4.6.1. It will show how to setup an RFID reader with minimal configuration and start reading tags. To keep things concise, much of the error handling required in a production application has been omitted.

First, you'll need to download the LLRP Toolkit (LTK) for .NET. It's available on the Impinj support site for Impinj partners. Create a new sub-directory called 'lib' in your project and extract these four LTK files to it:

LLRP.dll
LLRP.Impinj.dll
LLRP.Impinj.pdb
LLRP.pdb

Open up your project in Visual Studio and add references to these libraries by selecting Project->Add Reference from the menu.




Add the library import statements at the top of your class.

An ROSpec tells the reader what data you want to read and when you want to read it. The Speedway and Speedway Revolution readers support one ROSpec on the reader at a time. Before you can add a new ROSpec, you must delete any existing ones. This can be done by sending a DELETE_ROSPEC message.

using Org.LLRP.LTK.LLRPV1;
using Org.LLRP.LTK.LLRPV1.DataType;
using Org.LLRP.LTK.LLRPV1.Impinj;

static void Delete_RoSpec()
{
   MSG_DELETE_ROSPEC msg = new MSG_DELETE_ROSPEC();
   msg.ROSpecID = 0;
   MSG_ERROR_MESSAGE msg_err;
 
   MSG_DELETE_ROSPEC_RESPONSE rsp =
   reader.DELETE_ROSPEC(msg, out msg_err, 2000);
 
   if (rsp != null)
   {
      // Success
      Console.WriteLine(rsp.ToString());
   }
   else if (msg_err != null)
   {
      // Error
      Console.WriteLine(msg_err.ToString());
   }
   else
   {
      // Timeout
      Console.WriteLine("Timeout Error.");           
   }
}

Now you can add a new ROSpec using the ADD_ROSPEC message.

static void Add_RoSpec()
{
   MSG_ERROR_MESSAGE msg_err;
   MSG_ADD_ROSPEC msg = new MSG_ADD_ROSPEC();
 
   // Reader Operation Spec (ROSpec)
   msg.ROSpec = new PARAM_ROSpec();
   // ROSpec must be disabled by default
   msg.ROSpec.CurrentState = ENUM_ROSpecState.Disabled;
   // The ROSpec ID can be set to any number
   // You must use the same ID when enabling this ROSpec
   msg.ROSpec.ROSpecID = 123;
 
   // ROBoundarySpec
   // Specifies the start and stop triggers for the ROSpec
   msg.ROSpec.ROBoundarySpec = new PARAM_ROBoundarySpec();
   // Immediate start trigger
   // The reader will start reading tags as soon as the ROSpec
// is enabled msg.ROSpec.ROBoundarySpec.ROSpecStartTrigger = new PARAM_ROSpecStartTrigger(); msg.ROSpec.ROBoundarySpec.ROSpecStartTrigger
.ROSpecStartTriggerType = ENUM_ROSpecStartTriggerType.Immediate;
// No stop trigger. Keep reading tags until the ROSpec is disabled. msg.ROSpec.ROBoundarySpec.ROSpecStopTrigger =
new PARAM_ROSpecStopTrigger(); msg.ROSpec.ROBoundarySpec.ROSpecStopTrigger.ROSpecStopTriggerType = ENUM_ROSpecStopTriggerType.Null; // Antenna Inventory Spec (AISpec) // Specifies which antennas and protocol to use msg.ROSpec.SpecParameter = new UNION_SpecParameter(); PARAM_AISpec aiSpec = new PARAM_AISpec(); aiSpec.AntennaIDs = new UInt16Array(); // Enable all antennas aiSpec.AntennaIDs.Add(0); // No AISpec stop trigger. It stops when the ROSpec stops. aiSpec.AISpecStopTrigger = new PARAM_AISpecStopTrigger(); aiSpec.AISpecStopTrigger.AISpecStopTriggerType = ENUM_AISpecStopTriggerType.Null; aiSpec.InventoryParameterSpec =
new PARAM_InventoryParameterSpec[1]; aiSpec.InventoryParameterSpec[0] =
new PARAM_InventoryParameterSpec(); aiSpec.InventoryParameterSpec[0].InventoryParameterSpecID = 1234; aiSpec.InventoryParameterSpec[0].ProtocolID = ENUM_AirProtocols.EPCGlobalClass1Gen2; msg.ROSpec.SpecParameter.Add(aiSpec); // Report Spec msg.ROSpec.ROReportSpec = new PARAM_ROReportSpec(); // Send a report for every tag read msg.ROSpec.ROReportSpec.ROReportTrigger = ENUM_ROReportTriggerType.Upon_N_Tags_Or_End_Of_ROSpec; msg.ROSpec.ROReportSpec.N = 1; msg.ROSpec.ROReportSpec.TagReportContentSelector = new PARAM_TagReportContentSelector(); MSG_ADD_ROSPEC_RESPONSE rsp =
reader.ADD_ROSPEC(msg, out msg_err, 2000); if (rsp != null) { // Success Console.WriteLine (rsp.ToString()); } else if (msg_err != null) { // Error Console.WriteLine (msg_err.ToString()); } else { // Timeout Console.WriteLine("Timeout Error."); } }

When an ROSpec is added, it is disabled by default. Before it can be used, it must be enabled with the ENABLE_ROSPEC message.

static void Enable_RoSpec()
{
   MSG_ERROR_MESSAGE msg_err;
   MSG_ENABLE_ROSPEC msg = new MSG_ENABLE_ROSPEC();
   msg.ROSpecID = 123;
   MSG_ENABLE_ROSPEC_RESPONSE rsp =
   reader.ENABLE_ROSPEC(msg, out msg_err, 2000);
   if (rsp != null)
   {
      // Success
      Console.WriteLine (rsp.ToString());
   }
   else if (msg_err != null)
   {
      // Error
      Console.WriteLine (msg_err.ToString());
   }
   else
   {
      // Timeout
      Console.WriteLine("Timeout Error.");
   }
}

Since we set our ROSpec start trigger type to "immediate", the reader will begin reading tags as soon as the ENABLE_ROSPEC message is sent. In order to receive tag reports you must specify an event handler to call when data is ready. Here's an example of what that function should look like.

static void OnReportEvent(MSG_RO_ACCESS_REPORT msg)
{
   // Loop through all the tags in the report
   for (int i = 0; i < msg.TagReportData.Length; i++)
   {
      if (msg.TagReportData[i].EPCParameter.Count > 0)
      {
         string epc;
         // Two possible types of EPC: 96-bit and 128-bit
         if (msg.TagReportData[i].EPCParameter[0].GetType() ==
            typeof(PARAM_EPC_96))
         {
            epc = ((PARAM_EPC_96)
               (msg.TagReportData[i].EPCParameter[0]))
.EPC.ToHexString(); } else { epc = ((PARAM_EPCData) (msg.TagReportData[i].EPCParameter[0]))
.EPC.ToHexString(); } Console.WriteLine("epc = " + epc); } } }

Now that we have all of the basic functions written, let's write the main function that will connect to the reader and send all of the messages.

static LLRPClient reader;
 
static void Main(string[] args)
{
   // Create a LLRPClient instance.
   reader = new LLRPClient();
 
   /*
      Connect to the reader.
      Replace "SpeedwayR-10-25-32" with your reader's hostname.
      The second argument (2000) is a timeout value in milliseconds.
      If a connection cannot be established within this timeframe,
      the call will fail.
   */
   ENUM_ConnectionAttemptStatusType status;
   reader.Open("SpeedwayR-10-25-32", 2000, out status);
 
   // Check for a connection error
   if (status != ENUM_ConnectionAttemptStatusType.Success)
   {
      // Could not connect to the reader.
      // Print out the error
      Console.WriteLine(status.ToString());
      // Do something here.
      // Your application should not continue.
      return;
   }
 
   /*
      If you successfully connect to the reader, the next step is to
      create a delegate. The delegate determines which function gets
      called when a report event occurs.
   */
   reader.OnRoAccessReportReceived += new
   delegateRoAccessReport(OnReportEvent);
 
   // Send the messages
   Delete_RoSpec();
   Add_RoSpec();
   Enable_RoSpec();
 
   // Keep reading tags until the user presses return
   Console.ReadLine();
 
   // Cleanup the reader by deleting the ROSpec
   Delete_RoSpec();
}

 


Was this article helpful?
1 out of 1 found this helpful

Comments

Impinj (NASDAQ: PI) wirelessly connects billions of everyday items such as apparel, medical supplies, and automobile parts to consumer and business applications such as inventory management, patient safety, and asset tracking. The Impinj platform uses RAIN RFID, delivering information about items to the digital world and enabling the Internet of Things.