How can I get a String from HID device in Python with evdev?
Asked Answered
G

3

15

I am new to python but have experience with HID devices and evdev. I have a 2D barcode scanner which interfaces as HID device. The goal is to get the string from a QR code. I am able to recognize the scanner in Linux and even found its location in /dev/input.

I found evdev and have implemented the example below with my scanner. This is just the default code on their site. It reads the values but it prints long event codes with downs and ups. I can't see an easy way to turn this into string. All I want to do is read in a string from the HID scanner in Python. Any help or direction would be appreciated (maybe evdev isnt the answer).

Here is my current python code with some example output:

from evdev import *
dev = InputDevice('/dev/input/event1')

print(dev)

for event in dev.read_loop():
    if event.type == ecodes.EV_KEY:
        print(categorize(event))

Here is the output from some barcodes:

key event at 1383327570.147000, 2 (KEY_1), down
key event at 1383327570.147990, 2 (KEY_1), up
key event at 1383327570.148997, 3 (KEY_2), down
key event at 1383327570.150010, 3 (KEY_2), up
key event at 1383327570.151009, 29 (KEY_LEFTCTRL), down
key event at 1383327570.151009, 42 (KEY_LEFTSHIFT), down
key event at 1383327570.152017, 36 (KEY_J), down
key event at 1383327570.153005, 36 (KEY_J), up
key event at 1383327570.154004, 29 (KEY_LEFTCTRL), up
key event at 1383327570.155005, 32 (KEY_D), down
key event at 1383327570.155993, 32 (KEY_D), up
key event at 1383327570.157002, 48 (KEY_B), down
key event at 1383327570.158015, 48 (KEY_B), up
key event at 1383327570.158997, 48 (KEY_B), down
key event at 1383327570.282002, 18 (KEY_E), up
key event at 1383327570.283004, 49 (KEY_N), down
key event at 1383327570.284005, 49 (KEY_N), up
key event at 1383327570.284968, 18 (KEY_E), down

Many thanks!

Granitite answered 1/11, 2013 at 18:25 Comment(0)
M
19

There's a conversion step you're missing here. Your output is already in a pretty format, so i'll help you break it down a little more:

             Timestamp        , scancode, keycode, keystate
key event at 1383327570.147000, 2         (KEY_1), down
key event at 1383327570.147990, 2         (KEY_1), up

To make any useful sense of this, you need to do a couple of things:

  1. Only listen to key_down type events by filter only for keystate of a specific type (Down = 1, Up = 0)
  2. Convert the scancode into a ASCII code, which can vary by device and vary by how it's mapped to the system!

There's a simple-ish way to map them however. Generate a known barcode with all useable characters using an online service, then scan that barcode and map each scancode outputted to the correct letter/number for your scanner. You can use the following slightly modified piece of code to take better control of the output:

import evdev
from evdev import InputDevice, categorize  # import * is evil :)
dev = InputDevice('/dev/input/event1')

# Provided as an example taken from my own keyboard attached to a Centos 6 box:
scancodes = {
    # Scancode: ASCIICode
    0: None, 1: u'ESC', 2: u'1', 3: u'2', 4: u'3', 5: u'4', 6: u'5', 7: u'6', 8: u'7', 9: u'8',
    10: u'9', 11: u'0', 12: u'-', 13: u'=', 14: u'BKSP', 15: u'TAB', 16: u'Q', 17: u'W', 18: u'E', 19: u'R',
    20: u'T', 21: u'Y', 22: u'U', 23: u'I', 24: u'O', 25: u'P', 26: u'[', 27: u']', 28: u'CRLF', 29: u'LCTRL',
    30: u'A', 31: u'S', 32: u'D', 33: u'F', 34: u'G', 35: u'H', 36: u'J', 37: u'K', 38: u'L', 39: u';',
    40: u'"', 41: u'`', 42: u'LSHFT', 43: u'\\', 44: u'Z', 45: u'X', 46: u'C', 47: u'V', 48: u'B', 49: u'N',
    50: u'M', 51: u',', 52: u'.', 53: u'/', 54: u'RSHFT', 56: u'LALT', 100: u'RALT'
}
for event in dev.read_loop():
    if event.type == evdev.ecodes.EV_KEY:
        data = evdev.categorize(event)  # Save the event temporarily to introspect it
        if data.keystate == 1:  # Down events only
            key_lookup = scancodes.get(data.scancode) or u'UNKNOWN:{}'.format(data.scancode)  # Lookup or return UNKNOWN:XX
            print u'You Pressed the {} key!'.format(key_lookup)  # Print it all out!

