Lomax
Posts: 212
Joined: Wed May 20, 2015 9:43 pm

Capturing keyboard input from USB barcode scanner

Sun Oct 23, 2016 2:55 pm

Hi all,

I have a USB barcode scanner which outputs the scanned barcode in ASCII, followed by a carriage return. I need to build a simple Python app that sits and waits for a barcode to be scanned, performs an action and goes back to waiting for the next code. Seems simple enough, but I've spent a lot of time searching, looking at examples and testing out bits of code, yet I've been unable to get it to work. Here's one example of what I've tried:

Code: Select all

import sys

fp = open("/dev/hidraw2", "rb")

try:
    while True:
        buffer = fp.read(8)
        for c in buffer:
            if ord(c) > 0:
                print ord(c)
                print "\n" 

except KeyboardInterrupt:
    fp.close()
While this will give me the barcode (if run as root) there are a number of issues; firstly, hidraw devices are only accessible by root, which makes me think I should be listening to something else. Secondly, this code expects a fixed length barcode (8), while I want it to capture everything up to the CR as a single string. Thirdly, I would prefer if this was event driven rather than in the main loop. For example, with GPIO inputs I can use something like the code below to call a given function every time a button is pressed:

Code: Select all

GPIO.add_event_detect(11, GPIO.RISING, bouncetime=250)
GPIO.add_event_callback(11, play_pause)
I am pretty new to Python and the Pi and would be very grateful for any assistance!

User avatar
paddyg
Posts: 2561
Joined: Sat Jan 28, 2012 11:57 am
Location: UK

Re: Capturing keyboard input from USB barcode scanner

Sun Oct 23, 2016 3:51 pm

have you tried using pyserial? That would make it easy to check if there was anything waiting to be read so wouldn't block your main loop.
also https://groups.google.com/forum/?hl=en-GB&fromgroups=#!forum/pi3d

Lomax
Posts: 212
Joined: Wed May 20, 2015 9:43 pm

Re: Capturing keyboard input from USB barcode scanner

Sun Oct 23, 2016 5:31 pm

paddyg wrote:have you tried using pyserial? That would make it easy to check if there was anything waiting to be read so wouldn't block your main loop.
Thank you, that looks interesting, particularly the (experimental) thread support. However, I'm not even sure which device I should be listening to; the barcode reader acts like a HID keyboard and doesn't appear as a ttyUSB* thing. From dmesg:

Code: Select all

[    8.453351] usb 1-1.3: new low-speed USB device number 5 using dwc2
[    8.579041] usb 1-1.3: New USB device found, idVendor=04b4, idProduct=0100
[    8.579100] usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[    8.579121] usb 1-1.3: Product: USB Keyboard I/F    
[    8.579139] usb 1-1.3: Manufacturer:        
[    8.600145] input:         USB Keyboard I/F     as /devices/platform/soc/20980000.usb/usb1/1-1/1-1.3/1-1.3:1.0/0003:04B4:0100.0003/input/input2
[    8.655309] hid-generic 0003:04B4:0100.0003: input,hidraw2: USB HID v1.00 Keyboard [        USB Keyboard I/F    ] on usb-20980000.usb-1.3/input0

User avatar
paddyg
Posts: 2561
Joined: Sat Jan 28, 2012 11:57 am
Location: UK

Re: Capturing keyboard input from USB barcode scanner

Sun Oct 23, 2016 10:37 pm

Hmm, doing it 'properly' might be a bit messy, you probably need to use ioctl see the complicated stuff that @rurwin wrote for pi3d https://github.com/tipam/pi3d/tree/master/pi3d/event

You might be able to botch something up by using a thread for the scanner reader. I've not tested this so it might have all kinds of errors but you should be able to figure out what I'm getting at by looking in the documentations...

Code: Select all

import threading
import time

def read_scanner(buff, flag):
  fp = open("/dev/hidraw2", "rb")
  while flag >= 0:
    if flag > 0:
      buff.append(fp.read(1))
    time.sleep(0.001)
  fp.close()

buff = bytearray()
flag = 1
t = threading.Thread(target=read_scanner, args=(buff, flag))
t.start()
N = 8

try:
  while True:
    if len(buff) >= N:
      flag = 0 # stop adding
      print([i for i in buff[:N]],'\n')
      buff = buff[N:]
      flag = 1
    time.sleep(0.001)

except KeyboardInterrupt:
  flag = -1
also https://groups.google.com/forum/?hl=en-GB&fromgroups=#!forum/pi3d

Lomax
Posts: 212
Joined: Wed May 20, 2015 9:43 pm

Re: Capturing keyboard input from USB barcode scanner

Sun Oct 23, 2016 11:44 pm

Thank you - that's all very interesting, but golly, quite complex too! All I want to do is to read a string from a scanner/keyboard/stdin/thing! I've come up with this monstrosity instead, which seems to work the way I want:

Code: Select all

try:
    while True:
        code = raw_input("Scan: ")
        # do something with the scanned code

except KeyboardInterrupt:
    print "\nExit"

Lomax
Posts: 212
Joined: Wed May 20, 2015 9:43 pm

Re: Capturing keyboard input from USB barcode scanner

Mon Oct 24, 2016 12:48 am

This is actually pretty close to the complete code for what I need to achieve:

Code: Select all

lp = open("/dev/usb/lp0", "w")

items = {
    "8725000174958": "Beer",
    "4043058010443": "Tobacco",
    "50226894": "Filters",
    "50008667": "Rizzla"
}
count = 0

try:
    while True:
        code = raw_input("Scan: ")
        if code in items:
            count += 1
            item = items.get(code)
            print("Added " + item)
            lp.write(item + "\n")
        else:
            print("We don't have that!")
        if count == 3:
            print("Basket full!")
            lp.write("\x1E") # cut & present
            lp.flush()
            count = 0

except KeyboardInterrupt:
    lp.close()
    print "\nExit"
The look-up data will be a little more complex, and will contain more items (~50) - this is just a proof of concept using items currently on my desk... :) The finished unit won't have a screen attached, so the "print" commands are not really needed, but then again why not. I might hook up a row of LEDs to show how "full" the "basket" is, and an inactivity timer to empty it and reset the count.

Return to “Python”