Skip to content

bnutfilloyev/crt-c100-python

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

crt-c100-python

Pure Python library for the CRT C100-B11 bill acceptor (Creator Technology).

Implements the CCNet (CashCode NET) protocol directly over serial using pyserial. No proprietary .so or .dll dependency is required.

Features

  • Full CCNet protocol implementation (RESET, POLL, ENABLE, STACK, RETURN, HOLD, IDENTIFICATION, GET_BILL_TABLE)
  • High-level BillAcceptor class with context manager support
  • Synchronous polling or background threaded polling with callbacks
  • Thread-safe serial I/O
  • Bill table parsing with denomination lookup
  • Escrow mode (manual accept/reject) and auto-stack mode
  • CRC-16 (KERMIT) validation on all frames
  • No C library or proprietary SDK needed

Supported Hardware

Model Firmware Denominations
CRT C100-B11-UZS UZS (Uzbek Som) 1,000 -- 200,000 UZS

The library should work with other C100-B11 firmware variants (USD, EUR, RUB, CNY, etc.) since the CCNet protocol is the same. Only the bill table contents differ per variant.

Requirements

  • Python 3.7+
  • pyserial >= 3.4
  • Linux (tested on Ubuntu 20.04+, should work on any POSIX system)

Installation

pip install .

Or install directly in development mode:

pip install -e .

Quick Start

Synchronous Polling

from crt_c100 import BillAcceptor, DeviceState

with BillAcceptor("/dev/ttyUSB0") as ba:
    ba.initialize()

    while True:
        resp = ba.poll()
        if resp.state == DeviceState.ESCROW_POSITION:
            print("Bill in escrow:", ba.get_denomination_str(resp.bill_type))
            ba.stack()  # Accept the bill
        elif resp.state == DeviceState.BILL_STACKED:
            print("Accepted:", ba.get_denomination_str(resp.bill_type))

Threaded Polling with Callbacks

from crt_c100 import BillAcceptor

with BillAcceptor("/dev/ttyUSB0") as ba:
    ba.initialize()
    ba.auto_accept = True
    ba.on_bill_stacked = lambda e: print("Accepted:", e.bill_type)
    ba.start_polling()
    input("Press Enter to stop...")
    ba.stop_polling()

Query Device Information

from crt_c100 import BillAcceptor

with BillAcceptor("/dev/ttyUSB0") as ba:
    info = ba.identify()
    print("Model:", info.model)
    print("HW:", info.hardware_version)
    print("SW:", info.software_version)

    table = ba.get_bill_table()
    for bill in table.bills:
        print(f"  Slot {bill.index}: {bill}")

API Reference

BillAcceptor

The main high-level interface.

BillAcceptor(port, timeout=1.0, poll_interval=0.25)

Parameters:

  • port -- Serial port path (e.g., /dev/ttyUSB0, /dev/ttyS1)
  • timeout -- Serial read timeout in seconds
  • poll_interval -- Interval between poll commands in seconds

Methods:

Method Description
open() Open the serial connection
close() Stop polling and close the connection
initialize(bill_mask=0xFF, escrow_mode=True, reset_wait=5.0) Full init sequence: identify, reset, wait, load bill table, enable
poll() Single synchronous POLL command, returns PollResponse
stack() Accept the bill in escrow
return_bill() Reject the bill in escrow
hold() Extend escrow timeout
enable(bill_mask=0xFF, escrow_mode=True) Enable bill acceptance
disable() Disable bill acceptance
reset() Reset the device
identify() Query device identification
get_bill_table() Read the bill table from the device
get_denomination(bill_type) Look up denomination value by bill type index
get_denomination_str(bill_type) Get formatted denomination string
start_polling() Start background polling thread
stop_polling() Stop background polling thread

Callbacks:

Callback Triggered When
on_escrow Bill enters escrow position
on_bill_stacked Bill accepted and stacked
on_bill_returned Bill returned to customer
on_bill_rejected Bill rejected (with reason code)
on_state_change Any device state transition
on_error Cassette full/removed, jam, or cheat detected
on_failure Hardware failure

