A C library for interfacing with ICT-compatible bill acceptors via serial communication (RS-232). This library provides a simple, event-driven API with automatic reconnection and background processing.
- Asynchronous operation with background thread
- Event-based callback system
- Automatic device reconnection
- Configurable bill denomination mapping
- Thread-safe API
- Comprehensive error handling
- Zero external dependencies (POSIX only)
- Clean, well-documented code
Implements the ICT bill acceptor serial protocol:
- Baud rate: 9600
- Data format: 8 bits, even parity, 1 stop bit (8E1)
- Protocol: ICT standard command set
- Linux or POSIX-compatible system
- GCC or compatible C compiler
- pthread library
- Serial port device (e.g.,
/dev/ttyUSB0)
# Clone the repository
git clone https://github.com/yetimdasturchi/libict.git
cd libict
# Build the library
make
# Install system-wide (optional)
sudo make installmake examples#include <stdio.h>
#include <unistd.h>
#include "ict.h"
void event_handler(const IctEvent *ev, void *user) {
switch (ev->type) {
case ICT_EVENT_READY:
printf("Device ready\n");
break;
case ICT_EVENT_ESCROW:
printf("Bill: %d\n", ev->bill.amount);
ict_escrow_accept(); // Accept the bill
break;
case ICT_EVENT_STACKED:
printf("Bill stacked!\n");
break;
default:
break;
}
}
int main(void) {
// Initialize
if (ict_init("/dev/ttyUSB0") != 0) {
perror("init failed");
return 1;
}
// Register callback
ict_add_listener(event_handler, NULL);
// Enable acceptor
ict_status(1);
// Wait for events...
sleep(60);
// Cleanup
ict_shutdown();
return 0;
}# Run with default device (/dev/ttyUSB0)
./build/demo
# Or specify a device
./build/demo /dev/ttyUSB1int ict_init(const char *device_path);Initialize the library and open the serial device. Pass NULL to use default /dev/ttyUSB0.
Returns 0 on success, -1 on error.
void ict_shutdown(void);Shut down the library, stop background thread, and close the device.
int ict_status(int enable);Enable (non-zero) or inhibit (zero) the bill acceptor.
int ict_escrow_accept(void);Accept the bill currently in escrow (call after receiving ICT_EVENT_ESCROW).
int ict_escrow_reject(void);Reject the bill currently in escrow.
int ict_escrow_hold(void);Hold the bill in escrow without stacking or returning.
void ict_set_bill_value(uint8_t bill_type, int amount);Override the currency value for a specific bill type.
Default mappings (suitable for Uzbek Sum):
0x40→ 1,0000x41→ 2,0000x42→ 5,0000x43→ 10,0000x44→ 50,000
Example for US Dollars:
ict_set_bill_value(0x40, 100); // $1.00
ict_set_bill_value(0x41, 500); // $5.00
ict_set_bill_value(0x42, 1000); // $10.00
ict_set_bill_value(0x43, 2000); // $20.00
ict_set_bill_value(0x44, 10000); // $100.00void ict_add_listener(IctListenerCb cb, void *user);
void ict_remove_listener(IctListenerCb cb, void *user);Register/unregister event callbacks. Callbacks are invoked from the background thread.
typedef void (*IctListenerCb)(const IctEvent *ev, void *user);typedef enum {
ICT_EVENT_READY, // Device initialized and ready
ICT_EVENT_ESCROW, // Bill in escrow, awaiting decision
ICT_EVENT_STACKED, // Bill accepted and stacked
ICT_EVENT_REJECTED, // Bill rejected
ICT_EVENT_ERROR // Device error occurred
} IctEventType;typedef enum {
ICT_ERR_NONE = 0x00,
ICT_ERR_MOTOR = 0x20,
ICT_ERR_CHECKSUM = 0x21,
ICT_ERR_BILL_JAM = 0x22,
ICT_ERR_BILL_REMOVE = 0x23,
ICT_ERR_STACKER_OPEN = 0x24,
ICT_ERR_SENSOR = 0x25,
ICT_ERR_BILL_FISH = 0x27,
ICT_ERR_STACKER = 0x28,
// ...
} IctError;┌─────────────────┐
│ Application │
│ (Your Code) │
└────────┬────────┘
│ API calls
│ Event callbacks
┌────────▼────────┐
│ libict │
│ ┌───────────┐ │
│ │Background │ │
│ │ Thread │ │
│ └─────┬─────┘ │
│ │ │
└────────┼────────┘
│ Serial I/O
┌────────▼────────┐
│ /dev/ttyUSB0 │
│ (Bill Acceptor)│
└─────────────────┘
- Main thread: Your application code
- Background thread: Handles serial I/O, protocol state machine, automatic reconnection
- Thread-safe: All public API functions are safe to call from any thread
# Add user to dialout group
sudo usermod -a -G dialout $USER
# Or set permissions
sudo chmod 666 /dev/ttyUSB0The library will automatically retry connection in the background. Check:
- Device is plugged in
- Device appears in
ls -l /dev/ttyUSB* - Driver is loaded:
lsmod | grep usb
- Ensure device is powered
- Check baud rate (should be 9600 8E1)
- Enable debug logging (see below)
All library messages are printed to stderr. Redirect or filter as needed:
./demo 2>&1 | grep libictMIT License - See LICENSE file for details
Got an idea or found a bug? Jump in:
- Fork this repository
- Create a feature branch
- Make your changes with clear commit messages
- Add or update tests if needed
- Open a pull request
- Asilbek Askarov – Project Lead, protocol whisperer and overall “does-this-even-work?” guy.
- Manuchehr Usmonov – Core Library Developer, responsible for making the ICT talk like a civilized device.
- Yusufjon Yunusov – Testing & QA, the one who happily breaks things so users don’t have to.
- Umidjon Nurmatov – Project Sponsor, keeping the project alive with real hardware, real bills and real support.
- Issues: https://github.com/yetimdasturchi/libict/issues
- Email: yetimdasturchi@gmail.com
- Telegram: https://t.me/yetimdasturchi
If this library saved you some time (or a few hours of fighting with the ICT manual :|) and you’d like to support the project:
- Visa:
4140 8400 0153 1195 - Uzcard/Humo:
8600 4929 5502 3508 - Local support: tirikchilik.uz/yetimdasturchi
- Buy me a coffee: buymeacoffee.com/yetimdasturchi
Donations are completely optional, but highly appreciated and help keep the hardware, tests and development going.
- Initial release
- Core protocol implementation
- Event-based API
- Automatic reconnection
- Example application