Finish full HID descriptor, implement trigger report handling
This commit is contained in:
parent
b58c9c844f
commit
82566d09bf
@ -35,7 +35,7 @@ from sys import stdin, exit
|
|||||||
from _thread import start_new_thread
|
from _thread import start_new_thread
|
||||||
import machine
|
import machine
|
||||||
from machine import Pin, USBDevice
|
from machine import Pin, USBDevice
|
||||||
from utime import sleep
|
from utime import sleep, sleep_ms, sleep_us
|
||||||
import usb.device
|
import usb.device
|
||||||
from micropython import const
|
from micropython import const
|
||||||
from usb.device.hid import HIDInterface
|
from usb.device.hid import HIDInterface
|
||||||
@ -43,6 +43,8 @@ from usb.device.hid import HIDInterface
|
|||||||
print("PostalPoint(r) Barcode Scanner")
|
print("PostalPoint(r) Barcode Scanner")
|
||||||
print("Firmware version 0.0.1")
|
print("Firmware version 0.0.1")
|
||||||
|
|
||||||
|
onboardLED = Pin("LED", Pin.OUT)
|
||||||
|
|
||||||
|
|
||||||
# VID and PID of the USB device.
|
# VID and PID of the USB device.
|
||||||
VID = 0x1209 # USB Vendor ID
|
VID = 0x1209 # USB Vendor ID
|
||||||
@ -79,9 +81,9 @@ class USBHIDInterface(HIDInterface):
|
|||||||
0x09, 0xFE, # Usage 0xFE (Decoded Data)
|
0x09, 0xFE, # Usage 0xFE (Decoded Data)
|
||||||
0x82, 0x02, 0x01, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Buffered Bytes)
|
0x82, 0x02, 0x01, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Buffered Bytes)
|
||||||
0x95, 0x02, # Report Count (2 bytes)
|
0x95, 0x02, # Report Count (2 bytes)
|
||||||
0x06, 0x66, 0xFF, # Usage Page (Vendor Defined 0xFF66)
|
0x06, 0x69, 0xFF, # Usage Page (Vendor Defined 0xFF69, everyone else does FF66)
|
||||||
0x09, 0x04, # Usage (0x04)
|
0x09, 0x04, # Usage (0x04)
|
||||||
0x09, 0x00, # Usage (0x00)
|
0x09, 0x05, # Usage (0x05)
|
||||||
0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||||
0x05, 0x8C, # Usage Page (Bar Code Scanner Page)
|
0x05, 0x8C, # Usage Page (Bar Code Scanner Page)
|
||||||
0x25, 0x01, # Logical Maximum (1)
|
0x25, 0x01, # Logical Maximum (1)
|
||||||
@ -90,17 +92,129 @@ class USBHIDInterface(HIDInterface):
|
|||||||
0x09, 0xFF, # Usage (0xFF, Decode Data Continued)
|
0x09, 0xFF, # Usage (0xFF, Decode Data Continued)
|
||||||
0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
0x81, 0x02, # Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||||
0xC0, # End Collection (Logical)
|
0xC0, # End Collection (Logical)
|
||||||
|
0x09, 0x14, # Usage (Trigger report)
|
||||||
|
0xA1, 0x02, # Collection (Logical)
|
||||||
|
0x85, 0x04, # Report ID (4, trigger report from host)
|
||||||
|
0x15, 0x00, # Logical Minimum (0)
|
||||||
|
0x25, 0x01, # Logical Maximum (1)
|
||||||
|
0x75, 0x01, # Report Size (1)
|
||||||
|
0x95, 0x08, # Report Count (8)
|
||||||
|
0x09, 0x00, # Usage (0x00, filler bit)
|
||||||
|
0x09, 0x5F, # Usage (0x5F, prevent read of barcodes)
|
||||||
|
0x09, 0x60, # Usage (0x60, initiate barcode read)
|
||||||
|
0x09, 0x00, # Usage (0x00, filler)
|
||||||
|
0x09, 0x84, # Usage (0x84, sound powerup beep)
|
||||||
|
0x09, 0x85, # Usage (0x85, sound error beep)
|
||||||
|
0x09, 0x86, # Usage (0x86, sound success beep)
|
||||||
|
0x09, 0x87, # Usage (0x87, sound not-on-file beep)
|
||||||
|
0x91, 0x82, # Output (Data,Var,Abs,Volatile)
|
||||||
|
0xC0, # End Collection (Logical)
|
||||||
|
0x06, 0x69, 0xFF, # Usage Page (Vendor 0xFF69)
|
||||||
|
0x09, 0x30, # Usage (Vendor-defined: Configuration report for setting up scan modes)
|
||||||
|
0xA1, 0x02, # Collection (Logical)
|
||||||
|
0x85, 0x05, # Report ID (5)
|
||||||
|
0x15, 0x00, # Logical Minimum (0)
|
||||||
|
0x26, 0xFF, 0x00, # Logical Maximum (255)
|
||||||
|
0x75, 0x08, # Report Size (8 bits)
|
||||||
|
0x95, 0x01, # Report Count (1 byte)
|
||||||
|
0x05, 0x01, # Usage Page (Generic Desktop)
|
||||||
|
0x09, 0x3B, # Usage (Byte Count)
|
||||||
|
0x91, 0x02, # Output (Data,Var,Abs)
|
||||||
|
0x95, 0x3E, # Report Count (62 bytes)
|
||||||
|
0x06, 0x69, 0xFF, # Usage Page (Vendor Defined 0xFF69)
|
||||||
|
0x09, 0x05, # Usage (0x05, Config ASCII Data)
|
||||||
|
0x92, 0x02, 0x01, # Output (Data,Var,Abs,Buffered Bytes)
|
||||||
|
0xC0, # End Collection (Logical)
|
||||||
|
0x06, 0x69, 0xFF, # Usage Page (Vendor 0xFF69)
|
||||||
|
0x09, 0x31, # Usage (Vendor-defined: Scan mode switch)
|
||||||
|
0xA1, 0x02, # Collection (Logical)
|
||||||
|
0x85, 0x55, # Report ID (0x55)
|
||||||
|
0x15, 0x00, # Logical Minimum (0)
|
||||||
|
0x26, 0xFF, 0x00, # Logical Maximum (255)
|
||||||
|
0x75, 0x08, # Report Size (8 bits)
|
||||||
|
0x95, 0x01, # Report Count (1 byte)
|
||||||
|
0x06, 0x69, 0xFF, # Usage Page (Vendor 0xFF69)
|
||||||
|
0x09, 0x55, # Usage (0x55)
|
||||||
|
0x91, 0x02, # Output (Data,Var,Abs)
|
||||||
|
0xC0, # End Collection (Logical)
|
||||||
|
0x06, 0x69, 0xFF, # Usage Page (Vendor 0xFF69)
|
||||||
|
0x09, 0xF1, # Usage (Vendor-defined: Firmware update trigger)
|
||||||
|
0xA1, 0x02, # Collection (Logical)
|
||||||
|
0x85, 0xF1, # Report ID (0xF1)
|
||||||
|
0x15, 0x00, # Logical Minimum (0)
|
||||||
|
0x26, 0xFF, 0x00, # Logical Maximum (255)
|
||||||
|
0x75, 0x08, # Report Size (8 bits)
|
||||||
|
0x95, 0x01, # Report Count (1 byte)
|
||||||
|
0x06, 0x69, 0xFF, # Usage Page (Vendor 0xFF69)
|
||||||
|
0x09, 0xF1, # Usage (0xF1)
|
||||||
|
0x91, 0x02, # Output (Data,Var,Abs)
|
||||||
|
0xC0, # End Collection (Logical)
|
||||||
0xC0, # End Collection (Application)
|
0xC0, # End Collection (Application)
|
||||||
|
#
|
||||||
|
# NOTES:
|
||||||
|
#
|
||||||
|
# Report ID 2 is 63-bytes to the host.
|
||||||
|
# Byte 0 is the number of characters of barcode data to send in this report.
|
||||||
|
# Bytes 1, 2, and 3 are the AIM barcode identifier string. Nobody really uses this, some cheap scanners fudge it to QR code.
|
||||||
|
# Bytes 4 through 60 are the barcode data. Padded with NUL (0x00) bytes if the barcode is shorter than the available space.
|
||||||
|
# Bytes 61 and 62 are vendor-specific, this isn't part of the HID spec but everyone does it (see report 5 below for what we do)
|
||||||
|
# because they're copying everyone else. Honeywell uses it for another symbology ID.
|
||||||
|
# Byte 63 is 0x01 (b00000001) if the barcode data couldn't fit in a single report;
|
||||||
|
# it means more reports are coming right away with the rest of the data.
|
||||||
|
# If byte 63 is 0x00 it means there's no more barcode to send and the host can process it now.
|
||||||
|
#
|
||||||
|
# Report ID 4 is a single byte from the host: 76543210
|
||||||
|
# If pos. 7, 6, 5, or 4 is 1, the host wants us to make a particular beep sound/light indication.
|
||||||
|
# If pos. 1 is 1, host wants us to not send any barcode data or do any scans.
|
||||||
|
# If pos. 2 is 1, host wants us to start reading for barcodes without the user pressing a button.
|
||||||
|
#
|
||||||
|
# Report ID 5 is 63 bytes from the host to this device.
|
||||||
|
# This is a custom report for this specific device.
|
||||||
|
# The first byte is the data length.
|
||||||
|
# The rest of it is a "list" of ASCII strings, delimited with a NUL byte.
|
||||||
|
# The strings are displayed on our display and can be switched between by the user.
|
||||||
|
# The index of the string (as sent by the host) is sent as the second vendor byte in report 2.
|
||||||
|
# This allows the user to select a scan context/UI mode in the program, and the program
|
||||||
|
# can switch to it automatically when a barcode is scanned in that mode.
|
||||||
|
#
|
||||||
|
# Report ID 0x55 is a single byte from the host.
|
||||||
|
# It tells us to change our scan context to a string index from the most recent Report 5 list,
|
||||||
|
# overriding the user's last selection. This is for when the user changed modes in software,
|
||||||
|
# to keep the state in sync between the scanner and PC.
|
||||||
|
#
|
||||||
|
# Report ID 0xF1 triggers the Pi Pico to switch to firmware update mode when the byte sent is also 0xF1.
|
||||||
|
# The Pico will re-enumerate and appear as a USB flash drive so the firmware file can be dropped in.
|
||||||
]),
|
]),
|
||||||
set_report_buf=bytearray(1),
|
set_report_buf=bytearray(64),
|
||||||
protocol=const(0x00),
|
protocol=const(0x00),
|
||||||
interface_str=INTERFACE,
|
interface_str=INTERFACE,
|
||||||
)
|
)
|
||||||
|
|
||||||
def on_set_report(self, report_data, _report_id, _report_type):
|
def on_set_report(self, report_data, report_id, report_type):
|
||||||
if report_data[0] == 0x50:
|
global inhibitScan, feedbackBuzzer
|
||||||
print("Entering firmware update mode, power cycle to undo. Goodbye for now!")
|
#createAndSendBarcodeReports(bytes([report_id, report_type, report_data[0], report_data[1], reverse_byte(report_data[1])]))
|
||||||
|
|
||||||
|
if report_id == 0x04:
|
||||||
|
report_byte = report_data[1]
|
||||||
|
# HID POS trigger report
|
||||||
|
# Disable/enable scanning based on bit flag
|
||||||
|
inhibitScan = (report_byte >> 1 & 1 == 1)
|
||||||
|
|
||||||
|
if (report_byte >> 7 & 1 == 1):
|
||||||
|
# not-on-file beep, 10000000
|
||||||
|
feedbackBuzzer("NAF")
|
||||||
|
elif (report_byte >> 6 & 1 == 1):
|
||||||
|
# success beep, 01000000
|
||||||
|
feedbackBuzzer("OK")
|
||||||
|
elif (report_byte >> 5 & 1 == 1):
|
||||||
|
# error beep, 00100000
|
||||||
|
feedbackBuzzer("ERR")
|
||||||
|
elif (report_byte >> 4 & 1 == 1):
|
||||||
|
# powerup beep, 00010000
|
||||||
|
feedbackBuzzer("POW")
|
||||||
|
|
||||||
|
elif report_id == 0xF1 and report_data[1] == 0xF1:
|
||||||
|
# Enter firmware update mode
|
||||||
machine.bootloader()
|
machine.bootloader()
|
||||||
|
|
||||||
def send_data(self, data=None):
|
def send_data(self, data=None):
|
||||||
@ -116,6 +230,47 @@ usb.device.get().active(False)
|
|||||||
usb.device.get().config(usbinterface, builtin_driver=True, manufacturer_str=MANU, product_str=PROD, id_vendor=VID, id_product=PID)
|
usb.device.get().config(usbinterface, builtin_driver=True, manufacturer_str=MANU, product_str=PROD, id_vendor=VID, id_product=PID)
|
||||||
usb.device.get().active(True)
|
usb.device.get().active(True)
|
||||||
|
|
||||||
|
|
||||||
|
# Host can set this true and we won't send any scans.
|
||||||
|
inhibitScan = False
|
||||||
|
|
||||||
|
# Pulse the LED on and off with brightness fade
|
||||||
|
def pulseLED(speed):
|
||||||
|
r = 600
|
||||||
|
if speed == "fast":
|
||||||
|
r = 300
|
||||||
|
elif speed == "normal":
|
||||||
|
r = 600
|
||||||
|
elif speed == "slow":
|
||||||
|
r = 1000
|
||||||
|
|
||||||
|
for i in range(0,r):
|
||||||
|
onboardLED.on()
|
||||||
|
sleep_us(i)
|
||||||
|
onboardLED.off()
|
||||||
|
sleep_us(r-i)
|
||||||
|
for i in range(0,r):
|
||||||
|
onboardLED.on()
|
||||||
|
sleep_us(r-i)
|
||||||
|
onboardLED.off()
|
||||||
|
sleep_us(i)
|
||||||
|
|
||||||
|
def feedbackBuzzer(feedback):
|
||||||
|
global onboardLED
|
||||||
|
if feedback == "OK":
|
||||||
|
pulseLED("fast")
|
||||||
|
elif feedback == "ERR":
|
||||||
|
pulseLED("fast")
|
||||||
|
pulseLED("fast")
|
||||||
|
pulseLED("fast")
|
||||||
|
elif feedback == "POW":
|
||||||
|
pulseLED("slow")
|
||||||
|
elif feedback == "NAF":
|
||||||
|
pulseLED("normal")
|
||||||
|
pulseLED("fast")
|
||||||
|
pulseLED("fast")
|
||||||
|
pulseLED("normal")
|
||||||
|
|
||||||
def send_data(d):
|
def send_data(d):
|
||||||
print(d)
|
print(d)
|
||||||
|
|
||||||
@ -127,7 +282,7 @@ def pad_data(chunk, size):
|
|||||||
return chunk[:size]
|
return chunk[:size]
|
||||||
return chunk + b'\x00' * (size - len(chunk))
|
return chunk + b'\x00' * (size - len(chunk))
|
||||||
|
|
||||||
def createAndSendReports(barcodeData):
|
def createAndSendBarcodeReports(barcodeData):
|
||||||
global usbinterface
|
global usbinterface
|
||||||
chunks = split(barcodeData, 56)
|
chunks = split(barcodeData, 56)
|
||||||
for i, chunk in enumerate(chunks):
|
for i, chunk in enumerate(chunks):
|
||||||
@ -140,11 +295,13 @@ def createAndSendReports(barcodeData):
|
|||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
feedbackBuzzer("POW")
|
||||||
while True:
|
while True:
|
||||||
sleep(1)
|
sleep(1)
|
||||||
#createAndSendReports(b'POSTALPOINTROCKS')
|
if inhibitScan == False:
|
||||||
createAndSendReports(b'very long barcode please to help very long barcode please to help very long barcode please to help very long barcode please to help very long barcode please to help')
|
feedbackBuzzer("OK")
|
||||||
sleep(10)
|
createAndSendBarcodeReports(b'test barcode')
|
||||||
|
sleep(2)
|
||||||
|
|
||||||
|
|
||||||
except KeyboardInterrupt: # trap Ctrl-C input
|
except KeyboardInterrupt: # trap Ctrl-C input
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user