Properties:

Property Description
auto_accept Set to True to auto-stack bills in escrow (default False)
is_connected Whether the serial port is open
bill_table Loaded BillTable, or None
device_info Loaded DeviceIdentification, or None

DeviceState

Enum of all device states reported by POLL:

POWER_UP, POWER_UP_WITH_BILL, INITIALIZE, IDLING, ACCEPTING,
STACKING, RETURNING, UNIT_DISABLED, HOLDING, DEVICE_BUSY, REJECTING,
CASSETTE_FULL, CASSETTE_REMOVED, JAM_IN_ACCEPTOR, JAM_IN_STACKER,
CHEATED, PAUSE, FAILURE, ESCROW_POSITION, BILL_STACKED, BILL_RETURNED

Escrow Mode vs Auto-Stack

  • Escrow mode (escrow_mode=True, default): Bills are held in escrow. The host must call stack() to accept or return_bill() to reject. Use this when you need to validate the bill amount before accepting.

  • Auto-stack (escrow_mode=False): Valid bills are stacked automatically. The host is notified after the fact via BILL_STACKED state.

Enable Mask

The bill_mask parameter controls which denomination slots are enabled. Each bit corresponds to a slot index (0-7) in the bill table.

# Enable all 8 denominations:
ba.enable(bill_mask=0xFF)

# Enable only slots 0-6 (exclude slot 7):
ba.enable(bill_mask=0x7F)

# Compute mask from bill table (bills >= 10,000 only):
mask = ba.bill_table.get_enable_mask(min_value=10000)
ba.enable(bill_mask=mask)

Serial Connection

The CRT C100-B11 uses the following serial parameters:

Parameter Value
Baud rate 9600
Data bits 8
Parity None
Stop bits 1

On Linux, the device typically appears as /dev/ttyUSB0 (USB-to-serial adapter) or /dev/ttyS1 (onboard RS-232). The serial port requires root access or membership in the dialout group:

# Run as root:
sudo python my_script.py

# Or add user to dialout group (permanent):
sudo usermod -aG dialout $USER
# Log out and back in for the group change to take effect.

CCNet Protocol

The library implements the CCNet (CashCode NET) protocol as used by Creator Technology's C100-B11 bill acceptors. Key protocol details:

Frame format:

[SYNC=0x02] [ADR=0x03] [LNG] [CMD/DATA...] [CRC_L] [CRC_H]
  • SYNC: Always 0x02
  • ADR: Device address, always 0x03 for bill acceptors
  • LNG: Total frame length (including SYNC, ADR, LNG, and CRC bytes)
  • CRC: CRC-16/KERMIT (polynomial 0x08408), low byte first

Two-stage read: The receiver first reads 3 bytes (SYNC + ADR + LNG), then reads LNG - 3 remaining bytes. This matches the behavior of the manufacturer's C library.

Examples

See the examples/ directory:

  • basic_poll.py -- Synchronous polling loop
  • escrow_mode.py -- Manual accept/reject per bill
  • auto_accept.py -- Background thread with callbacks
  • device_info.py -- Query device info and bill table

Testing

pip install -e ".[dev]"
python -m pytest tests/ -v

Project Structure

crt_c100/
    __init__.py       Public API exports
    crc.py            CRC-16/KERMIT implementation
    constants.py      Protocol constants, status codes, timing
    exceptions.py     Exception hierarchy
    types.py          Data types (DeviceState, PollResponse, BillTable, ...)
    transport.py      Serial I/O, frame build/parse, CRC validation
    protocol.py       CCNet command layer
    acceptor.py       High-level BillAcceptor API

Architecture

BillAcceptor  (high-level, threads, callbacks)
    |
CCNetProtocol (commands: poll, reset, enable, stack, ...)
    |
CCNetTransport (serial I/O, frame build/parse, CRC)
    |
  pyserial   (raw serial port access)

License

MIT License. See LICENSE for details.

About

Pure Python CCNet protocol library for CRT C100-B11 bill acceptors. No proprietary .so/.dll required.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages