LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   optical mouse as a motion sensor - writting an aplication in C++ using Linux (https://www.linuxquestions.org/questions/programming-9/optical-mouse-as-a-motion-sensor-writting-an-aplication-in-c-using-linux-497222/)

alexandre_fs 10-31-2006 09:57 AM

optical mouse as a motion sensor - writting an aplication in C++ using Linux
 
Hello.

I am new to the Linux OS, and I am having some difficulties in creating an application (in c++) for reading the data transmitted by the computer mouse to the OS. ( I'm using linux because i think it's easier to access this type of information than in windows...)

The objective of the application is to use an optical mouse as a motion capture sensor (replacing the need to buy industrial encoders that are too expensive...)


Using the 'xxd' in the terminal, i can now understand the imps/2 protocol, and how to interpret each bit transmitted.


The command I use is: xxd -b -c 3 /dev/input/mice


Now, my problem :)

I want to create a C++ application for interpreting in real-time the data transmitted by the mouse. Until now I have thought about 3 processes, and none of them is working...

process #1: opening a pipe with the terminal, using the xxd command i previously explained:

void readSensor2()
{
FILE *fp;
char line[BUFSIZ];
if ((fp = popen("sudo xxd -ps -c 3 /dev/input/mice", "r")) != NULL)
{
while (fgets( line, BUFSIZ, fp) != NULL)
{
/*
if (strcmp(line, "080001")==0 )
printf("forward \n");
else if (strcmp(line, "2800ff")==0)
printf("backward \n");
else if (strcmp(line, "080100")==0)
printf("right \n");
else if (strcmp(line, "18ff00")==0)
printf("left \n");
*/
printf("%s", line);
}
pclose(fp);
}
}

really simple example, just to test the idea... the problem here, is that (i don't know why...) i can't have a real-time analysis... the data only appears each 2-3 seconds...

and if i do this:

void readSensor()
{
FILE *fp;
char line[BUFSIZ];
fp = popen("sudo xxd -l 3 -ps /dev/input/mice", "r");
fgets( line, BUFSIZ, fp);
if (strcmp(line, "080001")==0)
printf("forward \n");
else if (strcmp(line, "2800ff")==0)
printf("backward \n");
else if (strcmp(line, "080100")==0)
printf("right \n");
else if (strcmp(line, "18ff00")==0)
printf("left \n");

//printf("%s", line);
pclose(fp);
}

be constantly calling the readSensor() function, too much data is lost.


process #2: using 'ncurses'

The problem is that ncurses as far as I know, only captures the mouse movement in the screen. I mean, if the pointer is in the right upper corner, and you are still moving it further, no movement will be detected.


process #3: using GPM

here, i think relies my solution :)
but i can't find any good documentation for programming using gpm..
...and i can't compile the code... it says that

