平时用的最多的扫描枪通常只是一个简单的输入设备(好比键盘,鼠标), 另一头需要连接电脑, 用的 usb 或者串口. 扫描枪负责识别条码, 电脑收到后执行业务的逻辑. 有时候只是简单的数据采集工作, 数据传到服务器云端, 放台电脑在那儿显得浪费而且需要昂贵的维护. 而小巧灵活而且价格低廉的树莓派同学马上举手说: “我可以!”
可能你已经想到了, 用图形界面不是很 easy 吗? 是的, 但不是最好的办法. 即使目前最新版树莓派 4B 的性能对于图形界面来说只能算得上及格. 我们的目标是后台进程接受录入, 开机即用.
实验用到的设备
带 USB 接收器的条码扫描枪
树莓派 4b
我们将用到 python-evdev 程序库, 封装了对 usb 设备的读写操作. 官网 https://python-evdev.readthedocs.io/
安装 evdev
sudo pip install evdev
编写代码
简单几行代码检测 usb 设备:
#!/usr/bin/python3
import evdev
# 列出 usb 设备
devices = [evdev.InputDevice(path) for path in evdev.list_devices()]
print('发现设备: ')
for device in devices:
print(device.path, device.name, device.phys)
执行结果:
发现设备:
/dev/input/event0 Netum. HIDKB usb-0000:01:00.0-1.3/input0
显示成功读到了扫描枪设备. 接下来要读取扫描枪的输入了. 修改前面的代码
#!/usr/bin/python3
import asyncio, evdev
# 检测到输入时触发
async def print_events(device):
async for event in device.async_read_loop():
print(device.path, evdev.categorize(event), sep=': ')
# 列出 usb 设备
devices = [evdev.InputDevice(path) for path in evdev.list_devices()]
print('发现以下设备: ')
for device in devices:
print(device.path, device.name, device.phys)
for device in devices:
# 用 asyncio 同时接受多个设备的录入
asyncio.ensure_future(print_events(device))
loop = asyncio.get_event_loop()
loop.run_forever()
为了兼容多个设备的输入用到了
asyncio, 异步执行单个设备的事件循环, 这样就不会因为一个设备的输入 block 住其他设备.
运行看看效果:
发现以下设备:
/dev/input/event0 Netum. HIDKB usb-0000:01:00.0-1.3/input0
/dev/input/event0: event at 1584874459.655187, code 00, type 17, val 01
/dev/input/event0: event at 1584874459.655187, code 04, type 04, val 458787
/dev/input/event0: key event at 1584874459.655187, 7 (KEY_6), down
/dev/input/event0: synchronization event at 1584874459.655187, SYN_REPORT
/dev/input/event0: event at 1584874459.656157, code 04, type 04, val 458787
/dev/input/event0: key event at 1584874459.656157, 7 (KEY_6), up
/dev/input/event0: synchronization event at 1584874459.656157, SYN_REPORT
/dev/input/event0: event at 1584874459.657169, code 04, type 04, val 458790
/dev/input/event0: key event at 1584874459.657169, 10 (KEY_9), down
/dev/input/event0: synchronization event at 1584874459.657169, SYN_REPORT
/dev/input/event0: event at 1584874459.658158, code 04, type 04, val 458790
/dev/input/event0: key event at 1584874459.658158, 10 (KEY_9), up
………….
把每个按键的详细事件打印出来了. 说明成功接收到输入了. 还差一步, 把按键的内容转换成字符串. 继续修改代码:
#!/usr/bin/python3
import asyncio, evdev
# 按键转字符表 只列出了常用的字符
keymap = {
'KEY_0': u'0',
'KEY_1': u'1',
'KEY_2': u'2',
'KEY_3': u'3',
'KEY_4': u'4',
'KEY_5': u'5',
'KEY_6': u'6',
'KEY_7': u'7',
'KEY_8': u'8',
'KEY_9': u'9',
'KEY_A': u'A',
'KEY_B': u'B',
'KEY_C': u'C',
'KEY_D': u'D',
'KEY_E': u'E',
'KEY_F': u'F',
'KEY_G': u'G',
'KEY_H': u'H',
'KEY_I': u'I',
'KEY_J': u'J',
'KEY_K': u'K',
'KEY_L': u'L',
'KEY_M': u'M',
'KEY_N': u'N',
'KEY_O': u'O',
'KEY_P': u'P',
'KEY_Q': u'Q',
'KEY_R': u'R',
'KEY_S': u'S',
'KEY_T': u'T',
'KEY_U': u'U',
'KEY_V': u'V',
'KEY_W': u'W',
'KEY_X': u'X',
'KEY_Y': u'Y',
'KEY_Z': u'Z',
'KEY_TAB': u'\t',
'KEY_SPACE': u' ',
'KEY_COMMA': u',',
'KEY_SEMICOLON': u';',
'KEY_EQUAL': u'=',
'KEY_LEFTBRACE': u'[',
'KEY_RIGHTBRACE': u']',
'KEY_MINUS': u'-',
'KEY_APOSTROPHE': u'\'',
'KEY_GRAVE': u'`',
'KEY_DOT': u'.',
'KEY_SLASH': u'/',
'KEY_BACKSLASH': u'\\',
'KEY_ENTER': u'\n',
}
# 检测到输入时触发
async def print_events(device):
buf = ''
async for event in device.async_read_loop():
# key_up= 0 key_down= 1 key_hold= 2
if event.type == evdev.ecodes.EV_KEY and event.value == 1:
kv = evdev.events.KeyEvent(event)
# 本次修改的地方, 把事件映射到字符表
if (kv.scancode == evdev.ecodes.KEY_ENTER):
print('读到输入: ', buf)
'''
业务逻辑
'''
# 清空 buffer
buf = ''
else:
buf += keymap.get(kv.keycode)
devices = [evdev.InputDevice(path) for path in evdev.list_devices()]
print('发现以下设备: ')
for device in devices:
print(device.path, device.name, device.phys)
for device in devices:
asyncio.ensure_future(print_events(device))
loop = asyncio.get_event_loop()
loop.run_forever()
再来运行代码, 顺利拿到条码内容了.
增加点难度, 这次测试还加入了键盘, 效果棒棒哒.
以上实验全部代码都在 https://gitee.com/csling/ban-scanner
写在最后
如果你想把这个用到实际生产中, 然而还有许多问题要考虑, 比如动态添加和移除 usb 设备. 了解更多请关注我的公众号, 也欢迎留下你的想法.