从input事件(linux,c)读取条形码

我有一个从/ dev / input / event4读取条形码的小程序。 这是代码:

#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 a 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; } 

我现在用一个在线生成器创build了一些条形码( http://www.barcode-generator.de/V2/de/index.jsp )。 条形码是:123456789和1234567890

扫描条形码时,我的程序的输出是:

 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 

为123456789

 type: 1, code: 28, value: 0, rd: 16 

为1234567890

所以,10位数的条码无法正确识别。

代码:28表示这是一个RETURN / ENTER,这是一个条形码的内部terminal,所以这是直接来自扫描仪。

有人可以告诉我为什么? 也许代码有问题吗?

再见,安德烈

Solutions Collecting From Web of "从input事件(linux,c)读取条形码"

read()返回== sizeof ev ,您应该只考虑事件,因为我们在这里从输入设备读取数据。 如果它返回零,这意味着没有更多的事件即将到来(也许设备分离?)。 如果返回-1 ,则检查errno 。 如果read()返回任何其他值,那么内核驱动程序已经疯狂了,你可以认为这是一个致命的错误。

errno == EINTR正常(发送信号时发生),这本身不是错误。 这不应该发生在这里,但忽视它(把它当作一个呃逆,而不是一个错误)是相当安全的。

errno == EAGAINopen()标志中使用O_NONBLOCK时发生,并且没有新的事件可用。

这里绝对没有理由使用O_NONBLOCK 。 它所做的只是导致代码浪费CPU周期,从read()调用每秒返回几万次,返回-1 ,并返回errno == EAGAIN 。 只要放下它,这样read()就会等到新的事件到来,然后返回它。

查看我对input_event结构描述问题的回答。

总之,对于ev_type == 1 == EV_KEY

  • ev_value == 0 :释放的键(键)
  • ev_value == 1 :按下按键(按下按键)
  • ev_value == 2ev_value == 2 (键自动重复)
  • 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

设备提供的按键实际上是1 2 3 4 5 6 7 8 9 Enter

(请注意,您只显示了键释放事件;对于每个ev_code ,实际上应该看到两个,一个是ev_value == 1 ,后面是ev_value == 0

我试过的一个中国人非常好,虽然很便宜。 它有一个手册与几个条形码,包括一些条码格式之间切换(和数字的数字)。 我依稀记得使用两个条码切换到另一种模式,并使用最低音量的哔哔声。 即使分离后,它似乎仍然保留这些设置。


这里是一个什么样的实施,我会用来读取条形码的例子。 我显然将条形码阅读部分分开到一个单独的文件。

下面的代码是专门为公共领域( CC0许可),所以随时以任何你想要的方式使用它。 没有任何形式的保证,所以不要责怪我的任何破坏。 (任何错误修正都是值得欢迎的;如果有报告,我会检查并包含在下面的代码中,我建议在下面添加一条评论;每隔几天我都会阅读所有评论。

头文件barcode.h

 #ifndef BARCODE_H #define BARCODE_H #include <stdlib.h> #include <signal.h> /* This flags turns nonzero if any signal * installed with install_done is caught. */ extern volatile sig_atomic_t done; /* Install signals that set 'done'. */ int install_done(const int signum); /* Barcode device description. * Do not meddle with the internals; * this is here only to allow you * to allocate one statically. */ typedef struct { int fd; volatile int timeout; timer_t timer; } barcode_dev; /* Close a barcode device. * Returns 0 if success, nonzero errno error code otherwise. */ int barcode_close(barcode_dev *const dev); /* Open a barcode device. * Returns 0 if success, nonzero errno error code otherwise. */ int barcode_open(barcode_dev *const dev, const char *const device_path); /* Read a barcode, but do not spend more than maximum_ms. * Returns the length of the barcode read. * (although at most length-1 characters are saved at the buffer, * the total length of the barcode is returned.) * errno is always set; 0 if success, error code otherwise. * If the reading timed out, errno will be set to ETIMEDOUT. */ size_t barcode_read(barcode_dev *const dev, char *const buffer, const size_t length, const unsigned long maximum_ms); #endif /* BARCODE_H */ 

下面的barcode.c文件中的实现目前只接受数字(0到1),但添加其他必要的键(如KEY_ZKEY_Z )应该是微不足道的。 目前的人忽略了转移,控制等等,因为据我所知它们不是由任何扫描仪提供的。 它使用SIGRTMAX-0实时信号和每个条形码设备的定制定时器来读取条形码,因此您需要将其链接到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 the rt library; -lrt. */ #define UNUSED __attribute__((unused)) #define TIMEOUT_SIGNAL (SIGRTMAX-0) /* * done - flag used to exit program at 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, and 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 all 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; do { 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 the repeated elapsing, it is safe to * clear the 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) { /* We consider only key presses and 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 the code, except at 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 to 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; } 

这是一个示例程序example.c 。 你提供输入事件设备(如果你的udev提供这些设备,我建议在/dev/input/by-id//dev/input/by-path/使用符号链接,因为事件设备索引可能在内核版本上不稳定,硬件启动),以及您希望等到下一个条码的最长时间。

 #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 from INPUT-EVENT-DEVICE,\n"); fprintf(stderr, "waiting at most IDLE-TIMEOUT seconds for a new barcode.\n"); fprintf(stderr, "The INPUT-EVENT-DEVICE is grabbed, the digits do not appear as\n"); fprintf(stderr, "inputs in the machine.\n"); fprintf(stderr, "You can at any time end the program by sending it 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 is too 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 to 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; } 

正如您所看到的,它只是将条形码打印到标准输出(以及任何错误消息和警告标准错误)。 为了编译它,我建议使用下面的Makefile (缩进必须使用Tab ,不能是空格):

 CC := gcc CFLAGS := -Wall -Wextra -O2 LDFLAGS := -lrt .PHONY: all clean all: clean example clean: rm -f example *.o %.o: %.c $(CC) $(CFLAGS) -c $^ example: example.o barcode.o $(CC) $(CFLAGS) $^ $(LDFLAGS) -o example 

要编译,创建上面列出的四个文件,然后运行

 make clean example 

运行例如

 ./example /dev/input/event4 5.0 

将从/dev/input/event4读取条形码,但会在Ctrl + C (INT信号),HUP信号,TERM信号或5秒钟内没有条形码出现时退出。

请注意,如果在5秒钟内只读取了部分条码,我们确实得到了部分条码(只能尝试读取其余部分),但上面的示例程序忽略了部分条码,只显示超时。

有问题吗?