undefined reference to `Gpm_Open'
(...)

code:

#include <stdio.h>
#include <gpm.h>
#include <ncurses.h>

int my_handler(Gpm_Event *event, void *data)
{ printf("Event Type : %d at x=%d y=%d\n", event->type, event->x, event->y);
return 0;
}
int main()
{ Gpm_Connect conn;
int c;
conn.eventMask = ~0; /* Want to know about all the events */
conn.defaultMask = 0; /* don't handle anything by default */
conn.minMod = 0; /* want everything */
conn.maxMod = ~0; /* all modifiers included */

if(Gpm_Open(&conn, 0) == -1)
printf("Cannot connect to mouse server\n");

gpm_handler = my_handler;
while((c = Gpm_Getc(stdin)) != EOF)
printf("%c", c);
Gpm_Close();
return 0;
}


the best solution would definitly be without using gpm too...


any ideas??

thanks for reading this!!

tuxdev 10-31-2006 10:31 AM

Just open the device directly instead of using xxd. Or, ask the X-Server if you have that option. The X-Server might not give you movement data outside the window, though.

Also, please use [code] tags to preserve indentation and improve readability

divot_powell 01-23-2008 10:09 AM

Just pick apart the xxd source....
 
I'm sure this doesn't matter anymore given the time of posting, however I'm working on a wee project very similar to this. And to work out how to do it real time, all I've done is picked apart the source to the xxd program and strip it down for what you need. I've done just that and ended up with:

Code:

#include <stdio.h>

char hexxa[] = "0123456789abcdef0123456789ABCDEF", *hexx = hexxa;

int main() {
  FILE *fp;
  signed short e, p = 3;

  if ((fp = fopen("/dev/input/mice", "r")) == NULL) {
    printf ("ERROR!! No such file\n");
    return 2;
  }

  while ((e = getc(fp)) != EOF) {
    if (e > 128) e = e - 256;
    printf("%d", e);
//    putchar(hexx[(e >> 4) & 0xf]);
//    putchar(hexx[(e    ) & 0xf]);
    if (!--p) {
      putchar('\n');
      p = 3;
    } else {
      putchar(' ');
    }
  }

  if (p < 3)
    putchar('\n');

  fclose(fp);

  return 0;
}

/* Modified from xxd source code originally provided and copyrighted by Juergen Weigert:
 * * "(c) 1990-1998 by Juergen Weigert (jnweiger@informatik.uni-erlangen.de)
 * *
 * * Distribute freely and credit me,
 * * make money and share with me,
 * * lose money and don't ask me."
 */

If you want the hex output then comment out the printf statement, and uncomment the two putchar lines, however I found it more useful to have a decimal output:

40 12 -6

First number is direction and button presses, you'll have to work the exacts yourself. Negative second number for left, with the value as the speed, and positive for right. And finally the last number is negative for backwards, positive for forwards, and the value as speed.

alexandre_fs 01-25-2008 02:50 PM

hello. thank you for your post anyway. that project is terminated, but it still might be useful to someone else.

i ended up by listening the device files port directly and processing its information in real-time. it worked great!

if you need some info, just let me know.

bye

osor 01-26-2008 10:57 AM

@divot If you want to be as efficient as possible, you might want to use the evdev interface rather than mousedev. The reason is twofold:
  1. For evdev you are guaranteed to read 32 bytes at a time (and read() will block until data becomes available), so you don’t have to do character-by-character reading.
  2. The mousedev driver (as it is today) is a wrapper for the evdev driver. So by using evdev directly, you eliminate one layer of unnecessary wrapping.
Here is a thread about using evdev. Of course if all you want is a motion sensor, you can ignore any information about button presses, etc.

divot_powell 01-27-2008 04:12 PM

Hiya, thanks for your help. I've noticed in testing that the evdev method creates alot more information and I've found that the first method provides all the information at a quick enough speed for my purposes. The box I'm using is also not quick enough to allow me to process all of that information..

I have a follow up question though. Firstly I'd like to point out this is a peculiar linux version for an arm architecture I'm using here so not everything works, for instance /dev/input/ does not exist and I'm using /dev/usbmouse for my purposes. I have (finally) managed to find a method for distinguishing between two mouses that are plugged into my device by creating two separate usbmouse files using mknod. Now I'm trying to find a way to have linux make a file, or a c program to combine those two seperate outputs so that they can be read at the same time. Such that it displays similar to x1 y1 x2 y2, where x1 and y1 are from mouse 1 and x2 and y2 are from mouse two. If one mouse moves but not the other, the relevant mouse just displays zeroes, whilst if they both move it displays the correct values. Do you know of any way to do this?

I have noticed that it is possible to have one file be updated by two seperate devices, the situation I was in at first, however there was no way of distinguishing which device it was that had updated the file.. If there is a method that easily distinguishes this I can decode the information contained within it easily enough myself, however I cannot find a way of distinguishing the information without using two separate files, which provides a noodlescratcher of a problem with blocked reads of two files...

Any help on this issue would be very much appreciated.

The fact that it's a peculiar version of linux might cause issues so here's my /proc/version:
Linux version 2.6.12.2-cm-x255 (nils@abode) (gcc version 4.0.3) #2 Fri Dec 15 15:47:24 GMT 2006

osor 01-27-2008 09:25 PM

Quote:

Originally Posted by divot_powell (Post 3036906)
The box I'm using is also not quick enough to allow me to process all of that information..

You don’t need to process all of it if you don’t want to. I.e., ignore any events which are not of type EV_REL.
Quote:

Originally Posted by divot_powell (Post 3036906)
I have a follow up question though. Firstly I'd like to point out this is a peculiar linux version for an arm architecture I'm using here so not everything works, for instance /dev/input/ does not exist and I'm using /dev/usbmouse for my purposes. I have (finally) managed to find a method for distinguishing between two mouses that are plugged into my device by creating two separate usbmouse files using mknod. Now I'm trying to find a way to have linux make a file, or a c program to combine those two seperate outputs so that they can be read at the same time. Such that it displays similar to x1 y1 x2 y2, where x1 and y1 are from mouse 1 and x2 and y2 are from mouse two. If one mouse moves but not the other, the relevant mouse just displays zeroes, whilst if they both move it displays the correct values. Do you know of any way to do this?

You can create the equivalent of /dev/mice, by making a node whose major number is 13 and whose minor number is 31. This device is exactly like /dev/mouse0 except that events from both mice are sent (asynchronously, except whole packets of 3 bytes or so don’t overlap). But it seems like this is what your situation was before (i.e., it doesn’t distinguish the events).

There is no kernel-provided interface for a single file that distinguishes the devices (AFAIK), but you can accomplish the same by either having two forked processes with some sort of IPC, or by using non-blocking file descriptors with select(). In fact, this is just the kind of thing you’re supposed to do with select().

Also note that it is most reliable to read all three (or four) bytes at once in a single call to read() and process them in a separate function. The reason I say four is that if your mouse is configured specially, it might sent four bytes instead of three (this is why the evdev interface is more reliable).


All times are GMT -5. The time now is 12:02 AM.