Here's some sample output from this script for me

You Pressed the A key!
You Pressed the B key!
You Pressed the C key!
You Pressed the UNKNOWN:99 key!

Once you generate some barcodes online, you'll know which scancode gets mapped to which value! Build your own table and profit!

HTH

Metagalaxy answered 2/11, 2013 at 0:33 Comment(3)
This is exactly what I needed. Thank you! However, now that I have gotten to the next stage of testing, I have found that this code does not work with barcodes beyond about 10 characters. The scanner still works fine when plugged into a pc and just typing into an empty text window. But with this it will randomly drop most of the characters in a barcode. Is this a speed problem? Or am I doing something else wrong?Granitite
I fixed this issue by adding the evdev grab function in right before starting to loop. For the code above I used dev.grab() right above the event_loop for loop. I also added my final code to my own answer.Granitite
The codes that you refer to as "scancodes" in your script are actually a "keycodes". See wiki.archlinux.org/title/Keyboard_input#Identifying_scancodesGoldenrod
G
13

VooDooNOFX's code was hugely helpful and kicked me in the right direction. I wanted to answer my own question to add clarity for anyone else trying to interface a Honeywell MS7580 or similar optical scanner with keyboard emulation with python on linux.

This project was done using a BeagleBone Black where I had only command line access and thus could not just use the scanner as a regular keyboard. In order to process the shifts properly I had to add a second group of scancodes for when the shift button was depressed. Here is my final code for reading a string from a QR code:

import evdev
from evdev import InputDevice, categorize, ecodes  
dev = InputDevice('/dev/input/event1')

# Provided as an example taken from my own keyboard attached to a Centos 6 box:
scancodes = {
    # Scancode: ASCIICode
    0: None, 1: u'ESC', 2: u'1', 3: u'2', 4: u'3', 5: u'4', 6: u'5', 7: u'6', 8: u'7', 9: u'8',
    10: u'9', 11: u'0', 12: u'-', 13: u'=', 14: u'BKSP', 15: u'TAB', 16: u'q', 17: u'w', 18: u'e', 19: u'r',
    20: u't', 21: u'y', 22: u'u', 23: u'i', 24: u'o', 25: u'p', 26: u'[', 27: u']', 28: u'CRLF', 29: u'LCTRL',
    30: u'a', 31: u's', 32: u'd', 33: u'f', 34: u'g', 35: u'h', 36: u'j', 37: u'k', 38: u'l', 39: u';',
    40: u'"', 41: u'`', 42: u'LSHFT', 43: u'\\', 44: u'z', 45: u'x', 46: u'c', 47: u'v', 48: u'b', 49: u'n',
    50: u'm', 51: u',', 52: u'.', 53: u'/', 54: u'RSHFT', 56: u'LALT', 57: u' ', 100: u'RALT'
}

capscodes = {
    0: None, 1: u'ESC', 2: u'!', 3: u'@', 4: u'#', 5: u'$', 6: u'%', 7: u'^', 8: u'&', 9: u'*',
    10: u'(', 11: u')', 12: u'_', 13: u'+', 14: u'BKSP', 15: u'TAB', 16: u'Q', 17: u'W', 18: u'E', 19: u'R',
    20: u'T', 21: u'Y', 22: u'U', 23: u'I', 24: u'O', 25: u'P', 26: u'{', 27: u'}', 28: u'CRLF', 29: u'LCTRL',
    30: u'A', 31: u'S', 32: u'D', 33: u'F', 34: u'G', 35: u'H', 36: u'J', 37: u'K', 38: u'L', 39: u':',
    40: u'\'', 41: u'~', 42: u'LSHFT', 43: u'|', 44: u'Z', 45: u'X', 46: u'C', 47: u'V', 48: u'B', 49: u'N',
    50: u'M', 51: u'<', 52: u'>', 53: u'?', 54: u'RSHFT', 56: u'LALT',  57: u' ', 100: u'RALT'
}
#setup vars
x = ''
caps = False

#grab provides exclusive access to the device
dev.grab()

