wtorek, 17 maja 2011

Emprex dartboard under linux

Couple days ago I get from my friend Emprex dart board (http://www.emprex.com/02_products_02.php?id=348)




Of course there is no linux software for this device, so I decide to create one.
First think was to detect this device. After plug in 2.4GB reciver under /dev/usb directory device hiddev0 apear. There is also some hidrav device but hiddev is what I need.

To test if there is any data from dart board, I simply type:

cat /dev/usb/hiddev0 | hexdump

Then I presed any field (field 1 and 2) on board hexdump return code of pressed "button"

ex:

0000000 0081 ff00 0001 0000 0081 ff00 0000 0000
0000010 0082 ff00 0001 0000 0082 ff00 0000 0000

It no hard to figure out that there are 2 codes on one pres (one on key down and one on up). First numbers its offset of data (0000000) then code of key - field pressed (0081), the last sequence (I'm preety sure) is state of key - down or up (ff00 0001 0000 - for down, ff00 0000 0000 - for up).

The problem for using this device is name of this device in linux system. Sometimes it apear under hiddev0 or hiddev1 (depends on other devices plugged in to computer like mouse).
The best solution for this is to write udev rule.
My steps to create one is:

1) Read something about udev rules :) http://www.reactivated.net/writing_udev_rules.html#syntax
2) Check what information system provides obout this device, simply type udevadm info --attribute-walk --name=/dev/usb/hiddev0
3) Create file ex: /etc/udev/rules.d/10-emprex.rules with

KERNEL=="hiddev*", SUBSYSTEM=="usb", ACTION=="add", ATTRS{idVendor}=="046e", ATTRS{idProduct}=="d300", MODE="0666", NAME="usb/emprexdart", SYMLINK+="emprexdart"

4) Restart udev system: restart udev
5) Type udevadm monitor and plug in device, or test your new rule with udevadm test /devices/pci0000:00/0000:00:1d.0/usb5/5-2/5-2:1.1/usb/hiddev0 (device path if from udevadm info command).
6) If you have a trouble with rule try to stop udev (stop udev) and start it in debug mode (udevd --debug)
7) Also have in mind that there is ATTRS rule (look at udevadm info) not ATTR (I made that mistake :))

To figure out that key number is a field on dart board I created simple PHP script:


$filename = "/dev/emprexdart";
$sMapFile = "map.bin";

if(!file_exists($sMapFile)) {
$map = array();
}
else {
$map = unserialize(file_get_content($sMapFile));
}

$handle = fopen($filename, "r");
$hexCode = dechex(ord(" "));
$iCount = 0;
while( true ) {
$contents = fread($handle, 8);
if(ord($contents[4]) == 0) {
echo "Key up (".bin2hex($contents[4])."),\t int code: ". ord($contents[0])."\n";
}
else {
echo "Key down (".bin2hex($contents[4])."),\t int code: ". ord($contents[0])."\n";
// writing key code to map
$map[$iCount] = ord($contents[0]);
echo $iCount." ".(floor($iCount/4)+1)." ".(($iCount%4)+1)."\n";
exec("espeak '".(floor($iCount/4)+1)." ".(($iCount%4)+1)."'");
$iCount++;

}
//echo 'hidraw4 = asci: '.$contents[0].', hex: '.bin2hex($contents)."\n";
if($iCount == 85) {
// saving map file
file_put_contents($sMapFile, serialize($map));
break;
}
}
fclose($handle);


This code create map.bin file with mapping dart board fields (from 0 to 84) to numeric representation. It's just serialized array. Creating map file required pressing every field from dart board, to avoid mistake I use espeak linux tool to tell me which key I had presed. The result of this is my map file:


a:85:{i:0;i:65;i:1;i:129;i:2;i:97;i:3;i:33;i:4;i:66;i:5;i:130;i:6;i:98;i:7;i:34;
i:8;i:67;i:9;i:131;i:10;i:99;i:11;i:35;i:12;i:68;i:13;i:132;i:14;i:100;i:15;i:36
;i:16;i:69;i:17;i:133;i:18;i:101;i:19;i:37;i:20;i:70;i:21;i:134;i:22;i:102;i:23;
i:38;i:24;i:71;i:25;i:135;i:26;i:103;i:27;i:39;i:28;i:72;i:29;i:136;i:30;i:104;i
:31;i:40;i:32;i:73;i:33;i:137;i:34;i:105;i:35;i:41;i:36;i:74;i:37;i:138;i:38;i:1
06;i:39;i:42;i:40;i:75;i:41;i:139;i:42;i:107;i:43;i:43;i:44;i:76;i:45;i:140;i:46
;i:108;i:47;i:44;i:48;i:77;i:49;i:141;i:50;i:109;i:51;i:45;i:52;i:78;i:53;i:142;
i:54;i:110;i:55;i:46;i:56;i:79;i:57;i:143;i:58;i:111;i:59;i:47;i:60;i:80;i:61;i:
144;i:62;i:112;i:63;i:48;i:64;i:81;i:65;i:145;i:66;i:113;i:67;i:49;i:68;i:82;i:6
9;i:146;i:70;i:114;i:71;i:50;i:72;i:83;i:73;i:147;i:74;i:115;i:75;i:51;i:76;i:84
;i:77;i:148;i:78;i:116;i:79;i:52;i:80;i:57;i:81;i:89;i:82;i:4;i:83;i:3;i:84;i:1;
}


To test my idea I created simple scrit which should read poins throw at dart board.


$filename = "/dev/emprexdart";
$sMapFile = "map.bin";

$map = unserialize(file_get_contents($sMapFile));


$handle = fopen($filename, "r");
$hexCode = dechex(ord(" "));
$iCount = 0;
while( true ) {
$contents = fread($handle, 8);
if(ord($contents[4]) == 0) {
//echo "Key up (".bin2hex($contents[4])."),\t int code: ". ord($contents[0])."\n";
}
else {
//echo "Key down (".bin2hex($contents[4])."),\t int code: ". ord($contents[0])."\n";
$iNum = array_search(ord($contents[0]), $map);
$value = 0;
// czytamy

switch($iNum) {
case 80:
$value=50;
break;
case 81:
$value=60;
break;
case 82:
$value="Bounce out";
break;
case 83:
$value="Dart miss";
break;
case 84:
$value="Next player";
break;
default:
$segment=floor($iNum/4)+1;
$pole=(($iNum%4)+1);
if($pole==1)
$value=$segment*3;
elseif($pole==2)
$value=$segment;
elseif($pole==3)
$value=$segment*2;
else
$value=$segment;
}
//echo "Numer: $iNum, Pole: $pole, Segment: $segment, wartość: $value\n";
exec("espeak '".$value."'");
}
}
fclose($handle);


And that works pretty well :)