Search code examples
linuxlinux-kerneldrivertouchscreenraw-input

How to get Coordinates of Touchscreen Rawdata using Linux


We have a 3m microtouch display. It's connected to my Debian system via USB and recognized as human interface (hid). I am trying to access and push realtime information... if its getting touched I want to know where (x,y) and pipe it through netcat to another host.

Unfortunately I am only able to get raw data using:

cat /dev/input/event2 | hexdump

or

evtest

You get hexcode that seem nowhere documented...

Does anybody have a clue how to get that information? There must be a way to extract it from the hexcode. Unfortunately I have no idea how to interpret the hexcode. I couldn't find any source where its documented...

Is there a way the Kernel could provide me those desired information in realtime?

As a workaround, is there, maybe, a solution where the X-Server could tell me? The touchscreen behaves like a mouse in X. I actually already tried to get x,y-position of the mouse via xlib. But it was too slow and wouldn't tell me if somebody is touching or not...

evtest sample output:

Event: time 1425319271.595631, type 3 (EV_ABS), code 57 (ABS_MT_TRACKING_ID), value 51
Event: time 1425319271.595631, type 3 (EV_ABS), code 53 (ABS_MT_POSITION_X), value 10304
Event: time 1425319271.595631, type 3 (EV_ABS), code 54 (ABS_MT_POSITION_Y), value 30629
Event: time 1425319271.595631, type 3 (EV_ABS), code 48 (ABS_MT_TOUCH_MAJOR), value 893
Event: time 1425319271.595631, type 3 (EV_ABS), code 49 (ABS_MT_TOUCH_MINOR), value 414
Event: time 1425319271.595631, type 1 (EV_KEY), code 330 (BTN_TOUCH), value 1
Event: time 1425319271.595631, type 3 (EV_ABS), code 0 (ABS_X), value 10304
Event: time 1425319271.595631, type 3 (EV_ABS), code 1 (ABS_Y), value 30629
Event: time 1425319271.595631, -------------- SYN_REPORT ------------
Event: time 1425319271.601632, type 3 (EV_ABS), code 53 (ABS_MT_POSITION_X), value 10306
Event: time 1425319271.601632, type 3 (EV_ABS), code 54 (ABS_MT_POSITION_Y), value 30625
Event: time 1425319271.601632, type 3 (EV_ABS), code 48 (ABS_MT_TOUCH_MAJOR), value 962
Event: time 1425319271.601632, type 3 (EV_ABS), code 49 (ABS_MT_TOUCH_MINOR), value 421
Event: time 1425319271.601632, type 3 (EV_ABS), code 47 (ABS_MT_SLOT), value 1
Event: time 1425319271.601632, type 3 (EV_ABS), code 57 (ABS_MT_TRACKING_ID), value 52
Event: time 1425319271.601632, type 3 (EV_ABS), code 53 (ABS_MT_POSITION_X), value 15416
Event: time 1425319271.601632, type 3 (EV_ABS), code 54 (ABS_MT_POSITION_Y), value 24159
Event: time 1425319271.601632, type 3 (EV_ABS), code 48 (ABS_MT_TOUCH_MAJOR), value 649
Event: time 1425319271.601632, type 3 (EV_ABS), code 49 (ABS_MT_TOUCH_MINOR), value 354
Event: time 1425319271.601632, type 3 (EV_ABS), code 0 (ABS_X), value 10306
Event: time 1425319271.601632, type 3 (EV_ABS), code 1 (ABS_Y), value 30625
Event: time 1425319271.601632, -------------- SYN_REPORT ------------
Event: time 1425319271.606626, type 3 (EV_ABS), code 47 (ABS_MT_SLOT), value 0
Event: time 1425319271.606626, type 3 (EV_ABS), code 53 (ABS_MT_POSITION_X), value 10318
Event: time 1425319271.606626, type 3 (EV_ABS), code 54 (ABS_MT_POSITION_Y), value 30609
Event: time 1425319271.606626, type 3 (EV_ABS), code 48 (ABS_MT_TOUCH_MAJOR), value 1014
Event: time 1425319271.606626, type 3 (EV_ABS), code 49 (ABS_MT_TOUCH_MINOR), value 426
Event: time 1425319271.606626, type 3 (EV_ABS), code 47 (ABS_MT_SLOT), value 1
Event: time 1425319271.606626, type 3 (EV_ABS), code 54 (ABS_MT_POSITION_Y), value 24161
Event: time 1425319271.606626, type 3 (EV_ABS), code 48 (ABS_MT_TOUCH_MAJOR), value 681
Event: time 1425319271.606626, type 3 (EV_ABS), code 49 (ABS_MT_TOUCH_MINOR), value 376
Event: time 1425319271.606626, type 3 (EV_ABS), code 0 (ABS_X), value 10318
Event: time 1425319271.606626, type 3 (EV_ABS), code 1 (ABS_Y), value 30609
Event: time 1425319271.606626, -------------- SYN_REPORT ------------
Event: time 1425319271.611629, type 3 (EV_ABS), code 47 (ABS_MT_SLOT), value 0
Event: time 1425319271.611629, type 3 (EV_ABS), code 53 (ABS_MT_POSITION_X), value 10320
Event: time 1425319271.611629, type 3 (EV_ABS), code 54 (ABS_MT_POSITION_Y), value 30605
Event: time 1425319271.611629, type 3 (EV_ABS), code 48 (ABS_MT_TOUCH_MAJOR), value 1053
Event: time 1425319271.611629, type 3 (EV_ABS), code 49 (ABS_MT_TOUCH_MINOR), value 430
Event: time 1425319271.611629, type 3 (EV_ABS), code 47 (ABS_MT_SLOT), value 1
Event: time 1425319271.611629, type 3 (EV_ABS), code 48 (ABS_MT_TOUCH_MAJOR), value 705
Event: time 1425319271.611629, type 3 (EV_ABS), code 49 (ABS_MT_TOUCH_MINOR), value 392
Event: time 1425319271.611629, type 3 (EV_ABS), code 0 (ABS_X), value 10320
Event: time 1425319271.611629, type 3 (EV_ABS), code 1 (ABS_Y), value 30605

