TypeScript icon, indicating that this package has built-in type declarations

1.0.1 • Public • Published


Low Level Reader Protocol (LLRP) library for node.

Synopsis & Motivation

LLRP (Low Level Reader Protocol) is a binary protocol published by EPCglobal Inc. to define the standard communication interface between RFID readers (servers) and clients. For more infomration, check out the LLRP standard definition.

The LLRP ToolKit (LTK) project was created back in 2008 by a group of companies and universities to help the development of LLRP-based applications. The toolkit provided support for many languages including C, C++, Java, Perl, etc. A year later, however, Node.js came around and opened the door to countless possibilities and proved to be one of the easiest and fastest ways to IoT development.

LLRP makes the perfect use case for JavaScript, given that it's a message-oriented protocol with many messages that are best handled asynchronously. See reader reports handling in the example below.


llrpjs helps create programs for both servers (readers) and clients. The following example shows how to connect to a reader and collect EPC data of RFID tags.

const { LLRPClient, LLRPCore } = require("llrpjs");
const ADD_ROSPEC = require("./ADD_ROSPEC.json");

// create a client
const reader = new LLRPClient({ host: "" });

// print RFID tags
reader.on("RO_ACCESS_REPORT", msg => {
    let tagReportDataList = msg.getTagReportData();
    if (!Array.isArray(tagReportDataList)) {
        tagReportDataList = [tagReportDataList]
    for (let tagReportData of tagReportDataList) {
        let epc = tagReportData.getEPCParameter();
        console.log(`EPC: ${epc.getEPC()}`);
reader.on("READER_EVENT_NOTIFICATION", msg => {
    // handle reader event notification messages here

reader.on("error", err => {
    // handle errors
reader.on("connect", () => {
reader.on("disconnect", () => {

/** Main */
(async () => {
    try {
        // connect to reader
        await reader.connect();
        // wait for connection confirmation
        await checkConnectionStatus();

        // delete all existing ROSpecs (if any)
        await reader.transact(new LLRPCore.DELETE_ROSPEC({
            data: { ROSpecID: 0 /** all */ }

        // factory reset
        await reader.transact(new LLRPCore.SET_READER_CONFIG({
            data: { ResetToFactoryDefault: true }

        // add ROSpec defined in "./ADD_ROSPEC.json"
        await reader.transact(new LLRPCore.ADD_ROSPEC(ADD_ROSPEC));

        // enable ROSpec
        const { ROSpecID } = ADD_ROSPEC.data.ROSpec;
        await reader.transact(new LLRPCore.ENABLE_ROSPEC({
            data: { ROSpecID }

        // start ROSpec
        await reader.transact(new LLRPCore.START_ROSPEC({
            data: { ROSpecID }

        console.log("Press ctrl+c to exit");
    } catch (e) {

 * wait for a READER_EVENT_NOTIFICATION message
const checkConnectionStatus = async () => {
    let msg = await reader.recv(7000);
    if (!(msg instanceof LLRPCore.READER_EVENT_NOTIFICATION)) {
        throw new Error(`connection status check failed - unexpected message ${msg.getName()}`);
    const status = msg.getReaderEventNotificationData().getConnectionAttemptEvent()?.getStatus();
    if (status != "Success") {
        throw new Error(`connection status check failed ${status}`);

process.on("SIGINT", async () => {
    // request termination
    const rsp = await reader.transact(new LLRPCore.CLOSE_CONNECTION);
    if (rsp instanceof LLRPCore.CLOSE_CONNECTION_RESPONSE) {
        const status = rsp.getLLRPStatus();
        console.log(`${status.getErrorDescription()} - ${status.getStatusCode()}`)
    // make sure it's disconnected
    if (reader.socketWritable) await reader.disconnect();

Static typing

llrpjs is written in TypeScript and provides static typing support:

JSON and JSON Schema support

llrpjs commits to the same goals set by the LTK project. However, llrpjs attempts at accomplishing some of these goals differently:


The LTK project used XML to represent human-readable LLRP documents. However, given that JSON has out-of-the-box support in Node.js as well as many APIs and document-oriented databases (e.g. MongoDB), it makes sense to use it over XML in llrpjs. llrpjs uses a one-to-one mapping of the same XML format used in LTK:

llrpjs JSON (full example)

  "$schema": "https://llrpjs.github.io/schema/core/encoding/json/1.0/llrp-1x0.schema.json",
  "id": 103, "type": "ADD_ROSPEC",
  "data": {
    "ROSpec": {
      "ROSpecID": 1,
      "Priority": 0,
      "CurrentState": "Disabled",
      // ...

LTK XML (full example)

<ADD_ROSPEC MessageID='103'
    <!-- ... -->

JSON Schema over XSD

The LLRP JSON schema is generated from the source LTK definition and supports all types and formats. VSCode supports JSON schema out of the box:

CLI Tools

On installation, the package makes two CLI programs available: llrp2json and json2llrp. These commands can be used to convert binary llrp messages to JSON and the other way around. Use --help flag to get information on how to use.


llrpjs uses the same definition llrp-1x0-def.xml set by the LTK project to generate runtime type definitions and schema. This library is written in TypeScript and allows dynamic static typing using a given LLRP definition. That said, llrpjs in itself is definition-agnostic, meaning that the code itself only conforms to the meta schema that was set by the LTK project (llrpdef.xsd) and any definition that complies with the meta schema can be used to generate types for llrpjs.

Check out llrpjs-tools for more information.


Haytham Halimeh


Contributions, issues and feature requests are welcome!
Feel free to check issues page.


  • Tool to convert from llrpjs JSON to LTK XML and vice versa
  • Vendor definitions support
  • Documentation


This project is independent.

Package Sidebar


npm i llrpjs

Weekly Downloads






Unpacked Size

897 kB

Total Files


Last publish


  • haytham43