Read barcodes from input-event (linux, c) -
i have small program read barcodes /dev/input/event4. code:
#include <sys/file.h> #include <stdio.h> #include <string.h> #include <linux/input.h> int main (int argc, char *argv[]) { struct input_event ev; int fd, rd; //open device if ((fd = open ("/dev/input/event4", o_rdonly|o_nonblock)) == -1){ printf ("not vaild device.\n"); return -1; } while (1){ memset((void*)&ev, 0, sizeof(ev)); rd = read (fd, (void*)&ev, sizeof(ev)); if (rd <= 0){ printf ("rd: %d\n", rd); sleep(1); } if(rd>0 && ev.value==0 && ev.type==1){ printf("type: %d, code: %d, value: %d, rd: %d\n", ev.type, ev.code, ev.value, rd); } } return 0; }
i have created barcodes online-generator (http://www.barcode-generator.de/v2/de/index.jsp). barcodes are: 123456789 , 1234567890
the output of programm when scanning barcodes is:
type: 1, code: 2, value: 0, rd: 16 type: 1, code: 3, value: 0, rd: 16 type: 1, code: 4, value: 0, rd: 16 type: 1, code: 5, value: 0, rd: 16 type: 1, code: 6, value: 0, rd: 16 type: 1, code: 7, value: 0, rd: 16 type: 1, code: 8, value: 0, rd: 16 type: 1, code: 9, value: 0, rd: 16 type: 1, code: 10, value: 0, rd: 16 type: 1, code: 28, value: 0, rd: 16
for 123456789
and
type: 1, code: 28, value: 0, rd: 16
for 1234567890
so, 10-digit-barcodes not recognised correctly.
the code: 28 means return/enter, internal terminator barcode, comes directly scanner.
does can tell why ? maybe there wrong code ?
goodbye, andre
you should consider event when read()
returns == sizeof ev
, because we're reading input device here. if returns zero, means no more events forthcoming (maybe device detached?). if returns -1
, check errno
. if read()
returns other value, kernel driver has gone bonkers , consider fatal error.
errno == eintr
normal (occurs when signal delivered), not error per se. shouldn't happen here, ignoring (treating hiccup, not error) quite safe.
errno == eagain
occurs when used o_nonblock
in open()
flags, , there no new event available yet.
there absolutely no reason use o_nonblock
here. causes code waste cpu cycles, returning tens of thousands of times per second read()
call return -1
errno == eagain
. drop it, read()
wait until new event arrives, , returns it.
see answer input_event structure description question.
in summary, ev_type == 1 == ev_key
:
ev_value == 0
: key released (key up)ev_value == 1
: key pressed (key down)ev_value == 2
: autorepeat (key automatically repeated)ev_code == 1 == key_esc
ev_code == 2 == key_1
ev_code == 3 == key_2
ev_code == 10 == key_9
ev_code == 11 == key_0
ev_code == 28 == key_enter
the keypresses device provided 1 2 3 4 5 6 7 8 9 enter.
(note showed key release events; should see two, 1 ev_value == 1
, followed 1 ev_value == 0
, each ev_code
.)
the 1 chinese 1 i've tried nice, although dirt cheap. had manual few barcodes, including switch between barcode formats (and number of digits). vaguely remember using 2 barcodes switch mode, , use minimum volume beeps. seemed retain settings after being detached.
here example of kind of implementation i'd use read barcodes. i'd split barcode reading part separate file.
the below code dedicated public domain (licensed under cc0), feel free use in way wish. there no guarantees of kind, don't blame me breakage. (any bug fixes welcome; if reported, check , include in below code. recommend adding comment below; read comments answers every couple of days or so.)
the header file barcode.h
:
#ifndef barcode_h #define barcode_h #include <stdlib.h> #include <signal.h> /* flags turns nonzero if signal * installed install_done caught. */ extern volatile sig_atomic_t done; /* install signals set 'done'. */ int install_done(const int signum); /* barcode device description. * not meddle internals; * here allow * allocate 1 statically. */ typedef struct { int fd; volatile int timeout; timer_t timer; } barcode_dev; /* close barcode device. * returns 0 if success, nonzero errno error code otherwise. */ int barcode_close(barcode_dev *const dev); /* open barcode device. * returns 0 if success, nonzero errno error code otherwise. */ int barcode_open(barcode_dev *const dev, const char *const device_path); /* read barcode, not spend more maximum_ms. * returns length of barcode read. * (although @ length-1 characters saved @ buffer, * total length of barcode returned.) * errno set; 0 if success, error code otherwise. * if reading timed out, errno set etimedout. */ size_t barcode_read(barcode_dev *const dev, char *const buffer, const size_t length, const unsigned long maximum_ms); #endif /* barcode_h */
the implementation in following barcode.c
file accepts digits (0 through 1), should trivial add other necessary keys (like key_a
through key_z
). current 1 ignores shift, control, et cetera, not provided scanners far know. uses sigrtmax-0
realtime signal , custom timer per barcode device read barcodes, you'll need link against librt
(-lrt
):
#define _posix_c_source 200809l #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <fcntl.h> #include <signal.h> #include <time.h> #include <linux/input.h> #include <string.h> #include <stdio.h> #include <errno.h> /* link against rt library; -lrt. */ #define unused __attribute__((unused)) #define timeout_signal (sigrtmax-0) /* * done - flag used exit program @ sigint, sigterm etc. */ volatile sig_atomic_t done = 0; static void handle_done(int signum unused) { done = 1; } int install_done(const int signum) { struct sigaction act; sigemptyset(&act.sa_mask); act.sa_handler = handle_done; act.sa_flags = 0; if (sigaction(signum, &act, null) == -1) return errno; return 0; } /* * barcode input event device, , associated timeout timer. */ typedef struct { int fd; volatile int timeout; timer_t timer; } barcode_dev; static void handle_timeout(int signum unused, siginfo_t *info, void *context unused) { if (info && info->si_code == si_timer && info->si_value.sival_ptr) #if __gnuc__ > 4 || (__gnuc__ == 4 && __gnuc_minor__ >= 7) __atomic_add_fetch((int *)info->si_value.sival_ptr, 1, __atomic_seq_cst); #else __sync_add_and_fetch((int *)info->si_value.sival_ptr, 1); #endif } static int install_timeouts(void) { static int installed = 0; if (!installed) { struct sigaction act; sigemptyset(&act.sa_mask); act.sa_sigaction = handle_timeout; act.sa_flags = sa_siginfo; if (sigaction(timeout_signal, &act, null) == -1) return errno; installed = 1; } return 0; } int barcode_close(barcode_dev *const dev) { int retval = 0; if (!dev) return 0; if (dev->fd != -1) if (close(dev->fd) == -1) retval = errno; dev->fd = -1; if (dev->timer) if (timer_delete(dev->timer) == -1) if (!retval) retval = errno; dev->timer = (timer_t)0; /* handle pending timeout_signals */ while (1) { struct timespec t; siginfo_t info; sigset_t s; t.tv_sec = (time_t)0; t.tv_nsec = 0l; sigemptyset(&s); if (sigtimedwait(&s, &info, &t) != timeout_signal) break; if (info.si_code != si_timer || !info.si_value.sival_ptr) continue; #if __gnuc__ > 4 || (__gnuc__ == 4 && __gnuc_minor__ >= 7) __atomic_add_fetch((int *)info.si_value.sival_ptr, 1, __atomic_seq_cst); #else __sync_add_and_fetch((int *)info.si_value.sival_ptr, 1); #endif } return errno = retval; } int barcode_open(barcode_dev *const dev, const char *const device_path) { struct sigevent event; int fd; if (!dev) return errno = einval; dev->fd = -1; dev->timeout = -1; dev->timer = (timer_t)0; if (!device_path || !*device_path) return errno = einval; if (install_timeouts()) return errno; { fd = open(device_path, o_rdonly | o_noctty | o_cloexec); } while (fd == -1 && errno == eintr); if (fd == -1) return errno; errno = 0; if (ioctl(fd, eviocgrab, 1)) { const int saved_errno = errno; close(fd); return errno = (saved_errno) ? errno : eacces; } dev->fd = fd; memset(&event, 0, sizeof event); event.sigev_notify = sigev_signal; event.sigev_signo = timeout_signal; event.sigev_value.sival_ptr = (void *)&(dev->timeout); if (timer_create(clock_realtime, &event, &dev->timer) == -1) { const int saved_errno = errno; close(fd); return errno = (saved_errno) ? errno : emfile; } return errno = 0; } size_t barcode_read(barcode_dev *const dev, char *const buffer, const size_t length, const unsigned long maximum_ms) { struct itimerspec it; size_t len = 0; int status = etimedout; if (!dev || !buffer || length < 2 || maximum_ms < 1ul) { errno = einval; return (size_t)0; } /* initial timeout. */ it.it_value.tv_sec = maximum_ms / 1000ul; it.it_value.tv_nsec = (maximum_ms % 1000ul) * 1000000l; /* after elapsing, fire every 10 ms. */ it.it_interval.tv_sec = 0; it.it_interval.tv_nsec = 10000000l; if (timer_settime(dev->timer, 0, &it, null) == -1) return (size_t)0; /* because of repeated elapsing, safe * clear timeout flag here. */ #if __gnuc__ > 4 || (__gnuc__ == 4 && __gnuc_minor >= 7) __atomic_store_n((int *)&(dev->timeout), 0, __atomic_seq_cst); #else __sync_fetch_and_and((int *)&(dev->timeout), 0); #endif while (!dev->timeout) { struct input_event ev; ssize_t n; int digit; n = read(dev->fd, &ev, sizeof ev); if (n == (ssize_t)-1) { if (errno == eintr) continue; status = errno; break; } else if (n == sizeof ev) { /* consider key presses , autorepeats. */ if (ev.type != ev_key || (ev.value != 1 && ev.value != 2)) continue; switch (ev.code) { case key_0: digit = '0'; break; case key_1: digit = '1'; break; case key_2: digit = '2'; break; case key_3: digit = '3'; break; case key_4: digit = '4'; break; case key_5: digit = '5'; break; case key_6: digit = '6'; break; case key_7: digit = '7'; break; case key_8: digit = '8'; break; case key_9: digit = '9'; break; default: digit = '\0'; } /* non-digit key ends code, except @ beginning of code. */ if (digit == '\0') { if (!len) continue; status = 0; break; } if (len < length) buffer[len] = digit; len++; continue; } else if (n == (ssize_t)0) { status = enoent; break; } else { status = eio; break; } } /* add terminator character buffer. */ if (len + 1 < length) buffer[len + 1] = '\0'; else buffer[length - 1] = '\0'; /* cancel timeout. */ it.it_value.tv_sec = 0; it.it_value.tv_nsec = 0; it.it_interval.tv_sec = 0; it.it_interval.tv_nsec = 0l; (void)timer_settime(dev->timer, 0, &it, null); errno = status; return len; }
here example program, example.c
. supply input event device (i suggest using symlink in /dev/input/by-id/
or /dev/input/by-path/
if udev
provides those, event device indexes may not stable across kernel versions , hardware boots), , maximum duration you're willing wait for/until next barcode.
#include <stdlib.h> #include <string.h> #include <signal.h> #include <stdio.h> #include <errno.h> #include "barcode.h" #define barcode_maxlen 1023 int main(int argc, char *argv[]) { barcode_dev dev; unsigned long ms; int status, exitcode; if (argc != 3 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) { fprintf(stderr, "\n"); fprintf(stderr, "usage: %s [ -h | --help ]\n", argv[0]); fprintf(stderr, " %s input-event-device idle-timeout\n", argv[0]); fprintf(stderr, "\n"); fprintf(stderr, "this program reads barcodes input-event-device,\n"); fprintf(stderr, "waiting @ idle-timeout seconds new barcode.\n"); fprintf(stderr, "the input-event-device grabbed, digits not appear as\n"); fprintf(stderr, "inputs in machine.\n"); fprintf(stderr, "you can @ time end program sending a\n"); fprintf(stderr, "sigint (ctrl+c), sighup, or sigterm signal.\n"); fprintf(stderr, "\n"); return exit_failure; } if (install_done(sigint) || install_done(sighup) || install_done(sigterm)) { fprintf(stderr, "cannot install signal handlers: %s.\n", strerror(errno)); return exit_failure; } { double value, check; char dummy; if (sscanf(argv[2], " %lf %c", &value, &dummy) != 1 || value < 0.001) { fprintf(stderr, "%s: invalid idle timeout value (in seconds).\n", argv[2]); return exit_failure; } ms = (unsigned long)(value * 1000.0); check = (double)ms / 1000.0; if (value < check - 0.001 || value > check + 0.001 || ms < 1ul) { fprintf(stderr, "%s: idle timeout long.\n", argv[2]); return exit_failure; } } if (barcode_open(&dev, argv[1])) { fprintf(stderr, "%s: cannot open barcode input event device: %s.\n", argv[1], strerror(errno)); return exit_failure; } while (1) { char code[barcode_maxlen + 1]; size_t len; if (done) { status = eintr; break; } len = barcode_read(&dev, code, sizeof code, ms); if (errno) { status = errno; break; } if (len < (size_t)1) { status = etimedout; break; } printf("%zu-digit barcode: %s\n", len, code); fflush(stdout); } if (status == eintr) { fprintf(stderr, "signaled exit. complying.\n"); fflush(stderr); exitcode = exit_success; } else if (status == etimedout) { fprintf(stderr, "timed out, no more barcodes.\n"); fflush(stderr); exitcode = exit_success; } else { fprintf(stderr, "error reading input event device %s: %s.\n", argv[1], strerror(status)); fflush(stderr); exitcode = exit_failure; } if (barcode_close(&dev)) { fprintf(stderr, "warning: error closing input event device %s: %s.\n", argv[1], strerror(errno)); fflush(stderr); exitcode = exit_failure; } return exitcode; }
as can see, prints barcodes standard output (and error messages , warnings standard error). compile it, recommend using following makefile
(indentation must using tab, not spaces):
cc := gcc cflags := -wall -wextra -o2 ldflags := -lrt .phony: clean all: clean example clean: rm -f example *.o %.o: %.c $(cc) $(cflags) -c $^ example: example.o barcode.o $(cc) $(cflags) $^ $(ldflags) -o example
to compile, create 4 files listed above, run
make clean example
running example
./example /dev/input/event4 5.0
will read barcodes /dev/input/event4
, exit @ ctrl+c (int signal), hup signal, term signal, or if no barcode appears within 5 seconds.
note if partial barcode read within 5 seconds, partial part (and try , read rest of it), above example program ignores partial, , shows timeout.
questions?
Comments
Post a Comment