#loop
for event in dev.read_loop():
    if event.type == ecodes.EV_KEY:
        data = categorize(event)  # Save the event temporarily to introspect it
        if data.scancode == 42:
            if data.keystate == 1:
                caps = True
            if data.keystate == 0:
                caps = False
        if data.keystate == 1:  # Down events only
            if caps:
                key_lookup = u'{}'.format(capscodes.get(data.scancode)) or u'UNKNOWN:[{}]'.format(data.scancode)  # Lookup or return UNKNOWN:XX
            else:
                key_lookup = u'{}'.format(scancodes.get(data.scancode)) or u'UNKNOWN:[{}]'.format(data.scancode)  # Lookup or return UNKNOWN:XX
            if (data.scancode != 42) and (data.scancode != 28):
                x += key_lookup  
            if(data.scancode == 28):
                print x          # Print it all out!
                x = ''

Thanks so much to VooDoo for pointing me in the right direction with evdev.

Granitite answered 3/11, 2013 at 19:59 Comment(0)
T
4

thanks a lot ,it really helpful. and here is the python3 version:

import evdev
from evdev import *
import time 
#dev =evdev.InputDevice('/dev/input/by-id/usb-SM_SM-2D_PRODUCT_HID_KBW_APP-000000000-event-kbd')
dev =evdev.InputDevice('/dev/input/event1')
dev.grab()
print(dev)

# for event in dev.read_loop():
#     if event.type == ecodes.EV_KEY:
#         print(categorize(event))

scancodes = { 
    # Scancode: ASCIICode 
    0: None, 1: u'ESC', 2: u'1', 3: u'2', 4: u'3', 5: u'4', 6: u'5', 7: u'6', 8: u'7', 9: u'8', 
    10: u'9', 11: u'0', 12: u'-', 13: u'=', 14: u'BKSP', 15: u'TAB', 16: u'q', 17: u'w', 18: u'e', 19: u'r', 
    20: u't', 21: u'y', 22: u'u', 23: u'i', 24: u'o', 25: u'p', 26: u'[', 27: u']', 28: u'CRLF', 29: u'LCTRL', 
    30: u'a', 31: u's', 32: u'd', 33: u'f', 34: u'g', 35: u'h', 36: u'j', 37: u'k', 38: u'l', 39: u';', 
    40: u'"', 41: u'`', 42: u'LSHFT', 43: u'\\', 44: u'z', 45: u'x', 46: u'c', 47: u'v', 48: u'b', 49: u'n', 
    50: u'm', 51: u',', 52: u'.', 53: u'/', 54: u'RSHFT', 56: u'LALT', 57: u' ', 100: u'RALT' 
} 

capscodes = { 
    0: None, 1: u'ESC', 2: u'!', 3: u'@', 4: u'#', 5: u'$', 6: u'%', 7: u'^', 8: u'&', 9: u'*', 
    10: u'(', 11: u')', 12: u'_', 13: u'+', 14: u'BKSP', 15: u'TAB', 16: u'Q', 17: u'W', 18: u'E', 19: u'R', 
    20: u'T', 21: u'Y', 22: u'U', 23: u'I', 24: u'O', 25: u'P', 26: u'{', 27: u'}', 28: u'CRLF', 29: u'LCTRL', 
    30: u'A', 31: u'S', 32: u'D', 33: u'F', 34: u'G', 35: u'H', 36: u'J', 37: u'K', 38: u'L', 39: u':', 
    40: u'\'', 41: u'~', 42: u'LSHFT', 43: u'|', 44: u'Z', 45: u'X', 46: u'C', 47: u'V', 48: u'B', 49: u'N', 
    50: u'M', 51: u'<', 52: u'>', 53: u'?', 54: u'RSHFT', 56: u'LALT', 57: u' ', 100: u'RALT' 
} 
#setup vars 
x = '' 
caps = False 

#grab provides exclusive access to the device 


#loop 
for event in dev.read_loop(): 
    if event.type == ecodes.EV_KEY: 
     data = categorize(event) # Save the event temporarily to introspect it 
     if data.scancode == 42: 
      if data.keystate == 1: 
       caps = True 
      if data.keystate == 0: 
       caps = False 
     if data.keystate == 1: # Down events only 
      if caps: 
       key_lookup = u'{}'.format(capscodes.get(data.scancode)) or u'UNKNOWN:[{}]'.format(data.scancode) # Lookup or return UNKNOWN:XX 
      else: 
       key_lookup = u'{}'.format(scancodes.get(data.scancode)) or u'UNKNOWN:[{}]'.format(data.scancode) # Lookup or return UNKNOWN:XX 
      if (data.scancode != 42) and (data.scancode != 28): 
       x += key_lookup 
      if(data.scancode == 28): 
       print (x)   # Print it all out! 
       x = '' `enter code here`
Touber answered 21/12, 2020 at 8:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.