Credential Callbacks
Event-driven credential detection using OnCredentialPresented. Instead of polling with GetCardData, register a callback that fires automatically when a card is presented to the reader.
Single Reader, Single Callback
The simplest case -- one reader, one callback function.
- C++
- C#
- Python
#include <iostream>
#include <thread>
#include <chrono>
#include "Reader/Reader.h"
int main() {
using namespace Rik;
using namespace RikCommon;
ReaderHandle handle = nullptr;
try {
ReaderDefinition readerDef{};
readerDef.DeviceId.VendorId = 0x0C27;
readerDef.DeviceId.ProductId = 0x3BFA;
readerDef.ProtocolType = PROTOCOL_TYPE_FEATURE_REPORT;
handle = AbstractReader::CreateReaderInstance(readerDef, 3);
auto* reader = dynamic_cast<Reader*>(
AbstractReader::GetInstance(handle)
);
reader->Init();
// Register a credential callback
auto subId = reader->OnCredentialPresented([](CardData& card) {
std::cout << "Card detected (" << card.GetBitCount()
<< " bits): " << card.AsString() << std::endl;
});
// Main thread keeps running while callbacks fire in the background
std::cout << "Listening for cards... Press Ctrl+C to exit." << std::endl;
while (true) {
std::this_thread::sleep_for(std::chrono::seconds(1));
}
// Cleanup (unreachable in this example, but shown for completeness)
reader->UnsubscribeCredentialCallback(subId);
reader->Close();
AbstractReader::DestroyInstance(handle);
return 0;
} catch (const ReaderException& e) {
std::cerr << "Error: " << e.Message << std::endl;
if (handle) AbstractReader::DestroyInstance(handle);
return 1;
}
}
using rfIDEAS.ReaderIntegrationKit;
using rfIDEAS.ReaderIntegrationKit.Objects;
using rfIDEAS.ReaderIntegrationKit.Enum;
using rfIDEAS.ReaderIntegrationKit.Exceptions;
try
{
var readerDef = new ReaderDefinition
{
DeviceId = new DeviceId { VendorId = 0x0C27, ProductId = 0x3BFA },
ProtocolType = ProtocolType.FeatureReport
};
using var reader = new Reader(readerDef);
reader.Init();
// Register a credential callback
var subId = reader.OnCredentialPresented((CardData card) =>
{
Console.WriteLine($"Card detected ({card.BitCount} bits): {card.AsHexString()}");
});
// Main thread keeps running while callbacks fire in the background
Console.WriteLine("Listening for cards... Press Ctrl+C to exit.");
Thread.Sleep(Timeout.Infinite);
}
catch (ReaderException ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
import time
import reader_integration_kit.facade as rik
from reader_integration_kit.structures import ReaderDefinition, DeviceId, SerialPortSettings
from reader_integration_kit.enum import ProtocolType
from reader_integration_kit.errors import ReaderException
def on_card(card_data):
print(f"Card detected ({card_data.bit_count} bits): {card_data.as_hex_string()}")
try:
reader_def = ReaderDefinition(
DeviceId=DeviceId(VendorId=0x0C27, ProductId=0x3BFA),
ProtocolType=ProtocolType.FEATURE_REPORT,
SerialPortSettings=SerialPortSettings()
)
with rik.Reader(reader_def) as reader:
reader.init()
# Register a credential callback
sub_id = reader.on_credential_presented(on_card)
# Main thread keeps running while callbacks fire in the background
print("Listening for cards... Press Ctrl+C to exit.")
while True:
time.sleep(1)
except KeyboardInterrupt:
pass
except ReaderException as e:
print(f"Error: {e.message}")
Multiple Callbacks on One Reader
Register several callbacks on the same reader. Each callback receives its own subscription ID and fires independently. This is useful when different parts of your application need to react to the same card event -- for example, one callback logs the event while another updates a UI.
- C++
- C#
- Python
#include <iostream>
#include <thread>
#include <chrono>
#include "Reader/Reader.h"
int main() {
using namespace Rik;
using namespace RikCommon;
ReaderHandle handle = nullptr;
try {
ReaderDefinition readerDef{};
readerDef.DeviceId.VendorId = 0x0C27;
readerDef.DeviceId.ProductId = 0x3BFA;
readerDef.ProtocolType = PROTOCOL_TYPE_FEATURE_REPORT;
handle = AbstractReader::CreateReaderInstance(readerDef, 3);
auto* reader = dynamic_cast<Reader*>(
AbstractReader::GetInstance(handle)
);
reader->Init();
// Callback 1: log the raw data
auto logSub = reader->OnCredentialPresented([](CardData& card) {
std::cout << "[LOG] " << card.GetBitCount()
<< " bits: " << card.AsString() << std::endl;
});
// Callback 2: count total scans
int scanCount = 0;
auto countSub = reader->OnCredentialPresented([&scanCount](CardData& card) {
scanCount++;
std::cout << "[COUNT] Total scans: " << scanCount << std::endl;
});
// Callback 3: alert on high-bit-count cards
auto alertSub = reader->OnCredentialPresented([](CardData& card) {
if (card.GetBitCount() > 48) {
std::cout << "[ALERT] High bit count card: "
<< card.GetBitCount() << std::endl;
}
});
std::cout << "Listening with 3 callbacks..." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(60));
// Unsubscribe all
reader->UnsubscribeCredentialCallback(logSub);
reader->UnsubscribeCredentialCallback(countSub);
reader->UnsubscribeCredentialCallback(alertSub);
reader->Close();
AbstractReader::DestroyInstance(handle);
return 0;
} catch (const ReaderException& e) {
std::cerr << "Error: " << e.Message << std::endl;
if (handle) AbstractReader::DestroyInstance(handle);
return 1;
}
}
using rfIDEAS.ReaderIntegrationKit;
using rfIDEAS.ReaderIntegrationKit.Objects;
using rfIDEAS.ReaderIntegrationKit.Enum;
using rfIDEAS.ReaderIntegrationKit.Exceptions;
try
{
var readerDef = new ReaderDefinition
{
DeviceId = new DeviceId { VendorId = 0x0C27, ProductId = 0x3BFA },
ProtocolType = ProtocolType.FeatureReport
};
using var reader = new Reader(readerDef);
reader.Init();
// Callback 1: log the raw data
var logSub = reader.OnCredentialPresented((CardData card) =>
{
Console.WriteLine($"[LOG] {card.BitCount} bits: {card.AsHexString()}");
});
// Callback 2: count total scans
var scanCount = 0;
var countSub = reader.OnCredentialPresented((CardData card) =>
{
Interlocked.Increment(ref scanCount);
Console.WriteLine($"[COUNT] Total scans: {scanCount}");
});
// Callback 3: alert on high-bit-count cards
var alertSub = reader.OnCredentialPresented((CardData card) =>
{
if (card.BitCount > 48)
Console.WriteLine($"[ALERT] High bit count card: {card.BitCount}");
});
Console.WriteLine("Listening with 3 callbacks...");
Thread.Sleep(TimeSpan.FromSeconds(60));
// Unsubscribe all
reader.UnsubscribeCredentialCallback(logSub);
reader.UnsubscribeCredentialCallback(countSub);
reader.UnsubscribeCredentialCallback(alertSub);
}
catch (ReaderException ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
import time
import reader_integration_kit.facade as rik
from reader_integration_kit.structures import ReaderDefinition, DeviceId, SerialPortSettings
from reader_integration_kit.enum import ProtocolType
from reader_integration_kit.errors import ReaderException
scan_count = 0
def log_card(card_data):
print(f"[LOG] {card_data.bit_count} bits: {card_data.as_hex_string()}")
def count_card(card_data):
global scan_count
scan_count += 1
print(f"[COUNT] Total scans: {scan_count}")
def alert_card(card_data):
if card_data.bit_count > 48:
print(f"[ALERT] High bit count card: {card_data.bit_count}")
try:
reader_def = ReaderDefinition(
DeviceId=DeviceId(VendorId=0x0C27, ProductId=0x3BFA),
ProtocolType=ProtocolType.FEATURE_REPORT,
SerialPortSettings=SerialPortSettings()
)
with rik.Reader(reader_def) as reader:
reader.init()
# Register all three callbacks
log_sub = reader.on_credential_presented(log_card)
count_sub = reader.on_credential_presented(count_card)
alert_sub = reader.on_credential_presented(alert_card)
print("Listening with 3 callbacks...")
time.sleep(60)
# Unsubscribe all
reader.unsubscribe_credential_callback(log_sub)
reader.unsubscribe_credential_callback(count_sub)
reader.unsubscribe_credential_callback(alert_sub)
except ReaderException as e:
print(f"Error: {e.message}")
Multiple Readers with Callbacks
Each reader instance has its own independent set of callbacks. This example connects two readers and registers a callback on each. The reader's metadata is captured in the callback closure so you can tell which reader detected the card.
- C++
- C#
- Python
#include <iostream>
#include <string>
#include <thread>
#include <chrono>
#include "Reader/Reader.h"
int main() {
using namespace Rik;
using namespace RikCommon;
ReaderHandle handle1 = nullptr;
ReaderHandle handle2 = nullptr;
try {
// Reader 1
ReaderDefinition readerDef1{};
readerDef1.DeviceId.VendorId = 0x0C27;
readerDef1.DeviceId.ProductId = 0x3BFA;
readerDef1.ProtocolType = PROTOCOL_TYPE_FEATURE_REPORT;
handle1 = AbstractReader::CreateReaderInstance(readerDef1, 3);
auto* reader1 = dynamic_cast<Reader*>(
AbstractReader::GetInstance(handle1)
);
reader1->Init();
std::string name1 = reader1->GetMetadataStruct().PartNumber;
// Reader 2
ReaderDefinition readerDef2{};
readerDef2.DeviceId.VendorId = 0x0C27;
readerDef2.DeviceId.ProductId = 0x3BFB;
readerDef2.ProtocolType = PROTOCOL_TYPE_FEATURE_REPORT;
handle2 = AbstractReader::CreateReaderInstance(readerDef2, 3);
auto* reader2 = dynamic_cast<Reader*>(
AbstractReader::GetInstance(handle2)
);
reader2->Init();
std::string name2 = reader2->GetMetadataStruct().PartNumber;
// Register callbacks -- capture the reader name for identification
auto sub1 = reader1->OnCredentialPresented(
[name1](CardData& card) {
std::cout << "[" << name1 << "] Card: "
<< card.AsString() << std::endl;
});
auto sub2 = reader2->OnCredentialPresented(
[name2](CardData& card) {
std::cout << "[" << name2 << "] Card: "
<< card.AsString() << std::endl;
});
std::cout << "Listening on " << name1 << " and "
<< name2 << "..." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(60));
// Cleanup
reader1->UnsubscribeCredentialCallback(sub1);
reader2->UnsubscribeCredentialCallback(sub2);
reader1->Close();
AbstractReader::DestroyInstance(handle1);
reader2->Close();
AbstractReader::DestroyInstance(handle2);
return 0;
} catch (const ReaderException& e) {
std::cerr << "Error: " << e.Message << std::endl;
if (handle1) AbstractReader::DestroyInstance(handle1);
if (handle2) AbstractReader::DestroyInstance(handle2);
return 1;
}
}
using rfIDEAS.ReaderIntegrationKit;
using rfIDEAS.ReaderIntegrationKit.Objects;
using rfIDEAS.ReaderIntegrationKit.Enum;
using rfIDEAS.ReaderIntegrationKit.Exceptions;
try
{
var readerDef1 = new ReaderDefinition
{
DeviceId = new DeviceId { VendorId = 0x0C27, ProductId = 0x3BFA },
ProtocolType = ProtocolType.FeatureReport
};
var readerDef2 = new ReaderDefinition
{
DeviceId = new DeviceId { VendorId = 0x0C27, ProductId = 0x3BFB },
ProtocolType = ProtocolType.FeatureReport
};
using var reader1 = new Reader(readerDef1);
reader1.Init();
var name1 = reader1.GetMetadata().PartNumber;
using var reader2 = new Reader(readerDef2);
reader2.Init();
var name2 = reader2.GetMetadata().PartNumber;
// Register callbacks -- capture the reader name for identification
var sub1 = reader1.OnCredentialPresented((CardData card) =>
{
Console.WriteLine($"[{name1}] Card: {card.AsHexString()}");
});
var sub2 = reader2.OnCredentialPresented((CardData card) =>
{
Console.WriteLine($"[{name2}] Card: {card.AsHexString()}");
});
Console.WriteLine($"Listening on {name1} and {name2}...");
Thread.Sleep(TimeSpan.FromSeconds(60));
// Cleanup
reader1.UnsubscribeCredentialCallback(sub1);
reader2.UnsubscribeCredentialCallback(sub2);
}
catch (ReaderException ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
import time
import reader_integration_kit.facade as rik
from reader_integration_kit.structures import ReaderDefinition, DeviceId, SerialPortSettings
from reader_integration_kit.enum import ProtocolType
from reader_integration_kit.errors import ReaderException
try:
reader_def1 = ReaderDefinition(
DeviceId=DeviceId(VendorId=0x0C27, ProductId=0x3BFA),
ProtocolType=ProtocolType.FEATURE_REPORT,
SerialPortSettings=SerialPortSettings()
)
reader_def2 = ReaderDefinition(
DeviceId=DeviceId(VendorId=0x0C27, ProductId=0x3BFB),
ProtocolType=ProtocolType.FEATURE_REPORT,
SerialPortSettings=SerialPortSettings()
)
with rik.Reader(reader_def1) as reader1:
reader1.init()
name1 = reader1.get_metadata().get("PartNumber")
with rik.Reader(reader_def2) as reader2:
reader2.init()
name2 = reader2.get_metadata().get("PartNumber")
# Register callbacks -- use closures to identify the reader
def make_callback(reader_name):
def on_card(card_data):
print(f"[{reader_name}] Card: {card_data.as_hex_string()}")
return on_card
sub1 = reader1.on_credential_presented(make_callback(name1))
sub2 = reader2.on_credential_presented(make_callback(name2))
print(f"Listening on {name1} and {name2}...")
time.sleep(60)
# Cleanup
reader1.unsubscribe_credential_callback(sub1)
reader2.unsubscribe_credential_callback(sub2)
except ReaderException as e:
print(f"Error: {e.message}")
Dynamic Subscribe / Unsubscribe
Callbacks can be added and removed at any time during the reader's lifetime. This is useful for temporarily enabling credential detection -- for example, only listening during a specific workflow step.
- C++
- C#
- Python
auto* reader = dynamic_cast<Reader*>(
AbstractReader::GetInstance(handle)
);
// Start listening
auto subId = reader->OnCredentialPresented([](CardData& card) {
std::cout << "Card: " << card.AsString() << std::endl;
});
// ... perform some workflow that needs credential detection ...
// Stop listening -- the reader is still connected, but the callback
// no longer fires
reader->UnsubscribeCredentialCallback(subId);
// ... do other work with the reader (configure, beep, etc.) ...
// Start listening again with a different callback
auto subId2 = reader->OnCredentialPresented([](CardData& card) {
std::cout << "Phase 2 card: " << card.AsString() << std::endl;
});
using var reader = new Reader(readerDef);
reader.Init();
// Start listening
var subId = reader.OnCredentialPresented((CardData card) =>
{
Console.WriteLine($"Card: {card.AsHexString()}");
});
// ... perform some workflow that needs credential detection ...
// Stop listening
reader.UnsubscribeCredentialCallback(subId);
// ... do other work with the reader ...
// Start listening again with a different callback
var subId2 = reader.OnCredentialPresented((CardData card) =>
{
Console.WriteLine($"Phase 2 card: {card.AsHexString()}");
});
with rik.Reader(reader_def) as reader:
reader.init()
# Start listening
sub_id = reader.on_credential_presented(
lambda card: print(f"Card: {card.as_hex_string()}")
)
# ... perform some workflow that needs credential detection ...
# Stop listening
reader.unsubscribe_credential_callback(sub_id)
# ... do other work with the reader ...
# Start listening again with a different callback
sub_id2 = reader.on_credential_presented(
lambda card: print(f"Phase 2 card: {card.as_hex_string()}")
)
Thread Safety Considerations
Credential callbacks fire on a library-managed background thread (one per reader). All callbacks registered on the same reader are invoked sequentially -- they will not run in parallel with each other.
However, if your callback accesses state that is also used by another thread (for example, updating a collection that the main thread reads), you need to synchronize that access yourself:
- C++
- C#
- Python
#include <iostream>
#include <mutex>
#include <vector>
#include "Reader/Reader.h"
std::mutex dataMutex;
std::vector<std::string> cardLog;
auto subId = reader->OnCredentialPresented([](CardData& card) {
std::lock_guard<std::mutex> lock(dataMutex);
cardLog.push_back(card.AsString());
std::cout << "Logged card #" << cardLog.size() << std::endl;
});
var cardLog = new List<string>();
var lockObj = new object();
var subId = reader.OnCredentialPresented((CardData card) =>
{
lock (lockObj)
{
cardLog.Add(card.AsHexString());
Console.WriteLine($"Logged card #{cardLog.Count}");
}
});
import threading
card_log = []
log_lock = threading.Lock()
def on_card(card_data):
with log_lock:
card_log.append(card_data.as_hex_string())
print(f"Logged card #{len(card_log)}")
sub_id = reader.on_credential_presented(on_card)
See Also
- Configuration & Features -- Beeper, LED, LUID, and other reader operations
- Multi-Reader -- Managing multiple reader instances
- Reader API (C++) -- Full method reference
- C# API -- C# method reference
- Python API -- Python method reference