Solution

  • Console-based solution

    You can obtain parsed coordinates using evtest tool.

    1. If you only need single-touch coordinates: look for ABS_X and ABS_Y fields:

       type 3 (EV_ABS), code 0 (ABS_X), value 10306
       type 3 (EV_ABS), code 1 (ABS_Y), value 30625
      
    2. If you need multi-touch coordinates:

      • ABS_MT_SLOT represents number of finger
      • ABS_MT_POSITION_X and ABS_MT_POSITION_Y -- coordinates

      Finger #0:

       type 3 (EV_ABS), code 47 (ABS_MT_SLOT), value 0
       type 3 (EV_ABS), code 53 (ABS_MT_POSITION_X), value 10318
       type 3 (EV_ABS), code 54 (ABS_MT_POSITION_Y), value 30609
      

      Finger #1:

       type 3 (EV_ABS), code 47 (ABS_MT_SLOT), value 1
       type 3 (EV_ABS), code 53 (ABS_MT_POSITION_X), value 20301
       type 3 (EV_ABS), code 54 (ABS_MT_POSITION_Y), value 24161
      

    For example, if you need to send single-touch coordinates via network, you can use script like this:

    #!/bin/sh
    
    # ---- Global variables ----
    
    input=/dev/input/event0
    code_prefix="ABS"
    code="${code_prefix}_[XY]"
    val_regex=".*(${code_prefix}_\(.\)), value \([-]\?[0-9]\+\)"
    val_subst="\1=\2"
    
    # ---- Functions ----
    
    send_axis() {
        # 1. Convert axis value ($1) from device specific units
        # 2. Send this axis value via UDP packet
        echo $1
    }
    
    process_line() {  
        while read line; do
            axis=$(echo $line | grep "^Event:" | grep $code | \
                   sed "s/$val_regex/$val_subst/")
    
            if [ -n "$axis" ]; then
                send_axis $axis
            fi
        done
    }
    
    # ---- Entry point ----
    
    if [ $(id -u) -ne 0 ]; then
        echo "This script must be run from root" >&2
        exit 1
    fi
    
    evtest $input | process_line
    

    Program-based solution

    You can write C application that will read your event file. Obtained binary data can be easily interpreted, see section 5 in kernel documentation. You can wait for next data portion using select() syscall.

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <linux/input.h>
    
    /* Change it to your dev file for the touch screen */
    #define EVENT_DEVICE    "/dev/input/event2"
    #define EVENT_TYPE      EV_ABS
    #define EVENT_CODE_X    ABS_X
    #define EVENT_CODE_Y    ABS_Y
    
    /* TODO: Close fd on SIGINT (Ctrl-C), if it's open */
    static int fd = -1;
    
    int main(int argc, char *argv[])
    {
        struct input_event ev;
        char name[256] = "Unknown";
        fd_set readfds;
    
        /* /dev/input/event* files are only readable by root:input */
        if ((getuid()) != 0) {
            fprintf(stderr, "You are not root! This may not work...\n");
            return EXIT_SUCCESS;
        }
    
        /* Open device for non-blocking read */
        fd = open(EVENT_DEVICE, O_RDONLY | O_NONBLOCK);
        if (fd == -1) {
            fprintf(stderr, "%s is not a vaild device\n", EVENT_DEVICE);
            return EXIT_FAILURE;
        }
    
        /* Print device name */
        ioctl(fd, EVIOCGNAME(sizeof(name)), name);
        printf("Reading from:\n");
        printf("  - device file: %s\n", EVENT_DEVICE);
        printf("  - device name: %s\n", name);
    
        /* Prepare for select(): zero and set the fd into fd_set */
        FD_ZERO(&readfds);
        FD_SET(fd, &readfds);
    
        /* Press Ctrl-C to stop the program */
        for (;;) {
            const size_t ev_size = sizeof(struct input_event);
            ssize_t size;
            int ret;
            /* struct timeval timeout = { 30, 0 }; */
    
            /*
             * select(): no-timeout version, just stop on errors or
             * interrupts.
             */
            ret = select(fd + 1, &readfds, NULL, NULL, NULL);
            /*
             * select(): timeout-enabled version, stop if no event has
             * occurred until timeout; might be useful e.g. for background
             * tasks.
             */
            /* ret = select(fd + 1, &readfds, NULL, NULL, &timeout); */
            if (ret == -1) {
                perror("Error: select() failed");
                goto err;
            } else if (ret == 0) {
                fprintf(stderr, "Error: select() timeout\n");
                continue;
            }
    
            size = read(fd, &ev, ev_size);
            if (size < ev_size) {
                fprintf(stderr, "Error: Wrong size when reading\n");
                goto err;
            }
    
            if (ev.type == EVENT_TYPE && (ev.code == EVENT_CODE_X
                                          || ev.code == EVENT_CODE_Y)) {
                /* TODO: convert value to pixels */
                printf("%s = %d\n", ev.code == EVENT_CODE_X ? "X" : "Y",
                       ev.value);
            }
        }
    
        return EXIT_SUCCESS;
    
    err:
        close(fd);
        return EXIT_FAILURE;
    }
    

    Coordinates units

    First of all you need to know next things:

    • where is coordinates origin point (i.e. [x=0;y=0])
    • which units your device is using for representing coordinates

    This information usually can be found in driver code for your device.

    This is the driver for your device.

    So it seems like you need to divide your axis value from evtest by 65535 and multiply it by width or height of device (in pixels). For example, if you get X=30000, and width of your LCD panel is 1080 pixels, you need to do:

    X = round((30000 / 65535) * 1080) = 494 pixels