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 install
make 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/ttyUSB1
int 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ドル.00
void 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/ttyUSB0
The 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 libict
MIT 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