A USB barcode scanner that works the moment it meets a laptop can sit dead on a headless Linux controller: the reading service throws an uncaught "cannot open device" error, the HID node shows the scanner red and disconnected, and joining the service user to the input group changes nothing. The cause is almost never the reader or the runtime; it is a permissions setup that guards the wrong subsystem. This guide is for anyone wiring handheld or fixed readers to a screenless controller that logs batch scans and drives a green and red LED pair. You will identify the node the library actually opens, write the one udev rule that grants access, apply it without replugging, size the LED resistors, and choose between keyboard wedge and serial mode.
Symptom: the scanner reads at a desk and fails on the controller
The signature is consistent across runtimes. The node that wraps a HID library throws an uncaught "cannot open device" error, the flow shows the scanner input red with a Device disconnected status, and a scan does nothing. Run the same service once with sudo and the code decodes immediately. The reader itself is fine: lsusb lists it, the kernel log shows the HID interface attach, and the desk test worked. What fails is the open call on a device node, not decoding, not wiring and not the node library version.
Why the input group does not fix it
On current Debian-based systems, a shipped udev rule creates the evdev keyboard nodes at /dev/input/event* owned by root with group input and mode 0640. Joining the input group therefore grants exactly that subsystem. HID reader nodes, including the Node-RED USB HID nodes built on the node-hid library, open a different one: /dev/hidraw*, created by the kernel as root:root with mode 0600 and no shipped group rule. Your service user joined the group that guards nodes the library never opens.
Check it in two lines:
ls -l /dev/hidraw* # crw------- 1 root root
ls -l /dev/input/event0 # crw-rw---- 1 root input
The same debug logic — find the node the code touches, not the node you assume — is what the Linux camera device permissions guide applies to video nodes.
Likely causes in order
-
The hidraw node is root-only. Most likely on a first install. Test:
ls -l /dev/hidraw*prints mode 0600 and owner root, and the service works under sudo. -
The group was added but the service never restarted. Group membership is read when a process starts. Test:
id noderedshows the group, but the runtime process predates the change. Fix: restart the service. -
The rule file was written but never applied. Editing /etc/udev/rules.d does nothing to devices that are already plugged in, and restarting the udev service does not re-apply permissions either. Test: run
sudo udevadm control --reload-rules && sudo udevadm trigger, then check the node again with ls -l. -
The reader is in serial mode. A reader configured for USB virtual serial creates /dev/ttyACM0 or /dev/ttyUSB0 owned root:dialout with mode 0660, and the HID node finds no scanner at all. Test:
ls /dev/ttyACM* /dev/ttyUSB*. -
Wrong node index. Readers with more than one HID interface expose several nodes, and the library may open one that carries no data. Test:
sudo hexdump -C /dev/hidraw0while scanning; bytes stream only on the node that carries the reports.
Fix: one udev rule for the hidraw node
On a dedicated headless controller every hidraw device is a reader, so a subsystem-wide grant is the maintainable choice. Work through the steps:
- Plug the scanner in and list the nodes:
ls -l /dev/hidraw*. Note the newest index. - Confirm the node carries the scans. Run
sudo hexdump -C /dev/hidraw0, pull the trigger on a real code, and watch report bytes stream; stop with Ctrl-C. - Create /etc/udev/rules.d/99-hidraw-scanner.rules with a single line:
SUBSYSTEM=="hidraw", MODE="0660", GROUP="input"
- Apply it to the device that is already plugged in:
sudo udevadm control --reload-rules && sudo udevadm trigger. - Verify ownership:
ls -l /dev/hidraw*should now print crw-rw---- root input. - Add the runtime user to that group:
sudo usermod -aG input nodered, substituting your own service user for nodered. - Restart the reading service so the new membership takes effect, then scan without sudo. The decoded string appears in your flow, and the Device disconnected status clears.
If your distribution has no input group, create one with sudo groupadd scanio, put MODE 0660 and GROUP scanio in the rule, and add the service user to scanio instead.
Narrowing the rule to one reader
On a shared server, pin the grant to your reader. udevadm info -a -n /dev/hidraw0 walks the device chain and prints the ATTRS{idVendor} and ATTRS{idProduct} values, and lsusb shows the same pair after ID on the scanner's line. Quote both attributes in the rule line, before the MODE and GROUP settings, and only that model gets access. Skip the desktop answer TAG+="uaccess": it grants the current logged-in seat user access through ACLs and does nothing for a headless service account. MODE and GROUP are the headless mechanism.
If the stack opens libusb devices instead
Some HID libraries enumerate through libusb, and then the file that needs permission is not the hidraw node but the bus device under /dev/bus/usb. The rule shape is the same: match SUBSYSTEM=="usb", quote the same vendor and product attributes from lsusb, and set MODE 0660 with a group the runtime user belongs to.
Worked example: sizing the LED resistor for a 3.3 V GPIO pin and a 2.1 V green LED
Inputs, and where each comes from:
- 3.3 V GPIO high level: the standard logic level on the GPIO header of the single-board controller in this install and most of its peers.
- 2.1 V forward voltage: a typical green indicator LED at a few milliamps.
- 10 mA target current: chosen below the roughly 16 mA per-pin rating of such headers, leaving room for a buzzer on another pin.
The rule: a series resistor drops the supply voltage minus the LED forward voltage at the chosen current.
The arithmetic: R = (3.3 V - 2.1 V) / 0.010 A = 1.2 V / 0.010 A = 120 ohm.
Result and decision: 120 ohm is the minimum for 10 mA, and it is a standard stock value. The practical pick is 220 ohm, which runs (3.3 V - 2.1 V) / 220 ohm = 5.5 mA — clearly visible, cooler, and with double the margin. Anything from 150 to 330 ohm keeps one indicator between about 3.6 and 8 mA, so stock tolerance does not matter. What matters is holding every indicator at or below 10 mA, so the LED pair plus a beeper never approaches the 16 mA pin rating.
Wiring the green and red feedback LEDs
- One free GPIO pin to one leg of a 220 ohm resistor, the other leg to the green LED anode, cathode to a ground pin. Repeat on a second pin for the red LED.
- Configure both pins as outputs in your GPIO node or library. The stock GPIO node set for Node-RED covers this, and its keyboard node reads codes from the evdev layer, which is exactly why the input group helped that path and not the hidraw one.
- Decide the feedback from the decoded string itself: a valid symbology with the expected prefix switches green on for 500 ms; a read timeout or a rejected code flashes red three times at 150 ms, a signature you cannot mistake for the 500 ms success pulse.
- Log the string with a timestamp either way. Batch start and end times are the point of this install, and the log is what survives when an LED burns out.
Keyboard wedge or serial mode: pick per machine
Wedge is what most office scanners ship as: a USB HID keyboard, one cable for power and data, and any runtime that can read keystrokes. It costs you the udev rule above and the reload-and-trigger step. The keyboard wedge into an HMI guide shows the same mode from the display side.
Serial mode costs less permission work on most machines. Most industrial handheld and fixed readers switch their USB port to virtual serial with a configuration code: the node then appears as /dev/ttyACM0, or /dev/ttyUSB0 behind a serial chip, owned root:dialout with mode 0660 on Debian-based systems. Add the service user to dialout with usermod, restart the runtime, and no udev rule exists to maintain. The serial-port walkthrough for Node-RED covers this path, and USB or RS232 for a Linux PLC covers the choice from the PLC side.
Transport speed never decides it. A 12-character code crosses a 9,600 baud 8N1 serial link in 12 x 10 / 9,600 = 12.5 ms; USB HID sends one 8-byte report per keystroke and lands the same code in a few milliseconds. Both sit far below the roughly 100 ms beyond which feedback starts to feel late to an operator scanning every few seconds.
Decision: on a dedicated controller running only the scan service, the wedge rule above is fine. On a machine that also hosts consoles, other services or a strict permission posture, use serial mode, which also lets you frame records with your own prefix and suffix as in the prefix and suffix framing guide. The wider view of all three delivery paths sits in barcode scanner input methods to a PLC.
Readers for a fixed headless station
If you are buying rather than reusing office hardware, wired USB handhelds from the handheld code readers range fit this install: one cable for power and data, trigger-pull scanning, and a built-in good-read indicator on most models.
- Interface
- USB
- Connection
- Wired
- Interface
- USB
- Connection
- Wired
- Resolution
- Up to 3 MP
- Shutter
- Global
For a bracket-mounted station at the line port, a compact reader from the fixed code readers range such as the LS-CRD-0047 covers a 60-120 mm reading distance over USB 2.0 and reads hands-free, no trigger needed.
- Resolution
- Up to 3 MP
- Shutter
- Global
- Interface
- USB
Browse both ranges in the camera and reader finder, or request pricing for the pair of stations through the quote page.
Checks before you close the panel
- ls -l /dev/hidraw* prints crw-rw---- root input after the trigger step, or your chosen group if you made one.
- id lists the service user in the group, and the runtime process start time is after the usermod.
- A scan without sudo delivers the expected string to the flow or the log file.
- Each LED resistor measures 220 ohm, anything from 150 to 330 ohm acceptable, and no pin carries more than 10 mA.
- A deliberately bad code flashes the red pattern; a good code lights green for 500 ms.
- Unplug and replug the reader: the rule re-applies on its own. If the node index shuffles between ports, pin readers by identity as in identify barcode scanners by serial number after reconnects.
- The rule file sits in your configuration backup with a one-line comment naming the reader it grants.
When the usual answer is wrong
- chmod 666 /dev/hidraw0: it proves the diagnosis in thirty seconds, then dies at the next replug or reboot. The rule is the durable fix.
- The input-group desktop answer: it guards evdev, not hidraw, so it explains a stubborn failure rather than fixing it.
- Containers: a runtime in Docker cannot see the node unless it is passed through explicitly — the same lesson as passing V4L2 media nodes into containers — and the container user needs the group as well.
- Blaming the HID node version: an uncaught "cannot open device" on a root-only hidraw node is a permissions failure, and no library update changes who owns the node.
The decision for each case
- Office wedge scanner on a controller that only runs the scan service: keep wedge mode, apply the subsystem-wide hidraw rule with MODE 0660 and GROUP input, run the reload-and-trigger step, restart the runtime. Ten minutes, done.
- Industrial reader on a machine that hosts anything else: switch the reader to USB virtual serial with its configuration code, join the service user to dialout, and skip the udev rule entirely.
- Feedback: 220 ohm on every 3.3 V indicator, green on 500 ms for a decode, red 3 x 150 ms for a reject, and the reader's own beeper as the first confirmation.
- Building rather than retrofitting: pick the readers from the ranges above and walk the rest of the station through the system configurator. More reader installs sit on the code-reading topic page.
Questions
Why does joining the input group not give my Node-RED HID node access to the scanner?
The input group owns /dev/input/event*, the evdev keyboard nodes that shipped udev rules on Debian-based systems create with mode 0640. Node-RED USB HID nodes are built on the node-hid library, which opens /dev/hidraw* instead; those nodes come up root:root 0600 with no shipped group rule. Joining input guards a subsystem the library never opens, which is why the permission error survives.
Which mode should a scanner use on a headless Linux controller: keyboard wedge or serial?
Serial mode on machines that also run consoles or other services: the reader appears as /dev/ttyACM0 owned by the dialout group, so one usermod adds access with no udev rule, and a stray scan cannot type into a shell. Keyboard wedge is fine on a dedicated controller that only runs the scan service; budget one hidraw rule plus the reload-and-trigger step.
What resistor does an indicator LED need on a 3.3 V GPIO pin?
Divide the voltage the resistor must drop by the current you want. For a 2.1 V green LED at 10 mA: (3.3 V - 2.1 V) / 0.010 A = 120 ohm, the minimum. Standard 220 ohm is the practical pick and runs about 5.5 mA, bright enough for an indicator and comfortably inside the roughly 16 mA per-pin rating of single-board GPIO headers.
Do I have to unplug and replug the scanner after editing the udev rule?
No. Run sudo udevadm control --reload-rules followed by sudo udevadm trigger, which re-runs the uevents and applies the rule to devices that are already plugged in. Restarting the udev service alone does not re-apply permissions. You do need to restart the reading service after changing the user's groups, because group membership is read when the process starts.
How do I tell which /dev/hidraw node belongs to my scanner?
List the nodes before and after plugging: ls -l /dev/hidraw* shows the new one. Then run sudo hexdump -C /dev/hidraw0, scan a code, and the report bytes stream in only on the node that carries the data. udevadm info -a -n /dev/hidraw0 prints the vendor and product IDs you can quote in the rule to pin that exact reader.



