Impinj LTK Programmers Guide PDF
Impinj LTK Programmers Guide PDF
Version 5.12.0
http://www.impinj.com
Impinj, Octane, Speedway, xSpan and xArray are either registered trademarks or trademarks of
Impinj, Inc. Visit www.impinj.com/trademarks for additional information about Impinj trade-
marks.
LTK Programmers Guide
Contents
1 Introduction 8
1.1 Purpose of Guide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.2 Intended Audience . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.3 Overview of Contents . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.4 Document Conventions and Terms . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.4.1 Document Terms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.5 Technical Support . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.6 Other Reference Material . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.6.1 LLRP Standards Document . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
1.6.2 Octane LLRP Reference Guide . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.6.3 Speedway Embedded Developers’ Guide . . . . . . . . . . . . . . . . . . . . 12
1.6.4 Language-Specific API Reference Documentation . . . . . . . . . . . . . . . 12
1.6.5 www.LLRP.org . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
1.6.6 LLRP Toolkit Project on Source-Forge . . . . . . . . . . . . . . . . . . . . . 13
1.6.7 Impinj LTK Extension Files . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.6.8 Impinj Low Level User Data Support Application Note . . . . . . . . . . . . 13
2 LLRP Overview 14
2.1 Origin and Purpose . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.2 Connection Details . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.3 Declarative Specifications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.4 Messages, Parameters, and Fields . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.4.1 Messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.4.2 Fields . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
2.4.3 Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.5 Introduction to LLRP Specs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.5.1 Reader Operation Specs (ROSpecs) . . . . . . . . . . . . . . . . . . . . . . . 19
version 5.12.0 2
LTK Programmers Guide
3 LTK Overview 27
3.1 Origin and Purpose . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
3.2 LTK versus LLRP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
3.3 LTK Definition Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
3.4 LTK Vendor Extensions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
3.5 LTK-XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
version 5.12.0 3
LTK Programmers Guide
version 5.12.0 4
LTK Programmers Guide
6.4.3 LTKJAVA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
6.5 Debugging with LTK . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
6.5.1 LTKC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
6.5.2 LTKCPP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
6.5.3 LTKNET . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
6.5.4 LTKJava . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
version 5.12.0 5
LTK Programmers Guide
version 5.12.0 6
LTK Programmers Guide
14 Notices 201
version 5.12.0 7
LTK Programmers Guide
1 Introduction
version 5.12.0 8
LTK Programmers Guide
• Important LLRP and LTK usage notes are shown in italics in a separate paragraph that
begins with Impinj Best Practice!
• Code examples are formatted in separate text boxes. The name of the toolkit is shown in
bold at the top of the text box, and is not part of the compiled code example. When possible,
color syntax highlighting the appropriate language is used. In many circumstances, there are
several ways to complete a programming task in LTK. When such conditions arise, Impinj
recommends best practices or recommended approaches to use when solving these problems.
These sections are shown in code comments preceded by the following bold keywords: Impinj
Best Practice!
LTKCPP
version 5.12.0 9
LTK Programmers Guide
AccessSpec: Access Specification. This data element is passed to the Reader to describe a set of
operations to perform on a tag. It includes a filter set that describes the tag population to which
this rule applies. It also includes a list of read, write, lock, and kill commands to execute on each
tag that matches the filter.
AISpec: Antenna Inventory Specification. An ROSpec contains a list of AISpecs that are executed
in order. Each AISpec contains RF parameters, inventory parameters, and duration.
AntennaConfiguration: Each AISpec could contain one or more AntennaConfiguration param-
eters. These parameters describe the RF parameters (power, frequency, receive sensitivity) and
Gen2 settings (mode, filters, session) to use during an AISpec execution.
Custom Extension: A mechanism of LLRP that allows vendors to add functionality beyond the
standard behavior of LLRP.
EPC: Electronic Product Code. A unique identification code that is stored in the chip on an
RFID tag as a product goes through the supply chain.
EPCglobal – EPCglobal is an organization that leads the development of industry-driven stan-
dards for the Electronic Product Code (EPC) that support the use of RFID.
FOV: Field-of-view. The angular extent of the observable world that is visible to a Reader at a
given moment. This is typically related to antenna type, number, and position.
Impinj LTK: The LTK, which has been extended, compiled, tested, and packaged by Impinj for
use with Readers.
LLRP: The EPCglobal Low Level Reader Protocol standard.
LTK: The llrp-toolkit, an open source LLRP library development project.
RO: Reader Operations. The group chartered within EPCglobal to define LLRP.
ROSpec: Reader Operation Specification. This data element is passed to the Reader to describe
a bounded (start and end), triggered inventory operation.
STL: Standard Template Library.
XML: Extensible Markup Language. XML is the World Wide Web Consortium’s (W3C) recom-
mended standard for creating formats and sharing data on the Web.
version 5.12.0 10
LTK Programmers Guide
• Section 6 provides the LLRP standard describes the theory of operation and provides the
background information necessary to understand the LLRP control model and the high level
features of LLRP. This section is very helpful to Impinj LTK programmers because it provides
additional descriptions of the LLRP usage model.
• Sections 7 and 8 discuss the formatting conventions for the document and provide information
on the logical structure of LLRP.
• Section 9 describes the messages, parameters, and fields required to discover Reader capabil-
ities through LLRP.
• Section 10 describes the messages, parameters, and fields required to perform Reader opera-
tions (such as inventory) through LLRP.
• Section 11 describes the messages,parameters, and fields required to perform Reader access,
including write/lock/kill through LLRP.
• Section 12 provides the messages, parameters, and fields required to perform Reader config-
uration through LLRP.
• Section 13 provides the messages, parameters, and fields required to collect report data (for
example, tag reads) through LLRP.
version 5.12.0 11
LTK Programmers Guide
• Section 14 describes the messages, parameters, and fields required to get error status through
LLRP.
• Section 15 defines Gen2-specific messages, parameters, and fields.
• Section 16 describes the binary encoding of LLRP and should not be necessary for users of
the LTK.
The Octane LLRP reference guide provides a summary to system architects for validating and
understanding the standard LLRP features supported by Octane LLRP, as well as the unique
Octane LLRP custom extensions that provide additional capabilities. This guide provides detailed
information to developers who are planning to support Readers via LLRP.
Use Octane LLRP as a companion to this document. This document provides the structure for how
to use the LTK for both standard LLRP and Octane LLRP extensions. The Octane LLRP guide
provides the detailed information for each Impinj message, parameter, and field. The latest release
of Octane LLRP is available on the Impinj support portal at http://support.impinj.com.
The Speedway Embedded Developers’ Guide provides instructions for compiling and loading appli-
cations that run on the Speedway Reader platform.
The latest release of the Speedway Embedded Developers’ Guide is available on the Impinj support
portal at http://support.impinj.com.
Some of the LTK implementations have built-in documentation on the API language specifics.
View the individual LTK downloads on http://support.impinj.com for more information.
1.6.5 www.LLRP.org
Read more about LLRP at http://www.llrp.org, which provides general information on the
open source LLRP toolkit effort. It includes information about companies and organizations that
use the LTK, and information about open source tools and documentation to accompany LTK or
LLRP. This site does not contain any information about the Impinj-specific extensions to LLRP
or LTK.
version 5.12.0 12
LTK Programmers Guide
The Impinj LTK is heavily based on the open source project llrp-toolkit hosted at source-forge.
You can access the project at http://www.sourceforge.net/projects/llrp-toolkit.
Impinj is a committed contributor to the llrp-toolkit project. We submit all bug fixes and enhance-
ments that benefit the community llrp-toolkit project. Because of the disparity in Impinj and
llrp-toolkit release cycles, individual versions of the Impinj LTK may not match the open source
behavior exactly.
The LTK is supported in several languages, including languages not formally released by Impinj,
such as Perl.
In order to build LLRP libraries with support for Impinj Octane extensions, the Impinj LTK
extension xml files are required. Impinj extensions also allow LTK-XML based applications to
validate their packet descriptions. The latest release of the Impinj LTK Extension files is available
on the Impinj support portal at http://support.impinj.com.
In order to build the open source llrp-toolkit with support for Impinj extensions, the Impinj LTK
extension files are required. These files allow the toolkit to automatically generate support for
Octane LLRP extensions. Impinj extensions also allow LTK-XML based applications to validate
their packet descriptions. The latest release of the Impinj LTK Extension files is available on the
Impinj support portal at http://support.impinj.com.
Starting with Octane 4.4, the Reader Gateway supports RF phase and RSSI reporting through
custom LLRP extensions. The ImpinjTagReportContentSelector parameter allows the operator
to configure additional parameters to be reported via the TagReportData parameter including
the ImpinjRFPhaseAngle and ImpinjPeakRSSI parameters. For a complete description of how to
enable the low level user data feature for the Reader, refer to the Octane LLRP Reference Guide
and the application notes, titled Low Level User Data Support. Both documents are available on
the Impinj support portal at http://support.impinj.com.
version 5.12.0 13
LTK Programmers Guide
2 LLRP Overview
This section provides an introduction to LLRP. If you are already familiar with the LLRP standards,
you can continue to section 3. For a more complete description of LLRP, refer to the EPCglobal
standards documentation referenced in Section 1.6.1.
version 5.12.0 14
LTK Programmers Guide
commands to a Reader implementation of LLRP (hosted in the reader). The Reader implemen-
tation answers commands by using responses, and also generates asynchronous data called events
and reports.
LLRP supports reader- or client-initiated connections. By default Speedway will operate with
client-initiated connections, waiting until the application has connected on the well-known TCP
port 5084. In reader-initiated mode, the Reader will actively maintain connection to the host
application. If the connection is broken for any reason, the Reader will continually try to reconnect,
re-establishing connection without intervention from the host.
LLRP does not specify the behavior when the connection is broken. The Reader will continue to
operate and collect tag data. The collected data will be (optionally) delivered upon the resumption
of the connection if the HoldEventsAndReports field has been set in the LLRP configuration.
Readers only allow one LLRP connection at any time. Attempts to connect when an existing
connection is active and healthy, results in a standard LLRP message sent back to the application,
which reporting that another connection is currently active.
version 5.12.0 15
LTK Programmers Guide
2.4.1 Messages
Client/
Message Name Resp Reader Description
ADD_ACCESSSPEC Y C Adds an AccessSpec to the reader
ADD_ROSPEC Y C Adds an ROSpec to the Reader
CLIENT_REQUEST_OP Y R Not supported by Impinj LTK
CLOSE_CONNECTION Y R Reader generates this message before a
client initiated connection closure
CUSTOM_MESSAGE N/A R or C A custom message wrapper defined by
LLRP to hold all custom message
communication between the client and
reader
DELETE_ACCESSSPEC Y C Deletes an AccessSpec from the Reader
DELETE_ROSPEC Y C Deletes an ROSpec from the Reader
DISABLE_ACCESSSPEC Y C Disables an AccessSpec on the Reader
DISABLE_ROSPEC Y C Disables an ROSpec on the Reader
ENABLE_ACCESSSPEC Y C Enables an AccessSpec on the Reader
version 5.12.0 16
LTK Programmers Guide
Client/
Message Name Resp Reader Description
version 5.12.0 17
LTK Programmers Guide
Client/
Message Name Resp Reader Description
2.4.2 Fields
Fields are individual data elements with a known format. LLRP has ten basic types listed in Table
2.2 below for reference. In addition to these basic types, LLRP defines fields that contain lists (or
vectors) of some these basic types, which are also noted in Table 2.2 below. When constructing
LLRP messages/parameters, all fields must be present and within their valid range, although some
fields are conditionally ignored.
Table 2.2 LLRP Basic Field Types
version 5.12.0 18
LTK Programmers Guide
2.4.3 Parameters
Parameters are named data elements that can contain other parameters and/or fields. There are
two distinctions between parameters and fields. Parameters can be optional elements that do
not need to be present within the enclosing message or parameter. Parameters have a complex
structure unlike the simple field types and can contain other parameters and fields. LLRP has
numerous (nearly one hundred) defined parameters. Each parameter is uniquely identified by a
parameter ID.
ROSpecs control the operation of the Reader. A ROSpec can be thought of as a description of
the Inventory operations that the Reader is requested to perform. The Reader supports one
ROSpec at a time. An ROSpec contains the following elements. Unless otherwise noted, all
elements are mandatory.
• ROSpecID – an ID set by the client application to uniquely identify this spec in subsequent
commands.
• Priority – must always be set to 0.
• CurrentState – The current state of the ROSpec. When the application is adding ROSpecs,
this state must be set to Disabled.
• ROBoundarySpec – A description of the start and stop conditions for this operation.
• ListofSpecs – A list of one or more AntennaInventorySpecs to execute when this ROSpec
activates.
version 5.12.0 19
LTK Programmers Guide
• ROReportSpec (optional) – a parameter that describes when tag reports should be for-
warded to the application and what data they should contain. This parameter is optional;
if the value is not present, the reports generate according to the Reader’s current settings
available through the GET_READER_CONFIG message.
The ROBoundarySpec contains a start and stop trigger to define when the ROSpec should activate
and deactivate on the Reader. The start trigger supports the following start conditions:
• None/Null – The enabled ROSpec will not start unless started by the client application via
the START_ROSPEC message.
• Immediate – The ROSpec starts immediately once enabled. Note: with this trigger condi-
tion, an enabled ROSpec continuously restarts itself until disabled by the application using
the DISABLE_ROSPEC message.
• Periodic – The enabled ROSpec is triggered to start periodically. This allows operation
where an application directs the Reader to inventory tags for 10 seconds (such as cabinet or
shelf) every 5 minutes and reports the results back at the end of each 10 second read internal.
• GPI – The enabled ROSpec triggers a start when the GPI event enters a certain state (such
as TRUE/HIGH).
• None/Null – The enabled ROSpec only stops when stopped by the client application via the
STOP_ROSPEC message. Note: the ROSpec also stops when all of its enclosing operations
(AntennaInventorySpecs) complete.
• Duration – The enabled ROSpec stops when it has been active for a specified duration.
• GPI – The enabled ROSpec triggers a stop when a GPI event enters a certain state (for
example TRUE/HIGH).
Impinj Best Practice! Even if you are using complex start and
stop triggers, an LLRP Reader always responds to the
START_ROSPEC and STOP_ROSPEC messages. This is often very useful for
manually triggering complex specs for testing within your application.
The main RFID configuration for the inventory operation lies within the AntennaInventorySpecs
(called AISpec for shorthand). Within this parameter, the application must specify the antenna
set and the protocol (Gen2). Beyond that, all other RFID configuration (such as transmit power)
can be set via the Speedway configuration. If desired, it can also be specified within the AISpec
parameter.
version 5.12.0 20
LTK Programmers Guide
Typically, a ROSpec contains a single AISpec. This spec is usually configured to enable all antenna
ports (that have antennas connected) and uses the default Gen2 and RF configuration as specified
in the SET_READER_CONFIG message. However, a ROSpec might contain multiple AISpecs.
Multiple AISpecs can be programmed into the Reader for more complex behavior that is executed
autonomously and without intervention from the application. An application can include multiple
AISpecs within a ROSpec for the following reasons:
• The application requires tight timing or sequence control over antennas. This could be
executed by several AISpecs, each containing one or more antennas.
• The application requires different RF configurations at different times. One example is a use
case where the application first reads at low power and then reads at higher power to find
all of its related case tags. A programming option would be to use two AISpecs within a
single ROSpec that would execute automatically by the Speedway. This does not apply to
the xArray Gateway.
The application requires different Gen2 parameters at different times. One example of this is a
use case where, upon a trigger, the application first tries to read pallet tags by applying a Gen2
inventory filter. When no new pallet tags are visible, the application then instructs the Reader to
search for all tags (or all case tags). One programming option for this scenario would be to use
two AISpecs within a single ROSpec that would automatically be processed by the Reader.
When multiple AISpecs are contained within a ROSpec, the Speedway handles them as follows:
• The Speedway applies the configuration contained in the first AISpec as soon as the ROSpec-
StartTrigger is satisfied, and begins the inventory operation.
• When that operation completes, defined by the stop trigger in the AISpec, the Reader applies
the configuration contained in the second AISpec and begins that inventory operation.
• This process continues until one of the following two things occurs, at which the ROSpec
stops automatically.
The client uses the LLRP messages and manages the ROSpecs :
version 5.12.0 21
LTK Programmers Guide
Each of these client-initiated commands has a corresponding response sent by the Reader that
reports the success or error status of the request (for example, the ADD_ROSPEC_RESPONSE)
for a total of 14 messages that control ROSpecs.
LLRP AccessSpecs handles the extended tag operation of Gen2: read, write, lock, and kill. They
provide an extensible mechanism which allows future Gen2 and proprietary tag operations.
During inventory, an LLRP Reader continually monitors the list of enabled AccessSpecs. When a
spec matches certain conditions described below, the spec activates and the Reader performs the
operations contained within the spec. Based on the report configuration within the spec, results
are generated and are sent to the client application.
AccessSpecs design supports multiple, different air protocols with unique commands. Because the
Reader supports only Gen2 commands, this description simplifies the spec and conveys the general
intent rather than the exact syntax.
A Gen2 AccessSpec contains:
• AccessSpecID – uniquely identifies this spec with an ID set by the client application.
• AntennaID – Specifies the set of antennas that this spec considers active. Specifying zero
enables this spec for all antennas.
• ProtocolID – Specifies the protocol for access. This LLRP field allows support for other
future protocols. Currently LLRP and Speedway support only Gen2.
• CurrentState – The current state of the AccessSpec. When the application adds AccessSpec,
this state must be set to Disabled.
• ROSpecID – The ROSpec for which this spec should be considered active.
version 5.12.0 22
LTK Programmers Guide
• AccessSpecStopTrigger – A stop condition that disables and deletes the AccessSpec itself.
Used this for tag commissioning where the operation can only be performed a limited number
of times (once).
• AccessCommandOperation – Performs the list of read, write, lock, and kill operations.
• AccessReportSpec – Defines how the access data reports to the application using this optional
parameter.
Each of these client-initiated commands corresponds to a response sent by the Reader, which
reports success or the error status of the request,for example ADD_ACCESSSPEC_RESPONSE
for a total of 12 messages that control AccessSpecs.
For example, an application would add one or more AccessSpecs to:
• Write an EPC into a tag. In this instance, construct an AccessSpec that contains a write
operation to write the EPC, a write operation to write a Gen2 password, and a lock operation
to lock the EPC and password memory.
version 5.12.0 23
LTK Programmers Guide
2. Application requests the Reader’s capabilities (optional). This allows the application to
learn the unique characteristics of the Reader. Information about antennas (count), transmit
power, frequency control, and LLRP features are available via the capabilities.
3. Application requests the Reader’s configuration (optional). Some applications learn about
the current Reader configuration before applying any new configuration values.
4. Application determines the configuration of the Reader, and changes the configuration if
necessary (optional). This includes restoring the Reader to its LLRP default values, settings
power, channel, and Gen2 default values.
Impinj Best Practice! Unless your application is specifically
written to handle disconnected operation via LLRP, we recommend
restoring the LLRP factory defaults when connecting to a Reader.
A LTK example of this procedure is described in Section 6.5.3. A sample timeline of this exchange
(steps 5-8) is shown in Figure 2.1.
version 5.12.0 24
LTK Programmers Guide
version 5.12.0 25
LTK Programmers Guide
competitive advantages of their respective products. In creating this new standard, the EPCglobal
Reader Operations working group included a rich set of vendor extension points in LLRP. These
extension points allow vendors the flexibility to innovate and differentiate their products, yet still
operate within the standardized network framework. With clearly defined extension points, the
controlling software can more easily expose unique extensions to particular products. End-users
can also propose extensions for features specific to their enterprise.
version 5.12.0 26
LTK Programmers Guide
3 LTK Overview
The llrp-toolkit (LTK) is developed on source-forge (Section 1.6.6) and was released for general
availability in November of 2008. The LTK represents the contributions of several industry leading
companies and research labs.
version 5.12.0 27
LTK Programmers Guide
LTK offers some additional functionality that is not specified by the LLRP standard. Each LTK
offers support for open and closing connections with the Reader, for performing message transac-
tions that automatically wait for the Reader’s response, and for reporting errors during message
construction and parsing.
A single machine description file ’llrp-1x0-def.xml’ describes the messages and parameters in
the LLRP standard protocol (version 1.0.1). Each toolkit library parses this file and builds the
internal LLRP object representations. LTKC, LTKCPP, LTKJAVA, and LTKNET process this
definition file at compile time to build the classes and methods to support building and parsing
the LLRP message. In addition to containing the machine description of the protocol, the file also
contains embedded xhtml documentation and links to the LLRP standard for use by auto-generated
documentation and tooltips.
An example excerpt from the LLRP specification and the LTK ‘llrp-1x0-def.xml’ file for the
UTCTimestampParameter is shown below:
UTCTimestamp Parameter
• Compliance requirement: Compliant Readers and Clients that have UTC clocks SHALL
implement this parameter.
UTCTimestamp 7.1.3.1.1.1
• MicroSeconds: Unsigned Long Integer. This is the field time elapsed since the Epoch
(00:00:00 UTC, January 1, 1970) measured in microseconds.
2868 16.2.2.1
version 5.12.0 28
LTK Programmers Guide
<annotation>
<documentation>
<h:a href=“http://www.epcglobalinc.org/standards/
llrp/llrp/_1/_0/_1-standard-20070813.pdf#page=
37&view=fit”
>LLRP Specification Section 7.1.3.1.1.1</h:a>
<h:a href=“http://www.epcglobalinc.org/standards/
llrp/llrp/_1/_0/_1-standard-20070813.pdf#page=
131&view=fit”
>LLRP Specification Section 16.2.2.1</h:a>
</documentation>
<description copyright=”Copyright 2006, 2007, EPCglobal
Inc. The proprietary text of EPCglobal Inc. included here
is not a Contribution to the LLRP toolkit, under Apache
License, Version 2.0. The right to use the proprietary
text is limited to reproduction and display thereof within
the work.”>
<h:p>
The timestamps in LLRP messages or parameters can be
either the uptime or the UTC time [UTC]. If a Reader has
version 5.12.0 29
LTK Programmers Guide
</parameterDefinition>
LLRP-DEF
<customMessageDefinition name=“IMPINJ_ENABLE_EXTENSIONS”
vendor=“Impinj” subtype=“21”
namespace=“Impinj” responseType=
“IMPINJ_ENABLE_EXTENSIONS_RESPONSE”>
<annotation>
<documentation>
</documentation>
<description copyright=“Copyright 2007, 2008 Impinj
Inc.”>
<h:p>The IMPINJ_ENABLE_EXTENSIONS
message only applies for the duration of the current
LLRP connection.
version 5.12.0 30
LTK Programmers Guide
</customMessageDefinition>
3.5 LTK-XML
It is often useful to share the details of your LLRP specs with another party, such as when you
contact technical support. However, LLRP is a binary protocol, which makes the sharing of
messages in a human-readable form difficult. The LTK allows conversion of any LLRP message or
parameter into LTK-XML for easy viewing and sharing via e-mail. LTK-XML is an XML-based
representation of the LLRP message contents. There is an exact one-to-one mapping between any
LTK-XML message and its corresponding binary LLRP message.
For example, a SET_READER_CONFIG message that enables GPI (3) can be displayed in the
native LLRP binary format, compactly and efficiently as:
LLRP-HEXBINARY
04 03 00 00 00 13 00 00 00 00 00 00 E1 00 08 00 03 80 02
Hdr Msg Length MessageID Fac Type Len GPI # Cfg State
04 03 00 00 00 13 00 00 00 00 00 00 E1 00 08 00 03 80 02
version 5.12.0 31
LTK Programmers Guide
<ResetToFactoryDefault>False</ResetToFactoryDefault>
<GPIPortCurrentState>
<GPIPortNum>3</GPIPortNum>
<Config>True</Config>
<State>Unknown</State>
</GPIPortCurrentState>
</SET_READER_CONFIG>
Impinj Best Practice: When you submit support requests while you are
programming with the LTK, we recommended that you include the LTK-XML
copies of your ROSpecs and AccessSpecs. This gives our support team the
information they need to analyze your issue.
version 5.12.0 32
LTK Programmers Guide
4.1 Overview
LTKC library – This library is suitable for use in Linux-based ANSI C programs that require
LLRP support. Impinj distributes periodic releases of this toolkit. The releases are named ‘li-
bltkc_sdk_X_Y_Z.tgz’. Each release includes static Linux libraries for x86 and Arm plat-
forms. The ARM library is compatible with Speedway Readers and xArray Gateways. The x86
library is compatible with most 2.6 Linux systems and is useful for debugging and testing em-
bedded applications via a host processor. This library also includes libxml2 libraries to support
LTK-XML decoding for Linux platforms only.
LTKCPP Library – This library is suitable for use in Linux- or Windows-based C++ appli-
cations that require LLRP support. The release includes static Linux and Windows libraries.
Linux libraries are released for x86 and Arm platforms. The Linux Arm library is compatible
with Speedway Readers and xArray Gateways. The Linux x86 library is compatible with most
2.6 Linux systems and is useful for debugging and testing embedded applications via a host pro-
cessor. Windows libraries are compatible with x86 only. This library also includes libxml2 li-
braries to support LTK-XML decoding for Linux platforms only. Two version of this archive
file are released, a ‘libltkcpp_sdk_X_Y_Z.zip’ most suited to Windows platforms, and a ‘li-
bltkcpp_sdk_X_Y_Z.tgz’ version most suited to Linux platforms. Other than line endings
in the header files, these two archives are identical.
LTKNET Library – This library is suitable for use in the Windows C# .NET development envi-
ronment. This release includes dynamic managed assemblies (.dlls) for the C# .NET framework.
A single version of this zip file, ‘libltknet_sdk_X_Y_X.zip’ is available.
LTKJava Library – This library is suitable for use with Java virtual machines (version 1.5 and
later). The release includes jar files with and without Java dependencies. Two versions of this
archive are released, a ‘libltkjava_X.Y.Z.zip’ most suited to Windows platforms, and a ‘libltk-
java_X.Y.Z.tgz’ version most suited to Linux platforms. Other than line endings in the header
files, these two archives are identical.
version 5.12.0 33
LTK Programmers Guide
4.1.2 CHANGES.TXT
All Impinj LTK releases are accompanied by a CHANGES.TXT file which chronicles recent
changes to that particular LTK release with notes on new features, bug fixes, and more. The file
also contains the known defect list at the time of the release.
In the release name examples above, such as ‘libltknet_sdk_X_Y_X.zip’*, the version numbers
are represented as follows:
• “X” denotes the major release version. Changes in the major version represent significant
updates that are not necessarily backward compatible with previous updates.
• “Y” denotes the minor release version. Changes in the minor version represent API changes
and additions that have backward compatibility with previous versions.
• “Z” denotes the maintenance release version. Changes in the maintenance version represent
fully backward compatible bug fixes and do not affect the programming APIs.
We recommend a re-compile and re-link of your application for all new updates of the LTK.
version 5.12.0 34
LTK Programmers Guide
4.4.1 LTKC
The LTKC comes with a single example that exercises the library with a basic LLRP connection
and inventory. The steps to build and run the example are shown below. The ‘makefile’ for this
example attempts to compile the program for three platforms: x86-Linux, xScale-Linux, and Atmel-
ARM9-Linux. If your host platform does not contain all necessary compilers, the compilation will
fail.
LTKC
$ cd libltkc
$ cd example
$ make
cc -g -I.. -o example1 example1.c ../libltkc_x86.a
$ ls
example1 example1.c Makefile
$ ./example1 speedway-00-06-4b
INFO: Starting run 1 ================
NOTICE: Antenna 2 is disconnected
version 5.12.0 35
LTK Programmers Guide
4.4.2 LTKCPP
The LTKCPP distributions beyond 10.6.x contain the sample code used in this documentation.
Unzipping (or un-tarring) the distribution creates two new directories: ‘Documentation’ and
‘libltkcpp’. ‘Documentation’ contains the HTML-based documentation for programming the
LTK. Access the API documentation by opening the file ‘\Documentation\html\index.html’
in your web browser.
The ‘libltkcpp’ directory contains the library header files, library binary files, as well as the sam-
ple code referenced in this document. The documentation sample code builds using Linux makefile
or Microsoft Visual Studio Solution. The main ‘makefile’ for the documentation samples is ‘li-
bltkcpp/Makefile’. The Visual Studio solution file for documentation samples is ‘ImpinjExm-
ples.sln’.
Ensure that you have the proper tools installed (Section 4.3) to follow and build the Linux examples
below. The examples compile for x86_linux, or arm-Linux. Use the make target all_x86 to build
for x86-Linux, useall_atmel to compile for the Speedway embedded arm platform, and everything
version 5.12.0 36
LTK Programmers Guide
to compile for all targets. Executable applications build in the docSampleX subdirectories and use
suffixes to describe their target platform.
LTKCPP Linux
$ make all_x86
cd docsample1; make all_x86
make[1]: Entering directory ‘/home/impinj/Development/Temp/libltkcpp/docsample1’
make all AR=ar CXX=g++ CPPFLAGS=“-g -Wall -I..” SUFFIX=_x86
…
make[1]: Leaving directory ‘/home/impinj/Development/Temp/libltkcpp/docsample3’
$
version 5.12.0 37
LTK Programmers Guide
version 5.12.0 38
LTK Programmers Guide
$ cd docsample1/
$ ./docsample1_x86 speedway-00-11-b6
EPC: AAAA-AAAA
EPC: BBBB-BBBB
EPC: EEEE-EEEE
EPC: BBBB-BBBB
EPC: DDDD-DDDD
EPC: EEEE-EEEE
EPC: AAAA-AAAA
EPC: BBBB-BBBB
EPC: EEEE-EEEE
NOTICE: Antenna 3 is disconnected
version 5.12.0 39
LTK Programmers Guide
4.4.3 LTKNET
Unzip the archive file and browse to the ‘libltknet’ directory. Open the ‘ImpinjExamples.sln’
solution file to display an LTKNET example.
LTKNET
Build the samples by right-clicking the solution file, or by clicking Build at the top of the IDE
page, as shown in Figure 4.4.
version 5.12.0 40
LTK Programmers Guide
version 5.12.0 41
LTK Programmers Guide
C:\libltknet\DocSample1\bin\Debug>DocSample1.exe speedway-00-11-b6
Impinj C# LTK.NET RFID Application DocSample1 reader - speedway-00-11-b6
Initializing
Adding Event Handlers
Connecting To Reader
Enabling Impinj Extensions
Factory Default the Reader
Adding RoSpec
Enabling RoSpec
Starting RoSpec
EEEEEEEE
DDDDDDDD
BBBBBBBB
version 5.12.0 42
LTK Programmers Guide
AAAAAAAA
======Reader Event 2======2009-05-22T22:28:02.9720000Z
======Reader Event 1======2009-05-22T22:28:02.9670000Z
<AntennaEvent>
<EventType>Antenna_Disconnected</EventType>
<AntennaID>3</AntennaID>
</AntennaEvent>
<AntennaEvent>
<EventType>Antenna_Disconnected</EventType>
<AntennaID>4</AntennaID>
</AntennaEvent>
EEEEEEEE
DDDDDDDD
AAAAAAAA
EEEEEEEE
. . .
4.4.4 LTKJava
Windows
Download the zip file from http://support.impinj.com, and then extract the files from the
archive, as shown in Figure 4.6.
version 5.12.0 43
LTK Programmers Guide
version 5.12.0 44
LTK Programmers Guide
Set up your path to ensure access to both JAVA and ant. Your path might look something like the
example below. You might also need to set your JAVA_HOME environment variables as follows:
LTKJAVA – Windows
ant all
The samples should compile and result in the following output below:
LTKJAVA – Windows
C:\Development\LTK\impinj_java\ltk\LTKJava\libltkjava-10.14.0.2\libltkjava\examples>ant
Buildfile:
C:\Development\LTK\impinj_java\ltk\LTKJava\libltkjava-10.14.0.2\libltkjava\examples\build.x
version 5.12.0 45
LTK Programmers Guide
init:
[echo] using ant version ‘Apache Ant version 1.8.1 compiled on April 30 2010’
[echo] using java version ‘1.6.0_21’
docsample1:
docsample2:
docsample3:
docsample4:
all:
BUILD SUCCESSFUL
Total time: 4 seconds
C:\Development\LTK\impinj_java\ltk\LTKJava\libltkjava-10.14.0.2\libltkjava\examples>
version 5.12.0 46
LTK Programmers Guide
The output should appear similar to what is shown below. Replace 192.168.1.5 with the IP address
or hostname of your Reader. Program output looks similar to the display below.
LTKJAVA - Windows
C:\Development\LTK\impinj_java\ltk\LTKJava\libltkjava-10.14.0.2\libltkjava\examples>ant
-Dreadername=192.168.1.5 run-docsample1
Buildfile:
C:\Development\LTK\impinj_java\ltk\LTKJava\libltkjava-10.14.0.2\libltkjava\examples\build.x
init:
[echo] using ant version ‘Apache Ant version 1.8.1 compiled on April 30 2010’
[echo] using java version ‘1.6.0_21’
docsample1:
run-docsample1:
version 5.12.0 47
LTK Programmers Guide
BUILD SUCCESSFUL
Total time: 4 seconds
C:\Development\LTK\impinj_java\ltk\LTKJava\libltkjava-10.14.0.2\libltkjava\examples>
version 5.12.0 48
LTK Programmers Guide
Change to the ‘libltkjava’ directory. Review the ‘CHANGES.TXT’ file to view the latest
known issues and revisions to LTKJAVA. Change to the examples directory.
LTKJAVA
cd libltkjava/examples
Set up your ant path and your ANT_HOME and JAVA_HOME environment variables.
Note: Your values will likely be different than the example, but these variables act as guides for
setting up the correct environment.
LTKJAVA - Linux
export JAVA_HOME=/opt/ltk/jdk1.6.0_18/
export ANT_HOME=/opt/ltk/apache-ant-1.8.0RC1/
export PATH=$PATH:/opt/ltk/jdk1.6.0_18/bin:/opt/ltk/apache-ant-1.8.0RC1/bin/
Use the following code for building the examples using ant.
LTKJAVA - Linux
ant all
You should see output similar to that shown below. Individual programs can build using
‘ant docsampleX’ where X is the sample number.
LTKJAVA - Linux
./libltkjava/examples/build.xml
init:
[echo] using ant version ‘Apache Ant version 1.8.0RC1 compiled on January
5 2010’
[echo] using java version ‘1.6.0_18’
docsample1:
version 5.12.0 49
LTK Programmers Guide
docsample2:
docsample3:
docsample4:
all:
BUILD SUCCESSFUL
Total time: 2 seconds
Replace 192.168.1.5 with the IP address or hostname of your Reader. The program output looks
similar to the display below.
LTKJAVA - Linux
/mnt/hgfs/Development/LTK/impinj_java/ltk/LTKJava/libltkjava/examples/build.xml
version 5.12.0 50
LTK Programmers Guide
init:
[echo] using ant version ‘Apache Ant version 1.8.0RC1 compiled on January
5 2010’
[echo] using java version ‘1.6.0_18’
docsample1:
run-docsample1:
version 5.12.0 51
LTK Programmers Guide
BUILD SUCCESSFUL
Total time: 5 seconds
[revolution@RevolutionEDK examples]$
version 5.12.0 52
LTK Programmers Guide
LTK CConnection class manages the connection to the Reader, and also provides members to
send messages, receive messages, perform transactions (a send with an expected reply), and to get
error status. Examples of the use of the CConnection class are shown in Section 6.
version 5.12.0 53
LTK Programmers Guide
version 5.12.0 54
LTK Programmers Guide
• Simple fields, such as integer or enumerated values. For these fields, there are the accessor
functions getfield() and setfield(). Vector fields, such as a string or a vector of 32-bit
frequencies, have accessor functions getfield() and setfield(). The setfield() accessor
frees memory currently held by the vector field.
• Single subparameter pointers, used for both mandatory (1) and optional (0-1) subparameters.
For subparameter pointers, use accessor functions getsubparam() and setsubparam().
version 5.12.0 55
LTK Programmers Guide
The setsubparam() accessor removes the current subparameter, if there is one,from the
all-list and destructs it. It then adds the new subparameter to the all-list and sets the
specific pointer. The setsubparam() function returns a status code, either OK (0) for
success or a specific code for an error. The getsubparam() accessor returns a pointer to the
subparameter. In most cases the type of the pointer matches the subparameter. However,
for choices such as OpSpecs or SpecParameter, the pointer is a generic parameter type, so
the application must determine the actual type and cast accordingly. For more information,
see section 5.1.5 “How to Determine the Type of an Element”.
• Lists of subparameters, used for mandatory (1-N) and optional (0-N) lists. For these subpa-
rameters there are accessors beginsubparameter(), endsubparameter() for C++, nextsubpa-
rameter() for C, and addsubparameter(). The addsubparameter() accessor links the subpa-
rameter to the all-list. The addsubparam() function returns a status code, either OK (0) for
success or a specific code for an error. C++ uses the Standard Template Library (STL) lists
. In C there is no similar intermediate data structure. In both cases, similar to the single
subparameter, the type of the pointer depends on whether the subparameter is a specific
type or a choice.
In C++, the accessor functions are members of the class and the names are concise. In C, the
names of the accessor functions can be long and include the name of the element type (such as
LLRP_GET_READER_CAPABILITIES_getRequestedData).
For example, the LTKCPP API for the LLRP InventoryParameterSpec contains the following set
and get methods, including examples of lists (antennaConfiguration), fields (ParameterSpecID),
and vectors (ProtocolID).
version 5.12.0 56
LTK Programmers Guide
As a rule, we have found it best to use element constructors that allocate instances, and use the
accessor functions to manipulate and access fields and subparameters. In C++, this practice should
be considered mandatory. In C, it is possible to use local instances (as shown in the example) by
using great care.
We recommend this because of the destructors. LTKC/LTKCPP does not support mixing allocated
elements with non-allocated element, such as local variable instances of elements. The destructors
walk the all-lists recursively and delete (free) subparameters and vectors. Freeing a locally allocated
instance can cause havoc.
The accessor functions handle list maintenance and incremental destructing of vectors and sub-
parameters that are no longer needed. If you do not use the accessors, use appropriate memory
version 5.12.0 57
LTK Programmers Guide
At times a message or parameter type cannot be known in advance. The type displays and is deter-
mined at run-time. For example, a notification message arrives as an RO_ACCESS_REPORT, a
READER_EVENT_NOTIFICATION, or a KEEPALIVE. Another example in the ROSpecs lists
SpecParameters. Each of these might be an AISpec, an RFSurvey spec, or a CustomParameter.
An additional example is OpSpecs and OpSpecResults.
All elements display a type label. The type label points to a type descriptor. You can determine
the element type by comparing the type label pointer to the address of a known type descriptor.
After the type is known, use casting for the specific element type.
This example shows the function awaitAndPrintReport(). A LTKCPP sequence appears as:
LTKCPP
/*
* What happens depends on what kind of message
* received. Use the type label (m_pType) to
* discriminate message types.
*/
pType = pMessage->m_pType;
/*
* Is it a tag report? If so, print it out.
*/
if(&CRO_ACCESS_REPORT::s_typeDescriptor == pType)
{
CRO_ACCESS_REPORT * pNtf;
pNtf = (CRO_ACCESS_REPORT *) pMessage;
printTagReportData(pNtf);
version 5.12.0 58
LTK Programmers Guide
1. Create and validate an LTK-XML decoder using the type Registry that you built.
LTKCPP
CXMLTextDecoder * pDecoder;
/* Build a decoder to extract the message from XML */
pDecoder = new CXMLTextDecoder(m_pTypeRegistry,“setReaderConfig.xml”);
if(NULL == pDecoder)
{
version 5.12.0 59
LTK Programmers Guide
LTKCPP
LTKCPP
delete pDecoder;
4. To modify the message, convert it to the correct type. In this case, it is a SET_READER_CONFIG
message. If you do not need to touch the message (for example, to modify a field), this step
can be omitted.
LTKCPP
if(&CSET_READER_CONFIG::s_typeDescriptor != pCmdMsg->m_pType)
{
}
/* get the message as a SET_READER_CONFIG */
pCmd = (CSET_READER_CONFIG *) pCmdMsg;
version 5.12.0 60
LTK Programmers Guide
Error reporting is provided through the CErrorDetails structure, where LTKC/LTKCPP provides
the following error information to the application:
• A status code, where 0 always represents OK. The status code is enumerated and the appli-
cation easily distinguishes errors. Error codes are defined by an enumeration in ‘ltkc_base.h’
or ‘ltkcpp_base.h’.
• A string error message, where NULL represents no error. This simplifies the application log
or display error message without needing to interpret the status code.
• A pointer to a reference typeDescriptor contains basic information about an LLRP message
or parameter type, such as name and type number. An application uses a typeDescriptor
to print the name of a type. The meaning of the errorDetail’s typeDescriptor depends
on the error. For example, this might be the type of an unexpected parameter, or a missing
parameter, or a parameter being decoded when there was a data underrun. NULL represents
no reference type available.
• A pointer to a reference fieldDescriptor contains basic information about an LLRP field,
such as the name and basic type. An application uses a fieldDescriptor to print the name
of a field. The errorDetail’s fieldDescriptor reports only field encode and decode errors.
NULL represents no reference field available.
CErrorDetails instances appear within a decoder instance, encoder instance, and a connection
instance. The application can access them directly.
version 5.12.0 61
LTK Programmers Guide
if(NULL == pRspMsg)
{
5.1.8 Extensions
version 5.12.0 62
LTK Programmers Guide
contain custom parameters within reader capabilities that are vendor-specific. The application
must be capable of safely ignoring custom parameters if the connected reader is unknown.
However, the LLRP specification says that when a Reader receives a custom parameter that it
does not recognize, it must report an error. This also derives from the standards group decisions
for the LLRP use cases. For example, assume that the application sends a custom parameter in a
SET_READER_CONFIGURATION that the Reader does not understand. It is a safe conclusion
that the Reader functions will not act as the application intends. Normally, the Reader responds
with an error.
Therefore, the LTK implementations, LTKC/LTKCPP included, provide for tolerance of unrecog-
nized custom parameters and leave it to the higher layers to detect and handle errors.
Constructing Custom Elements
When an extension definition processes, the same templates are used to create per-element classes
(structs), type descriptors, constructor functions, destructor functions, and so on.
This means that constructing and manipulating a custom message or parameter is similar to the
process for a standard message or a parameter. The primary difference is that standard elements
have specific accessors for standard subparameters. Custom parameters use the enclosing element
accessors for a single list of all custom elements, rather than a specific list for each custom parameter
type.
Accessors
Standard parameters and messages with extension points represent them as a list of CustomPa-
rameters. When CustomParameters occur within a parameter or message, the LTKCPP code
generation process creates a list of CParameters (similar to choices), and names the subparameter
list Custom. The accessor functions for the custom list use pointers to the generic CParameters
type. For more information, see section 5.1.3 Fields, Subparameters, and Accessor Functions.
If the application tries to add a CustomParameter to an extension point list (also known as Custom)
that is not allowed at that point, the addCustom() accessor function returns an error. See Figure
5.4 for an example of the custom parameter accessors.
When the application scans the extension point list (also known as CustomParameter), it must
check each subparameter type. Before you (optionally) process the subparameters, see section
5.1.5 How to Determine the Type of an Element for more information.
Enrollment in TypeRegistry
Each LTK definition file processes the LTKCPP enrollment function when compiling the libraries.
The enrollment function takes a typeRegistry as an argument, and adds the typeDescriptors
for the definition file to the registry. The application program must call the enrollment function
for each set of extensions it uses.
version 5.12.0 63
LTK Programmers Guide
In this example, function run() calls getTheTypeRegistry(). This function constructs a new
CTypeRegistry and enrolls the standard LLRP message and parameter types. See Section 6.2 for
an example of this process.
5.2 LTKNET
The LTKNET follows the LLRP class hierarchy. A base message class implements the LLRP mes-
sage header information, as well as the encoding/decoding method FromBitArray, ToBitArray,
and the XML encoding method ToString. All LLRP messages (including CUSTOM_MESSAGE)
are derived from this class. A base parameter class implements the LLRP parameter header infor-
mation, as well as the encoding/decoding method FromBitArray, ToBitArray, and the XML
encoding method ToString. All LLRP parameters (including CUSTOM_PARAMETERS) derive
from this class.
5.2.1 LLRPClient
The LLRPClient class controls the state for the connection to a single Reader. This class contains
methods to open and close the Reader connection, which is a transaction method that sends LLRP
messages and awaits the responses. Event handlers handle asynchronous incoming messages from
the Reader.
version 5.12.0 64
LTK Programmers Guide
The Impinj_Installer class supplies a single static method that allows the application to install
the Impinj Extensions. The LLRPXMLParser class allows the LTKNET to import LTK-XML
messages directly into C# objects. See Figure 5.12 for details.
version 5.12.0 65
LTK Programmers Guide
Figure 5.13 shows a class diagram with select message types. Many messages that enable/disable,
start/stop, or get data from the Reader all follow the same format and are not shown here. The
following primary message classes are required to perform basic LLRP operations.
version 5.12.0 66
LTK Programmers Guide
Key classes and methods for building PARAM_ROSpecs are shown in Figure 5.14. Note that
the PARAM_AISpec is included within the ROSpec as part of its SpecParameter list. This
coupling is loose because LLRP allows coupling of other specs into the PARAM_ROSpecs as
well. Speedway supports only AISpecs as part of the PARAM_ROSpecs SpecParameter list.
version 5.12.0 67
LTK Programmers Guide
Key parameters for building a PARAM_AccessSpec are shown in Figure 5.15. The individual
Gen2 operations (read, write …) loosely couple into the access command object through the
UNION_AccessCommandOpSpec union. This allows the expansion of non-gen2 based operations
as per the LLRP standard charter. The same is true of the PARAM_C1G2TagSpec within the
UNION_AirProtocolTagSpecs.
version 5.12.0 68
LTK Programmers Guide
version 5.12.0 69
LTK Programmers Guide
5.3 LTKJAVA
The following sections explain specifics of using LTKJAVA for encoding, decoding, sending, and
receiving LLRP messages.
The most basic interface to the Reader is via the LLRPEndpoint interface. This interface
provides a mechanism to receive messages from the Reader. User classes that interact with the
Reader should implement this interface.
Messages from the Reader are passed to the application through the messageReceived method
of the LLRPEndpoint class. From a simple application perspective, implementing this mes-
sageReceived method gets the EPC tag data from the Reader. The example in Figure 5.16
shows a simple implementation of the messageReceived method.
LTKJAVA
version 5.12.0 70
LTK Programmers Guide
5.3.2 Connections
LLRPConnection objects hold the state for a connection to a single Reader. A connection allows
the sending and receiving of LLRPMessage(s).
A connection self-initiates, or the Reader initiates the connection. Self-initiated connections open
(initiate) by the application. Typical applications use self-initiated connections.
Although you can create an LLRPConnection object directly, most applications use a subclass of
LLRPConnection, which creates their connection objects. For self-initiated connections, use the
LLRPConnector class (derived from LLRPConnection), which creates an LLRPConnection
object. To use Reader-initiated connections, create an LLRPAcceptor object, which is also
derived from LLRPConnection.
Figure 5.17 shows an example of creating a connection to a Reader with the LLRPConnector
class. This example shows that connection failures are returned to the application through the
LLRPConnectionAttemptFailedException event.
LTKJAVA
e1.printStackTrace();
System.exit(1);
version 5.12.0 71
LTK Programmers Guide
After it is connected, the application sends and receives messages through the LLRPConnection
object (parent) returned by the LLRPConnector constructor. The LLRPConnection API
provides two different methods when it is sending messages: send and transact. Send delivers
the message to the Reader; as soon as the message is sent, the method returns. It does not
guarantee that the message is received by the Reader. Transact sends the message and then
awaits a response from the Reader. The method blocks until the response is received or until a
timeout occurs. Transact should be used for most LLRP messages sent from the application. An
exception is the GET_REPORT message, which does not have a response message.
In the previous few sections, we reviewed how to communicate with the Reader and to how send
and receive LLRPMessage objects. This section discusses LLRPMessages and their definitions.
LLRPMessage(s) send and receive information to and from the Reader. An LLRP contains zero
or more simple data fields followed by a set of LLRPParameters.
LLRP Messages all derive from LLRPMessage classes. This provides the basic message interface
and encodes LTKJAVA objects into LLRP Binary or LTK-XML.
There are two basic methods you can use to build LLRPMessage objects. First, they can be
constructed by creating the individual parameters and adding them to the message using Java
methods. Second, XML strings or files can also create LLRP messages.
Constructing simple messages by using LTKJAVA objects is typically the easiest method. Figure
5.18 shows an example of constructing an ENABLE_ROSPEC message from objects. The
object creates and sets the single field (ROSpecID).
LTKJAVA
logger.info(“ENABLE_ROSPEC …”);
ENABLE_ROSPEC ena = new ENABLE_ROSPEC();
ena.setMessageID(getUniqueMessageID());
ena.setROSpecID(rospec.getROSpecID());
version 5.12.0 72
LTK Programmers Guide
Sometimes it is more convenient to construct longer messages from LTK-XML files. For a de-
scription of how to use LTK-XML, see Section 3.5. The code in Figure 5.19 shows an example of
constructing an ADD_ROSPEC message from an XML file.
LTKJAVA
version 5.12.0 73
LTK Programmers Guide
• The JDOM exception indicates that the XML file could not be read properly into a DOM.
If this occurs, check for syntax errors or other XML errors.
• The InvalidLLRPMessageException indicates that the LTK-XML message could not
be properly converted to LTKJAVA objects due to its format or contents. If this occurs,
double-check namespaces and mandatory LLRP parameters for errors.
• IOException and FileNotFoundException are standard Java file I/O exceptions.
Impinj custom extensions integrate into the LTKJAVA library. LLRP allows custom extensions at
“extension points”. The LTK supports extension points as lists of custom parameters. If you see a
getCustomList method for a message or parameter, the message may contain custom extensions.
To learn what Impinj extensions might be present in each parameter or message, see the Octane
LLRP reference guide. The example in Figure 5.20 iterates a custom extension list.
LTKJAVA
logOneCustom(cust);
version 5.12.0 74
LTK Programmers Guide
}
if (cust.getParameterSubtype().equals( | ImpinjTagInforma-
tion.PARAMETER_SUBTYPE))
{
ImpinjTagInformation itag = (ImpinjTagInformation) cust;
/* process this */
}
5.3.7 Logging
LTKJAVA uses the de facto standard log4j interface. See the examples in the next section for
instances of using log4j in LTKJAVA applications. For more information about using log4j, see
http://logging.apache.org/log4j/1.2/index.html.
5.3.8 Summary
version 5.12.0 75
LTK Programmers Guide
LTKC_DIR = ..
LTKC_LIBS = $(LTKC_DIR)/libltkc_x86.a
$(LTKC_DIR)/libltkcimpinj_x86.a
LTKC_INCL = -I$(LTKC_DIR)
CFLAGS = -g $(LTKC_INCL)
example1 : example1.c
LIBXML2_LIB = $(LTKC_DIR)/libxml2$(SUFFIX).a
example1 : example1.c
LTKCPP_DIR = ..
LTKCPP_LIBS = $(LTKCPP_DIR)/libltkcpp_x86.a \
version 5.12.0 76
LTK Programmers Guide
$(LTKCPP_DIR)/libltkcppimpinj_x86.a
LTKCPP_INCL = -I$(LTKCPP_DIR)
CPPFLAGS = -g $(LTKCPP_INCL)
example1 : example1.cpp
LIBXML2_LIB = $(LTKC_DIR)/libxml2$(SUFFIX).a
example1 : example1.c
version 5.12.0 77
LTK Programmers Guide
2. On the Browse tab, browse to.\libltknet\Debug (or Release for a release ver-
sion), and then click OK to add the libraries to your project.
version 5.12.0 78
LTK Programmers Guide
LTKJava – To add LTKJava to your applications, add the JAR to the classpath, and then add
the import statements required to use the library. A sample import set is shown in Figure 6.6.
LTKJAVA
import org.llrp.ltk.exceptions.*;
import org.llrp.ltk.generated.enumerations.*;
import org.llrp.ltk.generated.interfaces.*;
import org.llrp.ltk.generated.messages.*;
import org.llrp.ltk.generated.parameters.*;
import org.llrp.ltk.types.*;
import org.llrp.ltk.net.LLRPConnection;
import org.llrp.ltk.net.LLRPConnectionAttemptFailedException;
import org.llrp.ltk.net.LLRPConnector;
import org.llrp.ltk.net.LLRPEndpoint;
import org.llrp.ltk.util.Util;
version 5.12.0 79
LTK Programmers Guide
CTypeRegistry * pTypeRegistry;
/*
* Allocate the type registry. This is needed
* by the connection to decode.
*/
pTypeRegistry = getTheTypeRegistry();
if(NULL == pTypeRegistry)
{
/*
version 5.12.0 80
LTK Programmers Guide
#region Initializing
{
Console.WriteLine(“Initializing\n”);
//Create an instance of LLRP reader client.
reader = new LLRPClient();
//Impinj Best Practice! Always install the Impinj extensions
Impinj_Installer.Install();
}
#endregion
6.3.1 LTKCPP
LTKCPP requires that you create connection objects. The connection object needs a buffer to
serialize outgoing messages and to de-serialize incoming messages. Select this buffer based on your
application’s use of LLRP. Choose the maximum buffer size based on the size of the reports you
expect to receive from the Reader. If you are reporting data at the end of very long specs, and
with large tag populations, choose your buffer size carefully. However, if you are working with
version 5.12.0 81
LTK Programmers Guide
immediate reporting (or reports of only a few hundred tags), a 32K buffer size is sufficient. Figure
6.10 shows how to create a connection object.
LTKCPP
/*
* Construct a connection (LLRP::CConnection).
* Using a 32kb max frame size for send/recv.
* The connection object is ready for business
* but not actually connected to the reader yet.
*/
pConn = new CConnection(pTypeRegistry, 32u*1024u);
if(NULL == pConn)
{
}
/*
* Open the connection to the reader
*/
if(m_Verbose)
{
}
rc = pConn->openConnectionToReader(pReaderHostName);
if(0 != rc)
{
version 5.12.0 82
LTK Programmers Guide
}
/*
* Record the pointer to the connection object so other
* routines can use it.
*/
m_pConnectionToReader = pConn;
if(m_Verbose)
{
CMessage * pMessage;
CREADER_EVENT_NOTIFICATION *pNtf;
CReaderEventNotificationData *pNtfData;
CConnectionAttemptEvent * pEvent;
/*
* Expect the notification within 10 seconds.
* It is suppose to be the very first message sent.
*/
pMessage = recvMessage(10000);
/*
* recvMessage() returns NULL if something went wrong.
*/
if(NULL == pMessage)
{
version 5.12.0 83
LTK Programmers Guide
}
/*
* Check to make sure the message is of the right type.
* The type label (pointer) in the message should be
* the type descriptor for READER_EVENT_NOTIFICATION.
*/
if(&CREADER_EVENT_NOTIFICATION::s_typeDescriptor != pMessage-
>m_pType)
{
goto fail;
}
/*
* Now that we are sure it is a READER_EVENT_NOTIFICATION,
* traverse to the ReaderEventNotificationData parameter.
*/
pNtf = (CREADER_EVENT_NOTIFICATION *) pMessage;
pNtfData = pNtf->getReaderEventNotificationData();
if(NULL == pNtfData)
{
goto fail;
}
/*
* The ConnectionAttemptEvent parameter must be present.
*/
pEvent = pNtfData->getConnectionAttemptEvent();
if(NULL == pEvent)
{
version 5.12.0 84
LTK Programmers Guide
goto fail;
}
/*
* The status in the ConnectionAttemptEvent parameter
* must indicate connection success.
*/
if(ConnectionAttemptStatusType_Success != pEvent->getStatus())
{
goto fail;
}
/*
* Done with the message
*/
delete pMessage;
/*
* Victory.
*/
return 0;
fail:
/*
* Something went wrong. Tattle. Clean up. Return error.
*/
printf(“ERROR: checkConnectionStatus failed\n”);
delete pMessage;
return -1;
version 5.12.0 85
LTK Programmers Guide
6.3.2 LTKNET
LTKNET hides the packet encoding/decoding registry from the API. LTKNET also automat-
ically awaits the ConnectionStatusEvent event and reports back with the status through the
reader.Open() command. It is important to check the status as well as the command return-code.
LTKNET
#region Connecting
{
Console.WriteLine(“Connecting To Reader\n”);
ENUM_ConnectionAttemptStatusType status;
//Open the reader connection. Timeout after 5 seconds
bool ret = reader.Open(readerName, 5000, out status);
//Ensure that the open succeeded and the reader
// returned the correct connection status result
if (!ret || status != ENUM_ConnectionAttemptStatusType.Success)
{
Console.WriteLine(“Failed to Connect to Reader \n”);
return;
}
}
#endregion
6.3.3 LTKJAVA
LTKJava awaits the ConnectionStatusEvent indicator and throws an exception if the connection
fails, as shown in Figure 6.13.
LTKJAVA
version 5.12.0 86
LTK Programmers Guide
6.4.1 LTKCPP
version 5.12.0 87
LTK Programmers Guide
CIMPINJ_ENABLE_EXTENSIONS * pCmd;
CMessage * pRspMsg;
CIMPINJ_ENABLE_EXTENSIONS_RESPONSE *pRsp;
/*
* Compose the command message
*/
pCmd = new CIMPINJ_ENABLE_EXTENSIONS();
pCmd->setMessageID(1);
/*
* Send the message, expect a certain type of response
*/
pRspMsg = transact(pCmd);
/*
* Done with the command message
*/
delete pCmd;
/*
* transact() returns NULL if something went wrong.
*/
if(NULL == pRspMsg)
{
/* transact already tattled */
return -1;
}
/*
* Cast to a CIMPINJ_ENABLE_EXTENSIONS_RESPONSE message.
*/
pRsp = (CIMPINJ_ENABLE_EXTENSIONS_RESPONSE *)
pRspMsg;
/*
* Check the LLRPStatus parameter.
*/
if (0 != checkLLRPStatus(pRsp->getLLRPStatus(),
“enableImpinjExtensions”))
{
version 5.12.0 88
LTK Programmers Guide
6.4.2 LTKNET
#region EnableExtensions
{
version 5.12.0 89
LTK Programmers Guide
6.4.3 LTKJAVA
version 5.12.0 90
LTK Programmers Guide
LTKJAVA
LLRPMessage response;
try {
logger.info(“IMPINJ_ENABLE_EXTENSIONS …”);
IMPINJ_ENABLE_EXTENSIONS ena =
new IMPINJ_ENABLE_EXTENSIONS();
ena.setMessageID(getUniqueMessageID());
response = connection.transact(ena, 10000);
StatusCode status =
((IMPINJ_ENABLE_EXTENSIONS_RESPONSE)response)
| .getLLRPStatus().getStatusCode();
if (status.equals(new StatusCode(“M_Success”))) {
logger.info(
“IMPINJ_ENABLE_EXTENSIONS was suc-
cessful”);
}
else {
logger.info(response.toXMLString());
logger.info(
“IMPINJ_ENABLE_EXTENSIONS Failure”);**
System.exit(1);
}
} catch (InvalidLLRPMessageException ex) {
logger.error(
“Could not process IMPINJ_ENABLE_EXTENSIONS
response”);
System.exit(1);
} catch (TimeoutException ex) {
logger.error(
“Timeout Waiting for IMPINJ_ENABLE_EXTENSIONS
response”);
System.exit(1);
version 5.12.0 91
LTK Programmers Guide
6.5.1 LTKC
char aBuf[100*1024];
/*
* Convert the message to an XML string.
* This fills the buffer with either the XML string
* or an error message. The return value could
* be checked.
*/
LLRP_toXMLString(&pMessage->elementHdr, aBuf, sizeof
aBuf);
/* Print the LTK-XML Text to the standard output. */
printf(“%s”, aBuf);
version 5.12.0 92
LTK Programmers Guide
6.5.2 LTKCPP
In LTKCPP, the built-in member function for all messages and parameters, toXMLString, prints
the LLRP or Impinj object in LTK-XML format.
LTKCPP
char aBuf[100*1024];
/*
* Convert the message to an XML string.
* This fills the buffer with either the XML string
* or an error message. The return value could
* be checked.
*/
pMessage->toXMLString(aBuf, sizeof aBuf);
/* Print the LTK-XML Text to the standard output. */
printf(“%s”, aBuf);
6.5.3 LTKNET
In LTKNET, the ToString() method, available for all LLRP messages and parameters, serializes
the object as LTK-XML.
LTKNET
version 5.12.0 93
LTK Programmers Guide
Console.WriteLine(msg_err.ToString());
reader.Close();
return;
6.5.4 LTKJava
In LTKJAVA, the toXMLString() method, available for all LLRP messages and parameters,
serializes the object as LTK-XML. In addition, LTKJAVA has a log4j mechanism that can enable
detailed logs. When trying to understand your LTKJAVA program, it is helpful to set the log level
for the LTK.
LTKJAVA
Logger.getRootLogger().setLevel(Level.DEBUG);
if (status.equals(new StatusCode(“M_Success”))) {
}
else {
logger.info(response.toXMLString());
logger.info(“IMPINJ_ENABLE_EXTENSIONS Failure”);
System.exit(1);
version 5.12.0 94
LTK Programmers Guide
Sections 6.2, 6.3, and 6.4 in this document provide details for Steps 1, 2, and 3.
Steps 3-7 involve sending LLRP messages to the Reader. In terms of LTK-XML, the following
message is exchanged between the application and the Reader during a successful transaction.
LTK-XML
<llrp:LLRPStatus>
<StatusCode>M_Success</StatusCode>
<ErrorDescription></ErrorDescription>
</llrp:LLRPStatus>
version 5.12.0 95
LTK Programmers Guide
</Impinj:IMPINJ_ENABLE_EXTENSIONS_RESPONSE>
<!— Use Reader Factory Default settings –>
<SET_READER_CONFIG MessageID=“2”>
<ResetToFactoryDefault>true</ResetToFactoryDefault>
</SET_READER_CONFIG>
<!—Readers expected response –>
<SET_READER_CONFIG_RESPONSE MessageID=“2”>
<LLRPStatus>
<StatusCode>M_Success</StatusCode>
<ErrorDescription></ErrorDescription>
</LLRPStatus>
</SET_READER_CONFIG_RESPONSE>
<!– Add a basic Gen2 ROSpec –>
<ADD_ROSPEC MessageID=“3”>
</ADD_ROSPEC>
<!—Readers expected response –>
<ADD_ROSPEC_RESPONSE MessageID=“3”>
<LLRPStatus>
<StatusCode>M_Success</StatusCode>
<ErrorDescription></ErrorDescription>
</LLRPStatus>
</ADD_ROSPEC_RESPONSE>
<!—Enable the ROSpec –>
<ENABLE_ROSPEC MessageID=“4”>
version 5.12.0 96
LTK Programmers Guide
</ENABLE_ROSPEC>
<!—Readers expected response –>
<ENABLE_ROSPEC_RESPONSE MessageID=“4”>
<LLRPStatus>
<StatusCode>M_Success</StatusCode>
<ErrorDescription></ErrorDescription>
</LLRPStatus>
</ENABLE_ROSPEC_RESPONSE>
<!—Start the ROSpec –>
<START_ROSPEC MessageID=“5”>
</START_ROSPEC>
<!—Readers expected response –>
<START_ROSPEC_RESPONSE MessageID=“5”>
<LLRPStatus>
<StatusCode>M_Success</StatusCode>
<ErrorDescription></ErrorDescription>
</LLRPStatus>
</START_ROSPEC_RESPONSE>
7.2 Setup
Section 4.4 in this document provides detailed setup information. It is assumed that the program-
mer has installed:
• Tools, and has compiled the application with support for the LTK, as described in Section
6.1.
• Library is initialized as described in Section 6.2, and the Reader connection is established.
• Impinj Octane extensions are enabled, as described in Section 6.4.
version 5.12.0 97
LTK Programmers Guide
7.3.1 LTKCPP
CSET_READER_CONFIG * pCmd;
CMessage * pRspMsg;
CSET_READER_CONFIG_RESPONSE *pRsp;
/*
* Compose the command message
*/
pCmd = new CSET_READER_CONFIG();
pCmd->setMessageID(2);
pCmd->setResetToFactoryDefault(1);
/*
* Send the message, expect a certain type of response
*/
pRspMsg = transact(pCmd);
/*
* Done with the command message
*/
delete pCmd;
version 5.12.0 98
LTK Programmers Guide
/*
* transact() returns NULL if something went wrong.
*/
if(NULL == pRspMsg)
{
/* transact already tattled */
return -1;
}
/*
* Cast to a SET_READER_CONFIG_RESPONSE message.
*/
pRsp = (CSET_READER_CONFIG_RESPONSE *) pRspMsg;
/*
* Check the LLRPStatus parameter.
*/
if(0 != checkLLRPStatus(pRsp->getLLRPStatus(), “resetConfigu-
rationToFactoryDefaults”))
{
/* checkLLRPStatus already tattled */
delete pRspMsg;
return -1;
}
/*
* Done with the response message.
*/
delete pRspMsg;
/*
* Victory.
*/
return 0;
version 5.12.0 99
LTK Programmers Guide
7.3.2 LTKNET
#region FactoryDefault
{
}
else
{
Console.WriteLine(“SET_READER_CONFIG Command
Timed out\n”);
reader.Close();
return;
}
}
#endregion
7.3.3 LTKJAVA
LLRPMessage response;
try {
// factory default the reader
logger.info(“SET_READER_CONFIG with factory de-
fault …”);
SET_READER_CONFIG set = new SET_READER_CONFIG();
set.setMessageID(getUniqueMessageID());
set.setResetToFactoryDefault(new Bit(true));
response = connection.transact(set, 10000);
// check whether ROSpec addition was successful
StatusCode status =
((SET_READER_CONFIG_RESPONSE)response).
getLLRPStatus().getStatusCode();
if (status.equals(new StatusCode(“M_Success”))) {
and when reporting the ROSpecID in the TagReportData parameters. The application assigns
a number that is only used for identification purposes. We recommend using a number significant
to your application. LTK and LLRP allow any 32-bit number (except 0) for these IDs.
The LTK-XML representation of this simple ROSpec is shown below:
LTK-XML
<?xml version=“1.0”?>
<ADD_ROSPEC
xmlns=“http://www.llrp.org/ltk/schema/core/encoding/xml/1.0”
xmlns:llrp=“http://www.llrp.org/ltk/schema/core/encoding/xml/
1.0”
xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”
xmlns:Impinj=“http://developer.impinj.com/ltk/schema/encoding/
xml/1.6”
xsi:schemaLocation=”http://www.llrp.org/ltk/schema/core/
encoding/xml/1.0
http://www.llrp.org/ltk/schema/core/encoding/xml/1.0/llrp.xsd
http://developer.impinj.com/ltk/schema/encoding/xml/1.6
http://developer.impinj.com/ltk/schema/encoding/xml/1.6/impinj.
xsd”
MessageID=“0”>
<ROSpec>
<ROSpecID>1111</ROSpecID>
<Priority>0</Priority>
<CurrentState>Disabled</CurrentState>
<ROBoundarySpec>
<ROSpecStartTrigger>
<ROSpecStartTriggerType>Null</ROSpecStartTriggerType>
</ROSpecStartTrigger>
<ROSpecStopTrigger>
<ROSpecStopTriggerType>Null</ROSpecStopTriggerType>
<DurationTriggerValue>0</DurationTriggerValue>
</ROSpecStopTrigger>
</ROBoundarySpec>
<AISpec>
<AntennaIDs>0</AntennaIDs>
<AISpecStopTrigger>
<AISpecStopTriggerType>Null</AISpecStopTriggerType>
<DurationTrigger>0</DurationTrigger>
</AISpecStopTrigger>
<InventoryParameterSpec>
<InventoryParameterSpecID>1234</InventoryParameterSpecID>
<ProtocolID>EPCGlobalClass1Gen2</ProtocolID>
</InventoryParameterSpec>
</AISpec>
</ROSpec>
</ADD_ROSPEC>
7.4.1 LTKCPP
In LTKCPP, each of the parameters within the ROSpec needs to be created individually. The
individual fields within the parameter also need to be explicitly set. After this has been completed,
the parameter is inserted into its parent parameter or message. As an example. shown in Figure
7.6, the CROSpecStartTrigger and CROSpecStopTrigger parameters are created and then
inserted into the newly created CROBoundarySpec. Repeat this procedure for each of the
parameters within the ROSpec.
Of particular note here is the use of “lists” or vectors of elements. The AISpec provides a list of
active antennas. In this case, we are setting the antenna set to ‘0’, or all antennas. To do so, we
need to create the llrp_u16v_t(1) list and assign its first element to the value ‘0’.
Notice that, although each object is constructed individually, all of the objects are freed by a single
delete pCmd; statement. The delete operator automatically frees all message sub-components
that were allocated in the previous new operations.
The ability to construct ROSpecs (or other messages or parameters) from XML is not available in
the LTKCPP.
LTKCPP
CROSpecStartTrigger * pROSpecStartTrigger =
new CROSpecStartTrigger();
pROSpecStartTrigger->setROSpecStartTriggerType(
ROSpecStartTriggerType_Null);
AISpecStopTriggerType_Null);
pAISpecStopTrigger->setDurationTrigger(0);
CInventoryParameterSpec * pInventoryParameterSpec =
new CInventoryParameterSpec();
pInventoryParameterSpec->setInventoryParameterSpecID(1234);
pInventoryParameterSpec->setProtocolID(AirProtocols_EPCGlobalClass1Gen2);
llrp_u16v_t AntennaIDs = llrp_u16v_t(1);
AntennaIDs.m_pValue[0] = 0; /* All */
CAISpec * pAISpec = new CAISpec();
pAISpec->setAntennaIDs(AntennaIDs);
pAISpec->setAISpecStopTrigger(pAISpecStopTrigger);
pAISpec->addInventoryParameterSpec(pInventoryParameterSpec);
CROSpec * pROSpec = new CROSpec();
pROSpec->setROSpecID(1111);
pROSpec->setPriority(0);
pROSpec->setCurrentState(ROSpecState_Disabled);
pROSpec->setROBoundarySpec(pROBoundarySpec);
pROSpec->addSpecParameter(pAISpec);
CADD_ROSPEC * pCmd;
CMessage * pRspMsg;
CADD_ROSPEC_RESPONSE * pRsp;
/*
* Compose the command message.
* N.B.: After the message is composed, all the parameters
* constructed, immediately above, are considered “owned”
* by the command message. When it is destructed so
* too will the parameters be.
*/
pCmd = new CADD_ROSPEC();
pCmd->setMessageID(3);
pCmd->setROSpec(pROSpec);
/*
* Send the message, expect a certain type of response
*/
pRspMsg = transact(pCmd);
/*
* Done with the command message.
* N.B.: And the parameters
*/
delete pCmd;
/*
* transact() returns NULL if something went wrong.
*/
if(NULL == pRspMsg)
{
}
/*
* Cast to a ADD_ROSPEC_RESPONSE message.
*/
pRsp = (CADD_ROSPEC_RESPONSE *) pRspMsg;
/*
* Check the LLRPStatus parameter.
*/
if(0 != checkLLRPStatus(pRsp->getLLRPStatus(), “addROSpec”))
{
}
/*
* Done with the response message.
*/
delete pRspMsg;
/*
* Victory.
*/
return 0;
7.4.2 LTKNET
There are two ways to build ROSpecs and other LLRP messages and parameters in LTKNET:
For simple messages, such as those as described in Section 7.3, it is easier and faster to con-
struct the LLRP objects programmatically. However, for large or complex messages such as
ADD_ROSPEC, ADD_ACCESSSPEC, and SET_READER_CONFIG, there are times where
XML-based construction is better.
Generally, for use cases where the configuration is relatively static, but where you want to “tune”
the Reader performance in the field, reading ADD_ROSPEC and ADD_ACCESSSPEC messages
from a file offers the greatest flexibility. However, if the user of the application requires fine-grained
control over specific LLRP options via a GUI or other mechanism, it might be easier using the
programmatic approach.
The object-based construction is shown in Figure 7.7. For an example of the LTK-XML based
construction, see Section 8.5.1.
LTKNET
#region ADDRoSpecWithObjects
{
Console.WriteLine(“Adding RoSpec\n”);
// set up the basic parameters in the ROSpec. Use all the defaults from the
reader
MSG_ADD_ROSPEC msg = new MSG_ADD_ROSPEC();
MSG_ERROR_MESSAGE msg_err;
msg.ROSpec = new PARAM_ROSpec();
msg.ROSpec.CurrentState = ENUM_ROSpecState.Disabled;
msg.ROSpec.Priority = 0x00;
msg.ROSpec.ROSpecID = 1111;
// setup the start and stop triggers in the Boundary Spec
msg.ROSpec.ROBoundarySpec = new PARAM_ROBoundarySpec();
msg.ROSpec.ROBoundarySpec.ROSpecStartTrigger = new
PARAM_ROSpecStartTrigger();
msg.ROSpec.ROBoundarySpec.ROSpecStartTrigger.ROSpecStartTriggerType
= ENUM_ROSpecStartTriggerType.Null;
msg.ROSpec.ROBoundarySpec.ROSpecStopTrigger = new
PARAM_ROSpecStopTrigger();
msg.ROSpec.ROBoundarySpec.ROSpecStopTrigger.ROSpecStopTriggerType
= ENUM_ROSpecStopTriggerType.Null;
msg.ROSpec.ROBoundarySpec.ROSpecStopTrigger.DurationTriggerValue
= 0;
// ignored by reader
// Add a single Antenna Inventory to the ROSpec
msg.ROSpec.SpecParameter = new UNION_SpecParameter();
PARAM_AISpec aiSpec = new PARAM_AISpec();
aiSpec.AntennaIDs = new UInt16Array();
aiSpec.AntennaIDs.Add(0); // all antennas
aiSpec.AISpecStopTrigger = new PARAM_AISpecStopTrigger();
aiSpec.AISpecStopTrigger.AISpecStopTriggerType =
ENUM_AISpecStopTriggerType.Null;
// use all the defaults from the reader. Just specify the minimum required
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);
MSG_ADD_ROSPEC_RESPONSE rsp =
reader.ADD_ROSPEC(msg, out msg_err, 12000);
if (rsp != null)
{
if (rsp.LLRPStatus.StatusCode != ENUM_StatusCode.M_Success)
{
Console.WriteLine(rsp.LLRPStatus.StatusCode.ToString());
reader.Close();
return;
}
}
else if (msg_err != null)
{
Console.WriteLine(msg_err.ToString());
reader.Close();
return;
}
else
{
Console.WriteLine(
“ADD_ROSPEC Command Timed out\n”);
reader.Close();
return;
}
}
#endregion
7.4.3 LTKJava
In LTKJAVA, there are two ways to build ROSpecs (as well as other LLRP messages and param-
eters):
For simple messages, such as those described in Section 7.3, it is easier and faster to construct the
LLRP objects programmatically. However, for large or complex messages such as ADD_ROSPEC,
ADD_ACCESSSPEC, and SET_READER_CONFIG, there are times where XML-based con-
struction is a better choice.
Generally, for use cases where the configuration is relatively static, but where you want to “tune”
the performance of the Reader in the field, reading ADD_ROSPEC and ADD_ACCESSSPEC
messages from a file offers the most flexibility. If the user of the application requires fine-grained
control over specific LLRP options via a GUI or other mechanism, the programmatic approach
might be easier to use.
The object-based construction is shown in Figure 7.8. For an example of the LTK-XML based
construction, see Section 8.5.1.
LTKJAVA
CENABLE_ROSPEC * pCmd;
CMessage * pRspMsg;
CENABLE_ROSPEC_RESPONSE * pRsp;
/*
* Compose the command message
*/
pCmd = new CENABLE_ROSPEC();
pCmd->setMessageID(4);
pCmd->setROSpecID(1111);
/*
* Send the message, expect a certain type of response
*/
pRspMsg = transact(pCmd);
/*
* Done with the command message
*/
delete pCmd;
/*
* transact() returns NULL if something went wrong.
*/
if(NULL == pRspMsg)
{
/* transact already tattled */
return -1;
}
/*
* Cast to a ENABLE_ROSPEC_RESPONSE message.
*/
pRsp = (CENABLE_ROSPEC_RESPONSE *) pRspMsg;
/*
* Check the LLRPStatus parameter.
*/
if(0 != checkLLRPStatus(pRsp->getLLRPStatus(), “enableROSpec”))
{
/* checkLLRPStatus already tattled */
delete pRspMsg;
return -1;
}
/*
* Done with the response message.
*/
delete pRspMsg;
/*
* Victory.
*/
return 0;
#region EnableRoSpec
{
Console.WriteLine(“Enabling RoSpec\n”);
MSG_ENABLE_ROSPEC msg = new MSG_ENABLE_ROSPEC();
MSG_ERROR_MESSAGE msg_err;
msg.ROSpecID = 1111; // this better match the ROSpec we created above
MSG_ENABLE_ROSPEC_RESPONSE rsp =
reader.ENABLE_ROSPEC(msg, out msg_err, 12000);
if (rsp != null)
{
if (rsp.LLRPStatus.StatusCode != ENUM_StatusCode.M_Success)
{
Console.WriteLine(rsp.LLRPStatus.StatusCode.ToString());
reader.Close();
return;
}
}
else if (msg_err != null)
{
Console.WriteLine(msg_err.ToString());
reader.Close();
return;
}
else
{
Console.WriteLine(“ENABLE_ROSPEC Command
Timed out\n”);
reader.Close();
return;
}
}
#endregion
LLRPMessage response;
try {
// factory default the reader
logger.info(“ENABLE_ROSPEC …”);
ENABLE_ROSPEC ena = new ENABLE_ROSPEC();
ena.setMessageID(getUniqueMessageID());
ena.setROSpecID(rospec.getROSpecID());
response = connection.transact(ena, 10000);
// check whether ROSpec addition was successful
StatusCode status =
((ENABLE_ROSPEC_RESPONSE)response).getLLRPStatus().
| getStatusCode();
if (status.equals(new StatusCode(“M_Success”))) {
logger.info(“ENABLE_ROSPEC was successful”);
}
else {
logger.error(response.toXMLString());
logger.info(“ENABLE_ROSPEC_RESPONSE
failed”);
System.exit(1);
}
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
CSTART_ROSPEC * pCmd;
CMessage * pRspMsg;
CSTART_ROSPEC_RESPONSE * pRsp;
/*
* Compose the command message
*/
pCmd = new CSTART_ROSPEC();
pCmd->setMessageID(5);
pCmd->setROSpecID(1111);
/*
* Send the message, expect a certain type of response
*/
pRspMsg = transact(pCmd);
/*
* Done with the command message
*/
delete pCmd;
/*
* transact() returns NULL if something went wrong.
*/
if(NULL == pRspMsg)
{
/* transact already tattled */
return -1;
}
/*
* Cast to a START_ROSPEC_RESPONSE message.
*/
pRsp = (CSTART_ROSPEC_RESPONSE *) pRspMsg;
/*
* Check the LLRPStatus parameter.
*/
if(0 != checkLLRPStatus(pRsp->getLLRPStatus(), “startRO-
Spec”))
{
/* checkLLRPStatus already tattled */
delete pRspMsg;
return -1;
}
/*
* Done with the response message.
*/
delete pRspMsg;
/*
* Victory.
*/
return 0;
#region StartRoSpec
{
Console.WriteLine(“Starting RoSpec\n”);
MSG_START_ROSPEC msg = new MSG_START_ROSPEC();
MSG_ERROR_MESSAGE msg_err;
msg.ROSpecID = 1111; // this better match the RoSpec we created above
MSG_START_ROSPEC_RESPONSE rsp =
reader.START_ROSPEC(msg, out msg_err, 12000);
if (rsp != null)
{
if (rsp.LLRPStatus.StatusCode != ENUM_StatusCode.M_Success)
{
Console.WriteLine(rsp.LLRPStatus.StatusCode.ToString());
reader.Close();
return;
}
}
else if (msg_err != null)
{
Console.WriteLine(msg_err.ToString());
reader.Close();
return;
}
else
{
}
#endregion
LLRPMessage response;
try {
logger.info(“START_ROSPEC …”);
START_ROSPEC start = new START_ROSPEC();
start.setMessageID(getUniqueMessageID());
start.setROSpecID(rospec.getROSpecID());
response = connection.transact(start, 10000);
// check whether ROSpec addition was successful
StatusCode status =
((START_ROSPEC_RESPONSE)response).getLLRPStatus().
getStatusCode();
if (status.equals(new StatusCode(“M_Success”))) {
logger.info(“START_ROSPEC was successful”);
}
else {
logger.error(response.toXMLString());
logger.info(“START_ROSPEC_RESPONSE failed”);
System.exit(1);
}
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
7.7.1 LTKCPP
In LTKCPP, a recvMessage(int timeout) method allows the caller to block received messages,
with a timeout specified in milliseconds. Upon exiting from this call, the application needs to
determine whether a message was received. If a message was received, the application examines
the message type and dispatches the message accordingly. Dispatching the message requires that
the application tests the message type. Each type of message or parameter contains a static
type descriptor that can be compared against the descriptor within an instance of a message or
parameter, as described in Section 5.1.5. The object can then be cast back to that type for handling.
The example in Figure 7.15 is shown in LTKCPP:
LTKCPP
startTime = time(NULL);
pMessage = recvMessage(1000);
/* validate the timestamp */
tempTime = time(NULL);
if(difftime(tempTime, startTime) > 1000)
{
bDone=1;
}
if(NULL == pMessage)
{
continue;
}
/*
* What happens depends on what kind of message
* received. Use the type label (m_pType) to
* discriminate message types.
*/
pType = pMessage->m_pType;
/*
* Is it a tag report? If so, print it out.
*/
if(&CRO_ACCESS_REPORT::s_typeDescriptor == pType)
{
CRO_ACCESS_REPORT * pNtf;
pNtf = (CRO_ACCESS_REPORT *) pMessage;
printTagReportData(pNtf);
}
/*
* Is it a reader event? This example only recognizes
* AntennaEvents.
*/
else if(&CREADER_EVENT_NOTIFICATION::s_typeDescriptor ==
pType)
{
CREADER_EVENT_NOTIFICATION *pNtf;
CReaderEventNotificationData *pNtfData;
pNtf = (CREADER_EVENT_NOTIFICATION *) pMessage;
pNtfData = pNtf->getReaderEventNotificationData();
if(NULL != pNtfData)
{
handleReaderEventNotification(pNtfData);
}
else
{
printf(“WARNING: READER_EVENT_NOTIFICATION
without data\n”);
}
Cur = pRO_ACCESS_REPORT->beginTagReportData();
Cur != pRO_ACCESS_REPORT->endTagReportData();
Cur++)
printOneTagReportData(*Cur);
7.7.2 LTKNET
In LTKNET, the LLRPClient object contains three event delegates to handle the three different
types of messages initiated by the Reader:
• RO_ACCESS_REPORT – These are the tag reports from both inventory and access oper-
ations. Reports are delivered through the delegate delegateRoAccessReport.
#region EventHandlers
{
}
#endregion
Figure 7.17 LTKNET –Adding Event Handlers for Asynchronous Reader Messages
The user-defined methods call for each message. In our simple example, we extract the EPC and
print the hex value to the console output. Reports could be empty, so the application checks for
empty reports, as shown in Figure 7.18. Note that, in general, more than one tag report can come
in each message, even with the default set to immediate reporting.
LLRP has two different tag representations, a short format specific to 96-bit EPCs, and a general
format suitable for all lengths of EPCs. We recommend that your application checks for both
types of formats, and treats them appropriately as shown below.
LTKNET
// Simple Handler for receiving the tag reports from the reader
static void reader_OnRoAccessReportReceived(MSG_RO_ACCESS_REPORT
msg)
{
7.7.3 LTKJava
In LTKJava, your application class inherits from LLRPEndpoint. LLRPEnpoint calls mes-
sageReceived whenever a message is received over the LLRP connection. Your class can override
this implementation to handle received messages.
This function is responsible for dispatching the various types of messages (notifications, reports
etc) that arrive on the connection. Thus, at the start of this function, check the message type. For
RO_ACCESS_REPORT messages, we retrieve the tag list. The tag list is a standard Java list
that can be iterated with standard methods shown below.
The tag report information can be extracted by examining the fields within the report.
Note: Most tag report data is optional, so it could be Null. In the example below, we simply
fetch the data and format a string containing the tag information that is available.
LLRP has two different tag representations, a short format specific to 96-bit EPCs, and a general
format suitable for all lengths of EPCs. We recommend that your application checks for both
types of formats and treats them appropriately as shown below.
LTKJAVA
// As an example here, we’ll just get the stuff out of here and
// for a super long string
LLRPParameter epcp = (LLRPParameter) tr.getEPCParameter();
// epc is not optional, so we should fail if we can’t find it
String epcString = “unknownEPC”;
if(epcp != null) {
if( epcp.getName().equals(“EPC_96”)) {
EPC_96 epc96 = (EPC_96) epcp;
epcString = epc96.getEPC().toString();
} else if ( epcp.getName().equals(“EPCData”)) {
EPCData epcData = (EPCData) epcp;
epcString = epcData.getEPC().toString();
}
} else {
logger.error(“Could not find EPC in Tag Report”);
System.exit(1);
}
// all of these values are optional, so check their non-nullness first
String antenna = “unknownAntenna”;
if(tr.getAntennaID() != null) {
antenna = tr.getAntennaID().getAntennaID().toString();
}
String index = “unknownChannelIndex”;
if(tr.getChannelIndex() != null) {
index = tr.getChannelIndex().getChannelIndex().toString();
}
String ftime = “unknownFirstTimestamp”;
if( tr.getFirstSeenTimestampUTC() != null) {
ftime = tr.getFirstSeenTimestampUTC().getMicroseconds().toString();
}
String paramId = “unknownParamSpecID”;
if(tr.getInventoryParameterSpecID() != null) {
paramId =
tr.getInventoryParameterSpecID().getInventoryParameterSpecID().toString()
}
String ltime = “unknownLastTimestamp”;
if(tr.getLastSeenTimestampUTC() != null) {
ltime = tr.getLastSeenTimestampUTC().getMicroseconds().toString();
}
String rssi = “unknownRssi”;
if(tr.getPeakRSSI() != null) {
rssi = tr.getPeakRSSI().getPeakRSSI().toString();
}
String roid = “unknownRospecID”;
if(tr.getROSpecID() != null) {
roid = tr.getROSpecID().getROSpecID().toString();
}
String tagseen = “unknownTagSeenCount”;
if(tr.getTagSeenCount() != null) {
tagseen = tr.getTagSeenCount().getTagCount().toString();
}
String output = ” EPC: ” + epcString +
“,Antenna:” + antenna +
“,ChanIndex:” + index +
“,SeenCnt:” + tagseen +
“,FirstSeen:” + ftime +
“,LastSeen:” + ltime +
“,RSSI:” + rssi +
“,ROSpecID:” + roid +
“,ParamID:” + paramId;
System.out.println(output);
8.1 Analysis
The default value for the Speedway of 30 dBm transmits power that typically is dependent on
the Regulatory region. Some versions of Speedway are capable of transmitting over 30 dBm to
compensate for high losses in the cabling and antennas. Thus, the software in this example must
set the max power based on the unit type. There are a few ways to accomplish this. You could
search the data sheet for Readers in FCC and ETSI, and hard-code the LLRP power index by
using a lookup table that is based on region and model number. Alternately, you could query
the Reader for its transmit power capabilities. In this example we choose the latter. For the first
approach, see Section 1.6.2 for a description of the document that contains this information.
The LLRP RFTransmitter parameter sets the transmit power. However, it also contains the
hopTableID and ChannelIndex, which cannot be set independently. Instead, the application must
query these three items for use in building the RFTransmitter parameter. The application must
then send this parameter via the LLRP SET_READER_CONFIG message.
To conserve AC power and reduce interference, the application should configure the ImpinjLowDu-
tyCycle as part of the SET_READER_CONFIG message. To use Impinj Autoset, the application
must set the ModeID in the C1G2RfControl parameter.
For a detailed description of these Octane LLRP extensions, see Section 1.6.2.
Note: Because the LTKCPP example uses LTK-XML, this example is not available on Windows.
We have already seen steps 1-4, 9 and 10 in this document, so the example starts with step 5. For
the complete sample code, see the docSample2 directories in the Impinj LTK distribution.
For LTKCPP, we will provide the object based parameter and message construction. For LTKNET
we will show the LTK-XML file-based construction when appropriate.
#region getReaderCapabilities
{
}
// Get the reader model number since some features are not
// available on Speedway.
PARAM_GeneralDeviceCapabilities dev_cap = msg_rsp.GeneralDeviceCapabilities;
// Check to make sure the model number mathces and that this
device
// is an impinj reader (deviceManufacturerName == 25882)
if ((dev_cap != null) &&
(dev_cap.DeviceManufacturerName == 25882))
{
modelNumber = dev_cap.ModelName;
}
else
{
Console.WriteLine(
“Could not determine reader model number\n”);
reader.Close();
return;
}
// get the uhf band capabilities. Inside this is the power table.
// take the last element of the power table to get the highest power.
PARAM_UHFBandCapabilities uhf =
msg_rsp.RegulatoryCapabilities.UHFBandCapabilities;
PARAM_TransmitPowerLevelTableEntry entry =
uhf.TransmitPowerLevelTableEntry[
uhf.TransmitPowerLevelTableEntry.Length - 1];
txPwrIndx = entry.Index;
double power = entry.TransmitPowerValue / 100;
Console.WriteLine(” Max Power ” + power.ToString() + ” dbm.”);
}
#endregion
CGET_READER_CAPABILITIES *pCmd;
CMessage * pRspMsg;
CGET_READER_CAPABILITIES_RESPONSE *pRsp;
CRegulatoryCapabilities *pReg;
CUHFBandCapabilities *pUhf;
CTransmitPowerLevelTableEntry *pPwrLvl;
CGeneralDeviceCapabilities *pDeviceCap;
std::list<CTransmitPowerLevelTableEntry *>::iterator PwrLvl;
/*
* Compose the command message
*/
pCmd = new CGET_READER_CAPABILITIES();
pCmd->setMessageID(m_messageID++);
pCmd->setRequestedData(GetReaderCapabilitiesRequestedData_All);
/*
* Send the message, expect a certain type of response
*/
pRspMsg = transact(pCmd);
/*
* Done with the command message
*/
delete pCmd;
/*
* transact() returns NULL if something went wrong.
*/
if(NULL == pRspMsg)
{
}
/* get the last power level in the table */
PwrLvl = pUhf->endTransmitPowerLevelTableEntry();
PwrLvl–;
// store the index for use int the ROSpec.
pPwrLvl = *PwrLvl;
m_PowerLevelIndex = pPwrLvl->getIndex();
if(1 < m_Verbose)
{
printf(“INFO: Reader Max Power index %u, power %f\n”,
pPwrLvl->getIndex(), pPwrLvl->getTransmitPowerValue()/100);
}
/* if this parameter is missing, or if this is not an Impinj
** reader, we can’t determine its capabilities so we exit
** Impinj Private Enterprise NUmber is 25882 */
if( (NULL == (pDeviceCap = pRsp->getGeneralDeviceCapabilities()))
||
(25882 != pDeviceCap->getDeviceManufacturerName()))
{
delete pRspMsg;
return -1;
}
m_modelNumber = pDeviceCap->getModelName();
if(1 < m_Verbose)
{
printf(“INFO: Reader Model Name %u\n”, m_modelNumber);
}
/*
* Done with the response message.
*/
delete pRspMsg;
/*
* Tattle progress, maybe
*/
if(m_Verbose)
{
LLRPMessage response;
GET_READER_CAPABILITIES_RESPONSE gresp;
GET_READER_CAPABILITIES get = new GET_READER_CAPABILITIES();
GetReaderCapabilitiesRequestedData data =
new GetReaderCapabilitiesRequestedData(
GetReaderCapabilitiesRequestedData.All);
get.setRequestedData(data);
get.setMessageID(getUniqueMessageID());
logger.info(“Sending GET_READER_CAPABILITIES message
…”);
try {
response = connection.transact(get, 10000);
// check whether GET_CAPABILITIES call was successful
gresp = (GET_READER_CAPABILITIES_RESPONSE)response;
StatusCode status = gresp.getLLRPStatus().getStatusCode();
if (status.equals(new StatusCode(“M_Success”))) {
logger.info(“GET_READER_CAPABILITIES was
successful”);
// get the info we need
GeneralDeviceCapabilities dev_cap = gresp.getGeneralDeviceCapabilities();
if ((dev_cap == null) ||
(!dev_cap.getDeviceManufacturerName().equals(new
UnsignedInteger(25882)))) {
logger.error(“DocSample2 must use Impinj
model Reader, not” +
dev_cap.getDeviceManufacturerName().toString());
System.exit(1);
}
modelName = dev_cap.getModelName();
logger.info(“Found Impinj reader model” + model-
Name.toString());
// get the max power level
if( gresp.getRegulatoryCapabilities() != null) {
UHFBandCapabilities band_cap =
gresp.getRegulatoryCapabilities().getUHFBandCapabilities();
List<TransmitPowerLevelTableEntry> pwr_list
=
band_cap.getTransmitPowerLevelTableEntryList();
TransmitPowerLevelTableEntry entry =
pwr_list.get(pwr_list.size() - 1);
maxPowerIndex = entry.getIndex();
maxPower = entry.getTransmitPowerValue();
// LLRP sends power in dBm * 100
double d = ((double) maxPower.intValue())/100;
logger.info(“Max power” + d +
” dBm at index ” + maxPowerIndex.toString());
}
}
else {
logger.info(response.toXMLString());
logger.info(“GET_READER_CAPABILITIES fail-
ures”);
System.exit(1);
}
} catch (InvalidLLRPMessageException ex) {
logger.error(“Could not display response string”);
} catch (TimeoutException ex) {
Console.WriteLine(msg_rsp.LLRPStatus.StatusCode.ToString());
reader.Close();
return;
}
}
else if (msg_err != null)
{
Console.WriteLine(msg_err.ToString());
reader.Close();
return;
}
else
{
Console.WriteLine(“GET reader Config Command Timed
out\n”);
reader.Close();
return;
}
// Get the hopTableId and Channel Index
if ((null != msg_rsp.AntennaConfiguration)
&& (0 < msg_rsp.AntennaConfiguration.Length)
&& (null != msg_rsp.AntennaConfiguration[0].RFTransmitter))
{
PARAM_RFTransmitter rftx =
msg_rsp.AntennaConfiguration[0].RFTransmitter;
// we have to get these two values as well otherwise
// we won’t know what to fill in the RFTransmitter
// parameter when we set transmit power
ChannelIndex = rftx.ChannelIndex;
hopTableID = rftx.HopTableID;
Console.WriteLine(” Saving ChanIndex ” +
ChannelIndex.ToString() +
” hopTableId ” +
hopTableID.ToString());
}
else
{
LTKCPP
CGET_READER_CONFIG *pCmd;
CMessage * pRspMsg;
CGET_READER_CONFIG_RESPONSE *pRsp;
std::list<CAntennaConfiguration*>::iterator pAntCfg;
/*
* Compose the command message
*/
pCmd = new CGET_READER_CONFIG();
pCmd->setMessageID(m_messageID++);
pCmd->setRequestedData(GetReaderConfigRequestedData_All);
/*
* Send the message, expect a certain type of response
*/
pRspMsg = transact(pCmd);
/*
* Done with the command message
*/
delete pCmd;
/*
* transact() returns NULL if something went wrong.
*/
if(NULL == pRspMsg)
{
/* transact already tattled */
return -1;
}
/*
* Cast to a CGET_READER_CONFIG_RESPONSE message.
*/
pRsp = (CGET_READER_CONFIG_RESPONSE *) pRspMsg;
/*
* Check the LLRPStatus parameter.
*/
if(0 != checkLLRPStatus(pRsp->getLLRPStatus(),
“getReaderConfig”))
{
/* checkLLRPStatus already tattled */
delete pRspMsg;
return -1;
}
/* just get the hop table and channel index out of
** the first antenna configuration since they must all
** be the same */
pAntCfg = pRsp->beginAntennaConfiguration();
if(pAntCfg != pRsp->endAntennaConfiguration())
{
CRFTransmitter *prfTx;
prfTx = (*pAntCfg)->getRFTransmitter();
m_hopTableID = prfTx->getHopTableID();
m_channelIndex = prfTx->getChannelIndex();
}
else
{
delete pRspMsg;
return -1;
}
if(1 < m_Verbose)
{
LTKJAVA
LLRPMessage response;
GET_READER_CONFIG_RESPONSE gresp;
GET_READER_CONFIG get = new GET_READER_CONFIG();
GetReaderConfigRequestedData data =
new GetReaderConfigRequestedData(
GetReaderConfigRequestedData.All);
get.setRequestedData(data);
get.setMessageID(getUniqueMessageID());
get.setAntennaID(new UnsignedShort(0));
get.setGPIPortNum(new UnsignedShort(0));
get.setGPOPortNum(new UnsignedShort(0));
logger.info(“Sending GET_READER_CONFIG message …”);
try {
response = connection.transact(get, 10000);
// check whether GET_CAPABILITIES addition was suc-
cessful
gresp = (GET_READER_CONFIG_RESPONSE)response;
StatusCode status = gresp.getLLRPStatus().getStatusCode();
if (status.equals(new StatusCode(“M_Success”))) {
logger.info(“GET_READER_CONFIG was suc-
cessful”);
List<AntennaConfiguration> alist = gresp.getAntennaConfigurationList();
if(!alist.isEmpty()) {
AntennaConfiguration a_cfg = alist.get(0);
channelIndex = a_cfg.getRFTransmitter().getChannelIndex();
hopTableID = a_cfg.getRFTransmitter().getHopTableID();
logger.info(“ChannelIndex” + channelIn-
dex.toString() +
” hopTableID ” + hopTableID.toString());
} else {
logger.error(“Could not find antenna configura-
tion”);
System.exit(1);
}
}
else {
logger.info(response.toXMLString());
logger.info(“GET_READER_CONFIG failures”);
System.exit(1);
}
} catch (InvalidLLRPMessageException ex) {
logger.error(“Could not display response string”);
} catch (TimeoutException ex) {
logger.error(“Timeout waiting for GET_READER_CONFIG
response”);
System.exit(1);
8.5.1 LTK-XML
LTK-XML
xmlns=“http://www.llrp.org/ltk/schema/core/encoding/xml/1.0”
xmlns:llrp=“http://www.llrp.org/ltk/schema/core/encoding/xml/
1.0”
xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”
xmlns:Impinj=“http://developer.impinj.com/ltk/schema/encoding/
xml/1.6”
xsi:schemaLocation=”http://www.llrp.org/ltk/schema/core/
encoding/xml/1.0
http://www.llrp.org/ltk/schema/core/encoding/xml/1.0/llrp.xsd
http://developer.impinj.com/ltk/schema/encoding/xml/1.6
http://developer.impinj.com/ltk/schema/encoding/xml/1.6/impinj.
xsd”
MessageID=“0”>
<ResetToFactoryDefault>false</ResetToFactoryDefault>
<AntennaConfiguration>
<AntennaID>0</AntennaID>
<!– we will over-write the transmit power in our code –>
<RFTransmitter>
<HopTableID>1</HopTableID>
<ChannelIndex>1</ChannelIndex>
<TransmitPower>1</TransmitPower>
</RFTransmitter>
<C1G2InventoryCommand>
<TagInventoryStateAware>false</TagInventoryStateAware>
<C1G2RFControl>
<!–Set mode to Gen2 DRM AutoSet Mode Tari is
ignored –>
<ModeIndex>1000</ModeIndex>
<Tari>0</Tari>
</C1G2RFControl>
<C1G2SingulationControl>
<!–Will use session 2 –>
<Session>2</Session>
<TagPopulation>32</TagPopulation>
<TagTransitTime>0</TagTransitTime>
</C1G2SingulationControl>
<Impinj:ImpinjInventorySearchMode xmlns=“http:
//developer.impinj.com/ltk/schema/encoding/
xml/1.6”>
<!–Will use Dual-target–>
<InventorySearchMode>Dual_Target</InventorySearchMode>
</Impinj:ImpinjInventorySearchMode>
<!–Enable Low Duty Cycle when no tags are seen for 10
seconds. Check antennas every 200 msec –>
<Impinj:ImpinjLowDutyCycle xmlns=“http://
developer.impinj.com/ltk/schema/encoding/xml/1.
6”>
<LowDutyCycleMode>Enabled</LowDutyCycleMode>
<EmptyFieldTimeout>10000</EmptyFieldTimeout>
<FieldPingInterval>200</FieldPingInterval>
</Impinj:ImpinjLowDutyCycle>
</C1G2InventoryCommand>
</AntennaConfiguration>
<ROReportSpec>
<ROReportTrigger>Upon_N_Tags_Or_End_Of_ROSpec
</ROReportTrigger>
<N>1</N>
<TagReportContentSelector>
<EnableROSpecID>false</EnableROSpecID>
<EnableSpecIndex>false</EnableSpecIndex>
<EnableInventoryParameterSpecID>false
</EnableInventoryParameterSpecID>
<EnableAntennaID>false</EnableAntennaID>
<EnableChannelIndex>false</EnableChannelIndex>
<EnablePeakRSSI>false</EnablePeakRSSI>
<EnableFirstSeenTimestamp>false</EnableFirstSeenTimestamp>
<EnableLastSeenTimestamp>false</EnableLastSeenTimestamp>
<EnableTagSeenCount>false</EnableTagSeenCount>
<EnableAccessSpecID>false</EnableAccessSpecID>
<C1G2EPCMemorySelector>
<EnableCRC>false</EnableCRC>
<EnablePCBits>false</EnablePCBits>
</C1G2EPCMemorySelector>
</TagReportContentSelector>
<!– Don’t need any extra tag information beyond EPC –>
</ROReportSpec>
</SET_READER_CONFIG>
8.5.2 LTKNET
The C# code that provisions this LTKNET configuration on the Reader Gateway is shown in
Figure 8.5. Note: this is nearly identical to the RoSpec example in Section 7.4.2.
#region SetReaderConfigWithXML
{
if (rsp != null)
{
if (rsp.LLRPStatus.StatusCode != ENUM_StatusCode.M_Success)
{
Console.WriteLine(rsp.LLRPStatus.StatusCode.ToString());
reader.Close();
return;
}
}
else if (msg_err != null)
{
Console.WriteLine(msg_err.ToString());
reader.Close();
return;
}
else
{
Console.WriteLine(“SET_READER_CONFIG Command
Timed out\n”);
reader.Close();
return;
}
}
#endregion
8.5.3 LTKCPP
CMessage * pCmdMsg;
CSET_READER_CONFIG *pCmd;
CMessage * pRspMsg;
CSET_READER_CONFIG_RESPONSE *pRsp;
CXMLTextDecoder * pDecoder;
std::list<CAntennaConfiguration *>::iterator Cur;
/* Build a decoder to extract the message from XML */
pDecoder = new CXMLTextDecoder(m_pTypeRegistry, “se-
tReaderConfig.xml”);
if(NULL == pDecoder)
{
return -1;
}
pCmdMsg = pDecoder->decodeMessage();
delete pDecoder;
if(NULL == pCmdMsg)
{
return -2;
}
if(&CSET_READER_CONFIG::s_typeDescriptor != pCmdMsg-
>m_pType)
{
return -3;
}
/* get the message as a SET_READER_CONFIG */
pCmd = (CSET_READER_CONFIG *) pCmdMsg;
/* It’s always a good idea to give it a unique message ID */
pCmd->setMessageID(m_messageID++);
Cur = pCmd->beginAntennaConfiguration();
Cur != pCmd->endAntennaConfiguration();
Cur++)
}
/*
* Send the message, expect a certain type of response
*/
pRspMsg = transact(pCmd);
/*
* Done with the command message
*/
delete pCmd;
/*
* transact() returns NULL if something went wrong.
*/
if(NULL == pRspMsg)
{
}
/*
* Cast to a CSET_READER_CONFIG_RESPONSE message.
*/
pRsp = (CSET_READER_CONFIG_RESPONSE *) pRspMsg;
/*
* Check the LLRPStatus parameter.
*/
if(0 != checkLLRPStatus(pRsp->getLLRPStatus(),
“setImpinjReaderConfig”))
}
/*
* Done with the response message.
*/
delete pRspMsg;
/*
}
/*
* Victory.
*/
return 0;
8.5.4 LTKJAVA
This section provides the Java code to use to provision this configuration. Note specifically how
the XML reads and exceptions are handled. In addition, notice how to access the parameter fields,
and how to add a custom parameter.
LTKJAVA
LLRPMessage response;
logger.info(“Loading SET_READER_CONFIG message from file
SET_READER_CONFIG.xml …”);
try {
LLRPMessage setConfigMsg = Util.loadXMLLLRPMessage(
new File(
“./source/org/impinj/llrp/ltk/examples/docsample2/SET_READER_CONFIG.xml”));
The customer wants to read tags through a portal separating a restricted storage facility
from their main manufacturing facility. The facility is located off-site and connects to
the enterprise by a low-bandwidth link. The RFID system serves as an audit tool and
will not be used for restricted access control. Personnel and asset observations must be
time-stamped to make an accurate correlation between them.
Two types of tags will pass through the portal: assets and personnel tags. For asset tags,
all of the information is contained in the EPC. For personnel tags, however, the Gen2
user memory contains a code that describes their access permissions for the facility and
company affiliation, and will be used off-line for equipment auditing. Personnel tags
use a 96-bit GID EPC format, while assets use a 96-bit GIAI format. The facility is
shared and contains several other RFID applications.
Data is to be sent over a very low-bandwidth connection. Accumulation should be
used to report tags only once every 30 seconds at most. The report size should be
minimized.
Because the Reader is located at a share facility, the Reader should minimize its RF
interference.
9.1 Analysis
The application uses the Speedway LLRP accumulation feature and limits the quantity and con-
tents of the tag reports from the Reader.
The application uses the LLRP C1G2Filter and eliminates reads of all tags except for GID-96 and
GRAI-96.
Because the Reader should minimize its RF interference, use only low duty cycle mode as described
in Section 8.5.
Steps 1-5, 7, 9, and 11 were covered in earlier examples. Examples for steps 6, 8 and 10 are
included in this section.
9.3.1 LTKNET
xmlns=“http://www.llrp.org/ltk/schema/core/encoding/xml/1.0”
xmlns:llrp=“http://www.llrp.org/ltk/schema/core/encoding/xml/
1.0”
xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”
xmlns:Impinj=“http://developer.impinj.com/ltk/schema/encoding/
xml/1.6”
xsi:schemaLocation=”http://www.llrp.org/ltk/schema/core/
encoding/xml/1.0
http://www.llrp.org/ltk/schema/core/encoding/xml/1.0/llrp.xsd
http://developer.impinj.com/ltk/schema/encoding/xml/1.6
http://developer.impinj.com/ltk/schema/encoding/xml/1.6/impinj.
xsd”
MessageID=“0”>
<ResetToFactoryDefault>false</ResetToFactoryDefault>
<AntennaConfiguration>
<AntennaID>0</AntennaID>
<C1G2InventoryCommand>
<TagInventoryStateAware>false</TagInventoryStateAware>
<!–Enable Low Duty Cycle when no tags are seen
for 10 seconds. Check antennas every 200 msec –>
<Impinj:ImpinjLowDutyCycle xmlns=“http://
developer.impinj.com/ltk/schema/encoding/xml/1.
6”>
<LowDutyCycleMode>Enabled</LowDutyCycleMode>
<EmptyFieldTimeout>10000</EmptyFieldTimeout>
<FieldPingInterval>200</FieldPingInterval>
</Impinj:ImpinjLowDutyCycle>
</C1G2InventoryCommand>
</AntennaConfiguration>
<ROReportSpec>
<ROReportTrigger>None</ROReportTrigger>
<N>0</N>
<TagReportContentSelector>
<EnableROSpecID>false</EnableROSpecID>
<EnableSpecIndex>false</EnableSpecIndex>
<EnableInventoryParameterSpecID>false
</EnableInventoryParameterSpecID>
<EnableAntennaID>false</EnableAntennaID>
<EnableChannelIndex>false</EnableChannelIndex>
<EnablePeakRSSI>false</EnablePeakRSSI>
<EnableFirstSeenTimestamp>true</EnableFirstSeenTimestamp>
<EnableLastSeenTimestamp>false</EnableLastSeenTimestamp>
<EnableTagSeenCount>false</EnableTagSeenCount>
<EnableAccessSpecID>false</EnableAccessSpecID>
<C1G2EPCMemorySelector>
<EnableCRC>false</EnableCRC>
<EnablePCBits>false</EnablePCBits>
</C1G2EPCMemorySelector>
</TagReportContentSelector>
</ROReportSpec>
</SET_READER_CONFIG>
9.3.2 LTKCPP
In LTKCPP, the example in Figure 9.2 uses a code similar to docsample2 (Section 8.5.3), except
it uses the default values for the Gen2 parameters.
LTKCPP
/*
** Apply this configuration to all antennas
*/
pAnt->setAntennaID(0);
/*
** Create the container Inventory command to hold all the parameters
*/
CC1G2InventoryCommand *pC1G2Inv = new CC1G2InventoryCommand();
/*
** set the Impinj Low Duty Cycle mode as per the use case
*/
CImpinjLowDutyCycle *pImpLdc = new CImpinjLowDutyCycle();
pImpLdc->setEmptyFieldTimeout(10000);
pImpLdc->setFieldPingInterval(200);
pImpLdc->setLowDutyCycleMode(ImpinjLowDutyCycleMode_Enabled);
pC1G2Inv->addCustom(pImpLdc);
/*
** Don’t forget to add the InventoryCommand to the antenna
** configuration, and then add the antenna configuration to the
** config message
*/
pAnt->addAirProtocolInventoryCommandSettings(pC1G2Inv);
pCmd->addAntennaConfiguration(pAnt);
/*
** report every tag (N=1)
*/
CROReportSpec *pROrs = new CROReportSpec();
pROrs->setROReportTrigger(ROReportTriggerType_None);
pROrs->setN(0);
/*
** Turn off report data that we don’t need since our use
** case suggests we are bandwidth constrained
*/
9.3.3 LTKJAVA
Impinj Best Practice! The PC bits contain the EPC length and
are a good “double check” for reading the EPC header. However, the PC
bits also contain several other indicators (UMI, XPC etc) that are
not guaranteed to be the same across all tag variants. If your
application filters on the PC bits, be sure to only filter on the
first 5 bits (0x10 – 0x14).
In LLRP, we add two C1G2Filter parameters to our ROSpec to select only these tags for inventory.
The filter Action uses the Select_Unselect action for the first filter, which excludes (unselects) all
tags that don’t match the filter (GRAI) and includes (selects) all tags that do match the filter.
The second filter uses the Select_DoNothing action, which builds on the first filter by adding those
tags that match the GID filter (selects) and leaving the other tags unchanged (doNothing). If we
re-used the Select_Unselect filter on the second filter, it would have unselected the GRAI tags
from the first filter.
9.4.1 LTKNET
In LTKNET, the LTK-XML uses construction based on the ROSpec. Figure 9.3 shows an excerpt
of the ROSpec with the C1G2Filters added to the C1G2InventoryCommand.
LTK-XML
<AntennaConfiguration>
<AntennaID>0</AntennaID>
<C1G2InventoryCommand>
<TagInventoryStateAware>false</TagInventoryStateAware>
<C1G2Filter>
<T>Do_Not_Truncate</T>
<C1G2TagInventoryMask>
<MB>1</MB>
<Pointer>32</Pointer>
<!– pointer is in decimal –>
<TagMask Count=“8”>33</TagMask>
</C1G2TagInventoryMask>
<C1G2TagInventoryStateUnawareFilterAction>
<Action>Select_Unselect</Action>
</C1G2TagInventoryStateUnawareFilterAction>
</C1G2Filter>
<C1G2Filter>
<T>Do_Not_Truncate</T>
<C1G2TagInventoryMask>
<MB>1</MB>
<Pointer>32</Pointer>
<!– pointer is in decimal –>
<TagMask Count=“8”>35</TagMask>
</C1G2TagInventoryMask>
<C1G2TagInventoryStateUnawareFilterAction>
<Action>Select_DoNothing</Action>
</C1G2TagInventoryStateUnawareFilterAction>
</C1G2Filter>
</C1G2InventoryCommand>
</AntennaConfiguration>
9.4.2 LTKCPP
CInventoryParameterSpec * pInventoryParameterSpec =
new CInventoryParameterSpec();
pInventoryParameterSpec->setInventoryParameterSpecID(1234);
pInventoryParameterSpec->setProtocolID(AirProtocols_EPCGlobalClass1Gen2);
/* make the bit pattern for the GID mask */
llrp_u1v_t gidMask = llrp_u1v_t(8);
gidMask.m_nBit = 8;
gidMask.m_pValue[0] = 0x33;
/* build the mask for the GID */
CC1G2TagInventoryMask *pMaskGID = new(CC1G2TagInventoryMask);
pMaskGID->setMB(1);
pMaskGID->setPointer(32);
pMaskGID->setTagMask(gidMask);
/* build the inventory action for the GID filter */
CC1G2TagInventoryStateUnawareFilterAction *pActionGID=
new CC1G2TagInventoryStateUnawareFilterAction();
pActionGID->setAction(C1G2StateUnawareAction_Select_Unselect);
/* Build the filter for the GID */
CC1G2Filter *pFilterGID = new CC1G2Filter();
pFilterGID->setC1G2TagInventoryStateUnawareFilterAction(pActionGID);
pFilterGID->setC1G2TagInventoryMask(pMaskGID);
pFilterGID->setT(C1G2TruncateAction_Do_Not_Truncate);
/* make the bit pattern for the GRAI mask */
llrp_u1v_t graiMask = llrp_u1v_t(8);
graiMask.m_nBit = 8;
graiMask.m_pValue[0] = 0x35;
/* build the mask for the GRAI */
CC1G2TagInventoryMask *pMaskGRAI = new(CC1G2TagInventoryMask);
pMaskGRAI->setMB(1);
pMaskGRAI->setPointer(32);
pMaskGRAI->setTagMask(graiMask);
/* build the inventory action for the FRAI filter */
CC1G2TagInventoryStateUnawareFilterAction *pActionGRAI=
new CC1G2TagInventoryStateUnawareFilterAction();
pActionGRAI->setAction(C1G2StateUnawareAction_Select_DoNothing);
/* Build the filter for the GRAI */
CC1G2Filter *pFilterGRAI = new CC1G2Filter();
pFilterGRAI->setC1G2TagInventoryStateUnawareFilterAction(pActionGRAI);
pFilterGRAI->setC1G2TagInventoryMask(pMaskGRAI);
pFilterGRAI->setT(C1G2TruncateAction_Do_Not_Truncate);
/* build the inventory command and add both filters */
CC1G2InventoryCommand *pInvCmd = new CC1G2InventoryCommand();
pInvCmd->setTagInventoryStateAware(false);
pInvCmd->addC1G2Filter(pFilterGID);
pInvCmd->addC1G2Filter(pFilterGRAI);
/* Build the antennaConfiguration to Contain this */
CAntennaConfiguration * pAntennaConfiguration =
new CAntennaConfiguration();
pAntennaConfiguration->setAntennaID(0);
pAntennaConfiguration->addAirProtocolInventoryCommandSettings(pInvCmd);
/* don’t forget to add this to the INventory Parameter Spec above */
pInventoryParameterSpec->addAntennaConfiguration(pAntennaConfiguration);
9.4.3 LTKJAVA
In LTKJAVA, Impinj uses the LTK-XML based construction of ROSpec. Figure 9.4 shows an
excerpt of the ROSpec with the C1G2Filters added to the C1G2InventoryCommand.
LTK-XML
<AntennaConfiguration>
<AntennaID>0</AntennaID>
<C1G2InventoryCommand>
<TagInventoryStateAware>false</TagInventoryStateAware>
<C1G2Filter>
<T>Do_Not_Truncate</T>
<C1G2TagInventoryMask>
<MB>1</MB>
<Pointer>32</Pointer>
<!– pointer is in decimal –>
<TagMask Count=“8”>33</TagMask>
</C1G2TagInventoryMask>
<C1G2TagInventoryStateUnawareFilterAction>
<Action>Select_Unselect</Action>
</C1G2TagInventoryStateUnawareFilterAction>
</C1G2Filter>
<C1G2Filter>
<T>Do_Not_Truncate</T>
<C1G2TagInventoryMask>
<MB>1</MB>
<Pointer>32</Pointer>
<!– pointer is in decimal –>
<TagMask Count=“8”>35</TagMask>
</C1G2TagInventoryMask>
<C1G2TagInventoryStateUnawareFilterAction>
<Action>Select_DoNothing</Action>
</C1G2TagInventoryStateUnawareFilterAction>
</C1G2Filter>
</C1G2InventoryCommand>
</AntennaConfiguration>
corresponding to EPC memory. We want to match (run the access spec) on tags that start with
hex 0x300035. This corresponds to the 16 PC (protocol control) bits in gen2 and in the EPCglobal
EPC header that corresponds to a GID (0x35). Use the TagMask parameter to ignore the last 11
bits of the PC word, comparing only against the first 5 bits, which is the length. This ensures that
we only get 96-bit GIDs that match this access spec. Once a tag matches, the Reader performs a
read of the first two words of user memory (MB 3).
Note: This command has two MB fields. The first is in the target tag and determines which tags
this AccessSpec applies to. The second is in the Read command, which performs the read.
Note: The pointers and lengths in the target tag parameter are specified in bits. The pointer and
length are specified in 16-bit words.
9.5.1 LTKNET
xmlns=“http://www.llrp.org/ltk/schema/core/encoding/xml/1.0”
xmlns:llrp=“http://www.llrp.org/ltk/schema/core/encoding/xml/
1.0”
xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”
xmlns:Impinj=“http://developer.impinj.com/ltk/schema/encoding/
xml/1.6”
xsi:schemaLocation=”http://www.llrp.org/ltk/schema/core/
encoding/xml/1.0
http://www.llrp.org/ltk/schema/core/encoding/xml/1.0/llrp.xsd
http://developer.impinj.com/ltk/schema/encoding/xml/1.6
http://developer.impinj.com/ltk/schema/encoding/xml/1.6/impinj.
xsd”
MessageID=“0”>
<AccessSpec>
<AccessSpecID>23</AccessSpecID>
<AntennaID>0</AntennaID>
<!– 0 means to work on all antennas –>
<ProtocolID>EPCGlobalClass1Gen2</ProtocolID>
<CurrentState>Disabled</CurrentState>
<ROSpecID>0</ROSpecID>
<!–0 means to work with any RO Spec –>
<AccessSpecStopTrigger>
<AccessSpecStopTrigger>Null</AccessSpecStopTrigger>
<OperationCountValue>0</OperationCountValue>
<!–OperationCountValue is ignored since we are not using
the trigger –>
</AccessSpecStopTrigger>
<AccessCommand>
<C1G2TagSpec>
<C1G2TargetTag>
<MB>1</MB>
<Match>true</Match>
<Pointer>16</Pointer>
<!–GID-96 looks like hex 300035 –>
<!– Use the mask so the 11 remaining PC bits are
don’t care –>
<TagMask>f800ff</TagMask>
<TagData>300035</TagData>
</C1G2TargetTag>
</C1G2TagSpec>
<!–read the first two words of user memory-->
<C1G2Read>
<OpSpecID>1</OpSpecID>
<AccessPassword>0</AccessPassword>
<MB>3</MB>
<WordPointer>0</WordPointer>
<WordCount>2</WordCount>
</C1G2Read>
</AccessCommand>
<AccessReportSpec>
<!–Report when the access spec completes (e.g. read is done)
–>
<AccessReportTrigger>End_Of_AccessSpec</AccessReportTrigger>
</AccessReportSpec>
</AccessSpec>
</ADD_ACCESSSPEC>
#region ADDAccessSpecWithXML
{
ENUM_LLRP_MSG_TYPE.ADD_ACCESSSPEC)
{
Console.WriteLine(“Could not extract message from
XML”);
reader.Close();
return;
}
}
catch
{
Console.WriteLine(“Unable to convert to valid XML”);
reader.Close();
return;
}
// Communicate that message to the reader
MSG_ADD_ACCESSSPEC msg = (MSG_ADD_ACCESSSPEC)obj;
MSG_ERROR_MESSAGE msg_err;
MSG_ADD_ACCESSSPEC_RESPONSE rsp =
reader.ADD_ACCESSSPEC(msg, out msg_err, 12000);
if (rsp != null)
{
if (rsp.LLRPStatus.StatusCode != ENUM_StatusCode.M_Success)
{
Console.WriteLine(rsp.LLRPStatus.StatusCode.ToString());
reader.Close();
return;
}
}
else if (msg_err != null)
{
Console.WriteLine(msg_err.ToString());
reader.Close();
return;
}
else
{
Console.WriteLine(“ADD_ACCESSSPEC Command
Timed out\n”);
reader.Close();
return;
}
}
#endregion
read.WordCount = 2;
read.WordPointer = 0;
read.OpSpecID = 2;
// add the opSpec and the TagSpec to the AccessCmd
PARAM_AccessCommand accessCmd = new PARAM_AccessCommand();
accessCmd.AirProtocolTagSpec = new UNION_AirProtocolTagSpec();
accessCmd.AirProtocolTagSpec.Add(tagSpec);
accessCmd.AccessCommandOpSpec.Add(read);
// create the stop trigger for the Access Spec
PARAM_AccessSpecStopTrigger stop = new PARAM_AccessSpecStopTrigger();
stop.AccessSpecStopTrigger = ENUM_AccessSpecStopTriggerType.Null;
stop.OperationCountValue = 0;
// Create and set up the basic accessSpec
PARAM_AccessSpec accessSpec = new PARAM_AccessSpec();
accessSpec.AccessSpecID = 24;
accessSpec.AntennaID = 0;
accessSpec.ROSpecID = 0;
accessSpec.CurrentState = ENUM_AccessSpecState.Disabled;
accessSpec.ProtocolID = ENUM_AirProtocols.EPCGlobalClass1Gen2;
// add the access command and stop trigger to the accessSpec
accessSpec.AccessCommand = accessCmd;
accessSpec.AccessSpecStopTrigger = stop;
// Add the Access Spec to the ADD_ACCESSSPEC message
MSG_ADD_ACCESSSPEC addAccess = new MSG_ADD_ACCESSSPEC();
addAccess.MSG_ID = msgID++;
addAccess.AccessSpec = accessSpec;
// communicate the message to the reader
MSG_ERROR_MESSAGE msg_err;
MSG_ADD_ACCESSSPEC_RESPONSE rsp =
reader.ADD_ACCESSSPEC(addAccess, out msg_err,
12000);
if (rsp != null)
{
if (rsp.LLRPStatus.StatusCode != ENUM_StatusCode.M_Success)
{
Console.WriteLine(rsp.LLRPStatus.StatusCode.ToString());
reader.Close();
return;
}
}
else if (msg_err != null)
{
Console.WriteLine(msg_err.ToString());
reader.Close();
return;
}
else
{
Console.WriteLine(“ADD_ACCESSSPEC Command
Timed out\n”);
reader.Close();
return;
}
}
#endregion
#region CheckForAccessResults
// check for read data results
if ((msg.TagReportData[i].AccessCommandOpSpecResult != null))
{
}
#endregion
9.5.2 LTKCPP
We build the ACCESS_SPEC message much like other messages in LTKCPP. Note the TagMask
and TagData, which mark the 11 C1G2PC bits as “don’t care”.
LTKCPP
CADD_ACCESSSPEC * pCmd;
CMessage * pRspMsg;
CADD_ACCESSSPEC_RESPONSE * pRsp;
pCmd = new CADD_ACCESSSPEC();
pCmd->setMessageID(m_messageID++);
/* build the C1G2Target Tag with the AccessSpec filter */
CC1G2TargetTag *ptargetTag = new CC1G2TargetTag();
ptargetTag->setMatch(true);
ptargetTag->setMB(1);
ptargetTag->setPointer(16);
llrp_u1v_t tagData = llrp_u1v_t(24);
tagData.m_nBit = 24;
tagData.m_pValue[0] = 0x30;
tagData.m_pValue[1] = 0x00;
tagData.m_pValue[2] = 0x35;
ptargetTag->setTagData(tagData);
llrp_u1v_t tagMask = llrp_u1v_t(24);
tagMask.m_nBit = 24;
tagMask.m_pValue[0] = 0xf8;
tagMask.m_pValue[1] = 0x00;
tagMask.m_pValue[2] = 0xff;
ptargetTag->setTagMask(tagMask);
/* build the AirProtocolTagSpec Add the filter */
CC1G2TagSpec *ptagSpec = new CC1G2TagSpec();
ptagSpec->addC1G2TargetTag(ptargetTag);
/* Build the read Op Spec */
CC1G2Read *pread = new CC1G2Read();
pread->setAccessPassword(0);
pread->setMB(3);
pread->setOpSpecID(1);
pread->setWordCount(2);
pread->setWordPointer(0);
/* Create the AccessCommand. Add the TagSpec and the OpSpec */
CAccessCommand *pAccessCommand = new CAccessCom-
mand();
pAccessCommand->setAirProtocolTagSpec(ptagSpec);
pAccessCommand->addAccessCommandOpSpec(pread);
/* set up the Access Report Spec rule to report only with ROSpecs */
CAccessReportSpec *pAccessReportSpec = new CAccessReport-
Spec();
pAccessReportSpec->setAccessReportTrigger(
AccessReportTriggerType_Whenever_ROReport_Is_Generated);
/* set up the stop trigger for the access spec. Do not stop */
CAccessSpecStopTrigger *pAccessStopTrigger = new CAc-
cessSpecStopTrigger();
pAccessStopTrigger->setAccessSpecStopTrigger(
AccessSpecStopTriggerType_Null);
pAccessStopTrigger->setOperationCountValue(0); /* ignored */
/* Create and configure the AccessSpec */
CAccessSpec *pAccessSpec = new CAccessSpec();
pAccessSpec->setAccessSpecID(23);
pAccessSpec->setAntennaID(0); /* valid for all antennas */
pAccessSpec->setCurrentState(AccessSpecState_Disabled);
pAccessSpec->setProtocolID(AirProtocols_EPCGlobalClass1Gen2);
pAccessSpec->setROSpecID(0); /* valid for All RoSpecs */
pAccessSpec->setAccessSpecStopTrigger(pAccessStopTrigger);
pAccessSpec->setAccessReportSpec(pAccessReportSpec);
pAccessSpec->setAccessCommand(pAccessCommand);
/* Add the AccessSpec to the ADD_ACCESS_SPEC message */
pCmd->setAccessSpec(pAccessSpec);
/*
* Send the message, expect a certain type of response
*/
char aBuf[64];
char bBuf[64];
std::list<CParameter *>::iterator OpSpecResults;
/*
* Print the EPC. It could be an 96-bit EPC_96 parameter
* or an variable length EPCData parameter.
*/
CParameter * pEPCParameter =
pTagReportData->getEPCParameter();
formatOneEPC(pEPCParameter, aBuf, 64);
/*
** This section only handles ReadResults. It can be extended in a
** similar fashion to handle all OpSpecResults
*/
bBuf[0] = ‘\0’;
for (
OpSpecResults = pTagReportData->beginAccessCommandOpSpecResult();
OpSpecResults != pTagReportData->endAccessCommandOpSpecResult();
OpSpecResults++)
{
if( (*OpSpecResults)->m_pType ==
&CC1G2ReadOpSpecResult::s_typeDescriptor)
{
formatOneReadResult(*OpSpecResults, bBuf, 64);
}
}
/*
* End of line
*/
printf(“EPC: %s %s\n”, aBuf, bBuf);
9.5.3 LTKJAVA
LLRPMessage response;
logger.info(“Loading ADD_ACCESSSPEC message from file
ADD_ACCESSSPEC.xml…”);
try {
LLRPMessage addAccessMsg = Util.loadXMLLLRPMessage(new
File(
“./source/org/impinj/llrp/ltk/examples/docsample3/ADD_ACCESSSPEC.xml”));
if (opr.getClass() == C1G2ReadOpSpecResult.class) {
epcString += ” Read: ”;
C1G2ReadOpSpecResult rr = (C1G2ReadOpSpecResult)
opr;
epcString += ” Status: ” + rr.getResult().toString();
epcString += ” Data: ” + rr.getReadData().toString();
}
Note: With accumulation enabled on the reports it is possible to get multiple Ac-
cessSpecs for the same tag. This occurs if the Reader tries multiple times to access the
same tag data and gets different results. Different results can occur because of errors
reported by the Tag or Reader.
Polling the Reader involves periodically generating the GET_REPORT message and delivering
it to the Reader. No change is required in the RO_ACCESS_REPORT handling, which was
demonstrated in previous examples.
9.6.1 LTKNET
Polling for report data in LTKNET involves periodically building a simple GET_REPORT mes-
sage and sending it to the Reader.
LTKNET
Thread.Sleep(30000);
#region PollReaderReports
{
9.6.2 LTKCPP
Polling for report data in LTKCPP involves periodically building a simple GET_REPORT message
and sending it to the Reader.
9.6.3 LTKJAVA
Polling for report data in LTKJAVA involves periodically building a simple GET_REPORT mes-
sage and sending it to the Reader. Note that this employs the send method rather than the
transact method.
LTKJAVA
An application wants to get more low-level data about tags as they pass through a
portal. The application will use this data to estimate the instantaneous velocity of the
tag, to help determine its direction and timing through the portal.
10.1 Analysis
To use the low-level data available via Speedway, the application must configure the tag data that is
reported through LLRP. Some of the data is available from the standard TagReportContentSelector
parameter. The remaining Impinj-specific data is available through the ImpinjTagReportContentS-
elector parameter, which is a custom extension to the standard LLRP.
The use case requires multiple reads to make a good estimate of velocity, so the application
enables “dual-target” inventory with Gen2 session 2. Setting the mode involves a tradeoff between
the number of samples and the accuracy of these samples (see the same application node). In
this case, DRM M=4 is chosen for the LTKCPP example, and Max throughput is chosen for the
LTKNET example.
After the Reader is configured and low-level data is enabled on the Reader, the application must
extract the data from the RO_ACCESS_REPORT message. Then the application must calculate
the radial velocity of the tag. For a description of estimating velocity, see the reference in Section
1.6.8.
You have sample code for steps 1-4, and 6-8, so this discussion and example begins with step 5.
For the complete sample code, see the docSample4 directories in the Impinj LTK distribution.
Examples for both LTKCPP and LTKNET show the object-based parameter and message con-
struction in the next section.
10.3.1 LTK-XML
The following LTK-XML describes the configuration required for the application. Specifically, take
note of the TagReportContentSelector and the ImpinjTagReportContentSelector, which enable
the required data for our velocity estimate. Note how to set the session, ImpinjInventorySearch-
Mode, and ModeIndex values in the code below.
LTK-XML
<SET_READER_CONFIG
xmlns=“http://www.llrp.org/ltk/schema/core/encoding/
xml/1.0”
xmlns:llrp=“http://www.llrp.org/ltk/schema/core/encoding/
xml/1.0”
xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”
xmlns:Impinj=“http://developer.impinj.com/ltk/schema/
encoding/xml/1.0”
xsi:schemaLocation=”http://www.llrp.org/ltk/schema/
core/encoding/xml/1.0
http://www.llrp.org/ltk/schema/core/encoding/xml/1.0/
llrp.xsd
http://developer.impinj.com/ltk/schema/encoding/xml/1.
8
http://developer.impinj.com/ltk/schema/encoding/xml/1.
8/impinj.xsd”
MessageID=“0”>
<ResetToFactoryDefault>false</ResetToFactoryDefault>
<AntennaConfiguration>
<AntennaID>0</AntennaID>
<C1G2InventoryCommand>
<TagInventoryStateAware>false</TagInventoryStateAware>
<C1G2RFControl>
<ModeIndex>0</ModeIndex>
<Tari>0</Tari>
</C1G2RFControl>
<C1G2SingulationControl>
<!–Need Dual-target for lots of reads, so use ses-
sion 2 –>
<Session>2</Session>
<TagPopulation>32</TagPopulation>
<TagTransitTime>0</TagTransitTime>
</C1G2SingulationControl>
<Impinj:ImpinjInventorySearchMode
xmlns=“http://developer.impinj.com/ltk/
schema/encoding/xml/1.8”>
<!–Need Dual-target for lots of low level data
–>
<InventorySearchMode>Dual_Target</InventorySearchMode>
</Impinj:ImpinjInventorySearchMode>
<Impinj:ImpinjLowDutyCycle
xmlns=“http://developer.impinj.com/ltk/
schema/encoding/xml/1.8”>
<LowDutyCycleMode>Disabled</LowDutyCycleMode>
<EmptyFieldTimeout>10000</EmptyFieldTimeout>
<FieldPingInterval>200</FieldPingInterval>
</Impinj:ImpinjLowDutyCycle>
</C1G2InventoryCommand>
</AntennaConfiguration>
<ROReportSpec>
<ROReportTrigger>Upon_N_Tags_Or_End_Of_ROSpec
</ROReportTrigger>
<N>1</N>
<!–Want to report every tag for velocity estimate –>
<TagReportContentSelector>
<!– Enable certain things that we’ll use with low
level data
especially antenna, channel, timestamp –>
<EnableROSpecID>false</EnableROSpecID>
<EnableSpecIndex>false</EnableSpecIndex>
<EnableInventoryParameterSpecID>false
</EnableInventoryParameterSpecID>
<EnableAntennaID>true</EnableAntennaID>
<EnableChannelIndex>true</EnableChannelIndex>
<EnablePeakRSSI>true</EnablePeakRSSI>
<EnableFirstSeenTimestamp>true</EnableFirstSeenTimestamp>
<EnableLastSeenTimestamp>false</EnableLastSeenTimestamp>
<EnableTagSeenCount>false</EnableTagSeenCount>
<EnableAccessSpecID>false</EnableAccessSpecID>
<C1G2EPCMemorySelector>
<EnableCRC>false</EnableCRC>
<EnablePCBits>false</EnablePCBits>
</C1G2EPCMemorySelector>
</TagReportContentSelector>
<Impinj:ImpinjTagReportContentSelector
xmlns=“http://developer.impinj.com/ltk/schema/
encoding/xml/1.8”>
<ImpinjEnableSerializedTID>
<SerializedTIDMode>Disabled</SerializedTIDMode>
</ImpinjEnableSerializedTID>
<ImpinjEnableRFPhaseAngle>
<RFPhaseAngleMode>Enabled</RFPhaseAngleMode>
</ImpinjEnableRFPhaseAngle>
<ImpinjEnablePeakRSSI>
<PeakRSSIMode>Enabled</PeakRSSIMode>
</ImpinjEnablePeakRSSI>
</Impinj:ImpinjTagReportContentSelector>
</ROReportSpec>
</SET_READER_CONFIG>
10.3.2 LTKNET
Using XML to set the configuration in LTKNET is identical to the previous examples.
10.3.3 LTKCPP
/* lets turn off off report data that we don’t need since our use
** case suggests we are bandwidth constrained */
CTagReportContentSelector *pROcontent = new CTagReportContentSe-
lector();
pROcontent->setEnableAccessSpecID(false);
/* these are very handy to have with low level data */
pROcontent->setEnableAntennaID(true);
pROcontent->setEnableChannelIndex(true);
pROcontent->setEnableFirstSeenTimestamp(true);
pROcontent->setEnableInventoryParameterSpecID(false);
pROcontent->setEnableLastSeenTimestamp(false);
pROcontent->setEnablePeakRSSI(false);
pROcontent->setEnableROSpecID(false);
pROcontent->setEnableSpecIndex(false);
pROcontent->setEnableTagSeenCount(false);
CC1G2EPCMemorySelector *pC1G2Mem = new CC1G2EPCMemorySelector();
pC1G2Mem->setEnableCRC(false);
pC1G2Mem->setEnablePCBits(false);
pROcontent->addAirProtocolEPCMemorySelector(pC1G2Mem);
pROrs->setTagReportContentSelector(pROcontent);
/* Turn on the low level phase data in the ImpinjTagContentSelector*/
CImpinjTagReportContentSelector * pImpTagCnt = new
CImpinjTagReportContentSelector();
CImpinjEnableRFPhaseAngle();
pEnableRfPhase->setRFPhaseAngleMode(ImpinjRFPhaseAngleMode_Enabled);
pImpTagCnt->setImpinjEnableRFPhaseAngle(pEnableRfPhase);
CImpinjEnablePeakRSSI * pEnablePeakRssi = new CImpinjEnable-
PeakRSSI();
pEnablePeakRssi->setPeakRSSIMode(ImpinjPeakRSSIMode_Enabled);
pImpTagCnt->setImpinjEnablePeakRSSI(pEnablePeakRssi);
CImpinjEnableSerializedTID * pEnableSerializedTID = new
CImpinjEnableSerializedTID();
pEnableSerializedTID->setSerializedTIDMode(ImpinjSerializedTIDMode_Disabled);
pImpTagCnt->setImpinjEnableSerializedTID(pEnableSerializedTID);
pROrs->addCustom(pImpTagCnt);
10.3.4 LTKJAVA
Using XML to set the configuration in LTKJAVA is identical to the code in previous examples.
10.4.1 LTKCPP
Figure 10.3 shows how to get an individual tag report from the RO_ACCESS_REPORT message.
After this report is extracted, the application must examine the report to find the low-level data.
In the LTKCPP, custom parameters are stored as STL lists. For tag information that was
set through the ImpinjTagReportContentSelector, the data is reported via the CustomParame-
ter within the standard LLRP TagReportDataParameter. Note that the type of each custom
parameter is checked before the function is called to extract the relevant data.
LTKCPP
for(
Cur = pTagReportData->beginCustom();
Cur != pTagReportData->endCustom();
Cur++)
10.4.2 LTKNET
LTKNET stores tag report data information inside a custom parameter array. The application
should validate the type of the parameter before casting it back to a PARAM_ImpinjRFPhaseAngle.
LTKNET
if (msg.TagReportData[i].Custom != null)
{
10.4.3 LTKJAVA
LTKJAVA stores tag report data inside a custom Parameter list. The application should validate
the type of the parameter before casting it back from type custom. The code below shows a
snippet from the tag data processing function.
LTKJAVA
if (cd.getClass() == ImpinjRFPhaseAngle.class) {
epcString += ” ImpinjPhase: ” +
((ImpinjRFPhaseAngle) cd).getPhaseAngle().toString();
}
if(cd.getClass() == ImpinjPeakRSSI.class) {
epcString += ” ImpinjPeakRSSI: ” +
((ImpinjPeakRSSI) cd).getRSSI().toString();
}
11 Using Monza 4 QT
Monza 4 QT features allow control of public and private areas of the tag memory. It uses the
same AccessSpec model as shown in Section 9. For a description of the Monza 4 QT feature
set, see the reference in Section 1.6. The only new programming exercise to use Monza 4, aside
from understanding the functionality, utilizes custom AccessSpecs via the CustomParameter in the
LLRP standard, as discussed in this section.
11.1.1 LTKCPP
}
/* Add a set QT op Spec */
pAccessCommand->addAccessCommandOpSpec(psetQT);
11.1.2 LTKNET
LTKNET uses the Add method of the AccessCommandOpSpec class to add a custom OpSpec.
as shown in Figure 11.2.
LTKNET
11.2.1 LTKCPP
In LTKCPP, OpSpecResults are stored as STL lists. The application must iterate the list and
examine each type of result, and then act appropriately, as shown in Figure 11.3.
LTKCPP
for (
OpSpecResults = pTagReportData->beginAccessCommandOpSpecResult();
OpSpecResults != pTagReportData->endAccessCommandOpSpecResult();
OpSpecResults++)
if( (*OpSpecResults)->m_pType ==
&CImpinjSetQTConfigOpSpecResult::s_typeDescriptor)
{
written = formatOneSetQTConfigResult(*OpSpecResults,
ptr, bufLimit, “\n SETQT”);
ptr += written;
bufLimit-=written;
}
11.2.2 LTKNET
In LTKNET, the application must loop through the list of OpSpecResults and compare the type
of each one with the possible types available, as shown in Figure 11.4.
LTKNET
if ((msg.TagReportData[i].AccessCommandOpSpecResult != null))
{
12 xArray Gateway
xArray uses the LTK in a way similar to Speedway. The examples below show how to create an
ROSPEC and READER_CONFIGURATION for the Location role in XML format, as well
as examples of how to write event handlers in C# and Java.
• For optimal performance when using Location role: Use Mode index 1002 over
1000 on FCC and GX1 xArrays. Use Mode index 0 on ETSI and Japan xArrays.
• Get the Tilt configuration before running an RO_SPEC. If you get the Tilt config-
uration while an RO_SPEC is running, the value returned will be the value before
the RO_SPEC started.
• You should set the Beacon state before starting the RO_SPEC. The Beacon state
will not update until an RO_SPEC stops running.
<ImpinjLocationReporting>
<EnableUpdateReport>true</EnableUpdateReport>
<EnableEntryReport>true</EnableEntryReport>
<EnableExitReport>true</EnableExitReport>
<EnableDiagnosticReport>true</EnableDiagnosticReport>
</ImpinjLocationReporting>
</Impinj:ImpinjLISpec>
</ROSpec>
</ADD_ROSPEC>
<?xml version="1.0"?>
<SET_READER_CONFIG MessageID="0"
xmlns:Impinj="http://developer.impinj.com/ltk/schema/encoding/xml/1.20"
xmlns="http://www.llrp.org/ltk/schema/core/encoding/xml/1.0">
<ResetToFactoryDefault>false</ResetToFactoryDefault>
<ImpinjLocationConfig xmlns=
"http://developer.impinj.com/ltk/schema/encoding/xml/1.20">
<UpdateIntervalSeconds>1</UpdateIntervalSeconds>
<ComputeWindowSeconds>255</ComputeWindowSeconds>
<TagAgeIntervalSeconds>300</TagAgeIntervalSeconds>
</ImpinjLocationConfig>
<ImpinjPlacementConfiguration
xmlns="http://developer.impinj.com/ltk/schema/encoding/xml/1.20">
<HeightCm>400</HeightCm>
<FacilityXLocationCm>0</FacilityXLocationCm>
<FacilityYLocationCm>0</FacilityYLocationCm>
<OrientationDegrees>0</OrientationDegrees>
</ImpinjPlacementConfiguration>
<ImpinjC1G2LocationConfig
xmlns="http://developer.impinj.com/ltk/schema/encoding/xml/1.20">
<ModeIndex>0</ModeIndex>
<Session>2</Session>
</ImpinjC1G2LocationConfig>
</SET_READER_CONFIG>
LTKJAVA
// xArray reports one tag per report for location, so there is only one EPC
String output = "Location EPCs: ";
for (EPCData epc : tagInfo.getEPCDataList())
output += epc.getEPC() + " ";
// Set the confidence factors
output += " ReadCnt=" + tagInfo.getImpinjLocationReportData().
getImpinjLocationConfidence().getReadCount();
output += " Confs:";
for (int dataIndex = 0; dataIndex <
tagInfo.getImpinjLocationReportData().getImpinjLocationConfidence().
getConfidenceData().size(); dataIndex++)
output += tagInfo.getImpinjLocationReportData().getImpinjLocationConfidence().
getConfidenceData().get(dataIndex) + " ";
// Get the other location report parameters
output += " Xcm=" + tagInfo.getImpinjLocationReportData().getLocXCentimeters();
output += " Ycm=" + tagInfo.getImpinjLocationReportData().getLocYCentimeters();
output += " Type=" + tagInfo.getImpinjLocationReportData().getType();
output += " TS=" + getDateStringFromEpoch(
tagInfo.getImpinjLocationReportData().getTimestampUTC());
return output;
}
if (msg.Custom != null)
{
int numCustom = msg.Custom.Length;
for (int i = 0; i < numCustom; i++)
{
if (msg.Custom[i] is PARAM_ImpinjExtendedTagInformation)
{
PARAM_ImpinjExtendedTagInformation tagInfo =
(PARAM_ImpinjExtendedTagInformation)msg.Custom[i];
}
// Print out xArray Location report details.
private String ImpinjLocationOutput(PARAM_ImpinjExtendedTagInformation tag-
Info)
{
tagInfo.ImpinjLocationReportData.TimestampUTC).ToLocalTime();
return output;
13 Revision History
14 Notices
Copyright © 2017, Impinj, Inc. All rights reserved.
Impinj gives no representation or warranty, express or implied, for accuracy or reliability of infor-
mation in this document. Impinj reserves the right to change its products and services and this
information at any time without notice.
EXCEPT AS PROVIDED IN IMPINJ’S TERMS AND CONDITIONS OF SALE (OR AS OTH-
ERWISE AGREED IN A VALID WRITTEN INDIVIDUAL AGREEMENT WITH IMPINJ), IM-
PINJ ASSUMES NO LIABILITY WHATSOEVER AND IMPINJ DISCLAIMS ANY EXPRESS
OR IMPLIED WARRANTY, RELATED TO SALE AND/OR USE OF IMPINJ PRODUCTS IN-
CLUDING LIABILITY OR WARRANTIES RELATING TO FITNESS FOR A PARTICULAR
PURPOSE, MERCHANTABILITY, OR INFRINGEMENT.
NO LICENSE, EXPRESS OR IMPLIED, BY ESTOPPEL OR OTHERWISE, TO ANY PATENT,
COPYRIGHT, MASK WORK RIGHT, OR OTHER INTELLECTUAL PROPERTY RIGHT IS
GRANTED BY THIS DOCUMENT.
Impinj assumes no liability for applications assistance or customer product design. Customers
should provide adequate design and operating safeguards to minimize risks.
Impinj products are not designed, warranted or authorized for use in any product or application
where a malfunction may reasonably be expected to cause personal injury or death or property or
environmental damage (“hazardous uses”) or for use in automotive environments. Customers must
indemnify Impinj against any damages arising out of the use of Impinj products in any hazardous
or automotive uses.
Impinj, GrandPrix ™, Indy ®, Monza ®, Octane ™, QT ®, Speedway ®, STP ™, True3D ™,
xArray ®, and xSpan ® are trademarks or registered trademarks of Impinj, Inc. All other product
or service names are trademarks of their respective companies.
These products may be covered by one or more U.S. patents. See http://www.impinj.com/
patents for details.
For more information, contact [email protected]