1. Logitech Extreme 3D pro
Deze Logitech Extreme 3D Pro heb ik gekregen op mijn verjaardag van 2013. Hij heeft een USB aansluiting en is daarom digitaal. Dit in tegenstelling tot de verouderde analoge joysticks die via de gameport werken, zoals de Microsoft SideWinder 3D Pro (zie onder). Onder Ubuntu meldt de joystick zich automatisch aan en is dus hotpluggable. Anno 2014 kent latop default al een js0, dus wordt js1 geopend. Verder wordt er ook een stream met de naam event9 geopend.
Een test kan worden uitgevoerd met jstest /dev/input/js1 of nog leuker met cat /dev/input/js1 dat de output laat zien die wij willen gebruiken. cat /dev/input/event9 laat zijn dat ook dit data spuugt, maar jstest kan er geen joystick in zien.
Door het bestand /dev/input/js1 byte voor byte uit te lezen kan er met sdlBasic een quick en dirty programmaatje worden gemaakt om de joystick geluidjes te laten maken. Het blijkt dat er telkens 8 bytes worden uitgespogen om een verandering aan te geven. De eerste 3 bytes zijn een LSB-tijdcode. Byte 4 is altijd 0. De volgende 2 bytes geven de gewijzigde waarde. De laatste 2 bytes geven aan om welke switch of as het gaat.
Dit document gaat over joysticks die op een PC aangesloten kunnen worden. Tegenwoordig zijn dit alle joysticks met een USB aansluiting. Voor de Arduino joystick module kijk op deze pagina.
- Test joystick
|
- Een test op een Linux PC kan worden uitgevoerd met cat /dev/input/js0 dat de output laat zien die wij willen gebruiken. Meer informatie kan worden verkregen door wat kleine programma`s te installeren. Na een sudo apt install jstest-gtk kan je een joystick testen met jstest-gtk in een terminal.
- Als python geïnstalleerd is dan kan je een joystick ook testen met python3 /var/www/html/robotigs/software/Dashboard/joystickTest.pyx.py in een terminal.
|
- Logitech Extreme 3D pro
|
Deze Logitech Extreme 3D Pro heb ik gekregen op mijn verjaardag van 2013. Hij heeft een USB aansluiting en is daarom digitaal. Onder Ubuntu meldt de joystick zich automatisch aan en is dus hotpluggable.
Door het bestand /dev/input/js1 byte voor byte uit te lezen kan er met sdlBasic een quick en dirty programmaatje worden gemaakt om de joystick geluidjes te laten maken. Het blijkt dat er telkens 8 bytes worden uitgespogen om een verandering aan te geven. De eerste 3 bytes zijn een LSB-tijdcode. Byte 4 is altijd 0. De volgende 2 bytes geven de gewijzigde waarde. De laatste 2 bytes geven aan om welke switch of as het gaat. |
- Legacy Microsoft SideWinder 3D pro
|
Een introductie over de werking van analoge legacy linux joysticks kan worden gevonden op epanorama.net. Deze joystick http://www.freelink.cx/joystick.html (Microsoft® SideWinder™ 3D Pro) incorporates digital-optical technology for precision, speed, and reliability. Hij heeft geen aansluitingen op de pins 12 en 15 in zijn stekker. Dit leert ons direct dat het geen feedback joystick is, want feedback joysticks gebruiken de midi pins om de feedback te ontvangen van de pc. Verder kent de gameport enkele dubbel uitgevoerde pins, die in mijn stekker niet zijn opgenomen, oftewel, die pins ontbreken gewoon. Het betreft de pins 8 en 9 die beiden een duplicaat van pin 1 (5V) zijn. Verder is pin 5 niet uitgevoerd, die een duplicaat van pin 4 (Ground) is.
De communicatie tussen pc en deze joystick kan op 2 manieren verlopen:
-
Analoog Eigenlijk is er geen communicatie in dit geval. De pins in de gameport stekker zijn direct aangeloten op de schakelaars en potmeters in de joystick. Dit levert uiteraard een beperking op in het aantal functies dat in een joystick kan worden opgenomen. Zo zijn er maximaal 4 buttons en 4 potmeters. Helaas is het niet mogelijk om deze joystick pin 1 van een aarde te voorzien in plaats van 5Vdc. Daaruit blijkt dat hij kennelijk gevoed moet worden om ook in de analoge modus correct te kunnen werken. Dat is jammer, want anders was het bijzonder simpel geweest om een spanningsdeler op te bouwen met 1 weerstand die het hele meetbereik zou benutten.
-
Microsoft Digital Overdrive protocol Om deze beperking te omzeilen werd het populair om de meting al in de joystick uit te voeren en de meetresultaten digitaal door te geven naar de pc via een seriëel protocol. Dit protocol voedt zelfstandig continu data naar de gameport van de PC. Indien u ook een uitgang nodig heeft omdat uw joystick van het type feedack is, dan worden hiervoor de MIDI-pins in de gamepoort gebruikt.
Microsoft Digital Overdrive protocol is supported by Microsoft SideWinder 3D Pro. There is one caveat with the 3D Pro. There are 9 buttons reported, although the joystick has only 8. The 9th button is the mode switch on the rear side of the joystick. However, moving it, you reset the joystick, and make it unresponsive for about a one third of a second. Furthermore, the joystick will also re-center itself, taking the position it was in during this time as a new center position.
Het is mij gebleken dat in zeer bijzondere gevallen de joystick kan vastlopen. In dat geval moet de joystick worden reset terwijl de connector nergens aan verbonden is. De joystick wordt gereset door de •/•• schakelaar aan de achterkant om te zetten. Deze schakelaar maakt een keuze tussen 2 verschillende emulaties. Enkel in stand • is het mogelijk om de throttle te gebruiken en daarom gebruiken wij die stand. In stand •• is het echter mogelijk om de switches van de joystick iets nauwkeuriger uit te lezen. De 4 buttons in de voet van de joystick zijn op deze manier niet te bereiken.
Analoog - Hierbij zijn de assen van de stick verbonden met potentiometers van 100klin.
Digitaal - Hierbij zijn de assen van de stick verbonden met schakelaars.
Female connector op mrBlue |
|
Pin |
Joystick theorie |
Robot |
Sidewinder stand • |
Stand •• |
1 |
XY1 (+5v) |
Joystick A |
5Vdc |
Voeding +5Vdc |
Voeding +5Vdc |
2 |
Switch 1 |
Joystick A |
D6 |
Fire Button |
|
3 |
X1 |
Joystick A |
ADC0 (C0) |
X coordinate stick |
|
4 |
Ground (for switch 1&2) |
Joystick A |
Aarde |
Voeding Ground |
Voeding Ground |
5 |
Ground (for switch 3&4) |
Joystick A |
Leeg |
|
|
6 |
Y1 |
Joystick A |
ADC1 (C1) |
Y coordinate stick |
|
7 |
Switch 2 |
Joystick A |
D5 |
Top button |
|
8 |
NC |
- |
Leeg |
|
|
9 |
XY2 (+5v) |
Joystick B |
Leeg |
|
|
10 |
Switch 3 |
Joystick B |
D4 |
Button boven |
|
11 |
X2 |
Joystick B |
ADC2 (C2) |
Draaien |
|
12 |
NC |
PC=>MIDI |
Leeg |
|
|
13 |
Y2 |
Joystick B |
ADC3 (C3) |
Throttle schuif |
|
14 |
Switch 4 |
Joystick B |
D3 |
Button onder |
|
15 |
|
|
|
|
|
De nauwkeurigheid met 1 weerstand van gelijke grootte als de joystick lijkt voldoende te zijn. Nadelen zijn dat er slechts 60% van het meetbereik wordt gebruikt en dat de functie niet lineair is. Er zal dus met iets als een conversietabel gewerkt moeten worden.
|
//This is an include, so no parsing needed
$qry2 = ("SELECT onderdelen.*,
soortonderdeel.id
FROM onderdelen
JOIN soortonderdeel ON onderdelen.idsrtodd = soortonderdeel.id
WHERE onderdelen.partid = $idpart
;");
$result2 = pg_query($con1, $qry2);
$row4 = pg_fetch_array($result2); //Make the fields accesible
foreach( $row4 AS $key => $val ){
$$key = stripslashes( $val );
}
$jaar = substr($changeddate,0,4);
$maand = substr($changeddate,5,2);
$dag = substr($changeddate,8,2);
$lastupdate = ("$jaar $maand $dag");
$prtsrttxt = get_txt($con1, 26, 1, $idsrtodd);
$jaar = substr($createddate,0,4);
$maand = substr($createddate,5,2);
$dag = substr($createddate,8,2);
$create_stamp = ("$dag $maand $jaar");
//GENERAL INFO FOR EVERY USER **************************************************
echo (" ");
echo (""); //Header
$tmp = get_txt($con1, 9, $koekje[2], 3923); //Fieldname at screen
if ($koekje[3]=="1") { //If this user wants to translate then
echo ("$tmp"); //If desired
}else{ echo ("$tmp"); } //Else just show if no need to translate
if ($koekje[0]>0 and $koekje[3]>0){ //User is logged in and translates
echo (" (edit)");
}
echo ("Algemene informatie test | ");
if ($codetext) { //Code like KY-026 or NA-41
echo ("");
$tmp = get_txt($con1, 9, $koekje[2], 3739); //Fieldname at screen
if ($koekje[3]=="1") { //If this user wants to translate then
echo ("$tmp"); //Class for colour
}else{ //So you are not allowed to translate
echo $tmp; //Show screenname of this field in user language
} //End of If you want to translate this fieldname at screen
echo ("  | ");
if ($codelink) {
echo ("$codetext");
}else{
echo ("$codetext");
} //End of if ($pinoutlink)
echo (" | ");
} //End of if ($codetext)
$tmp = get_txt($con1, 9, $koekje[2], 3741); //Fieldname at screen
if ($prijseurinclbtw > 0){ //Show price indication
$prijs_formatted = number_format($prijseurinclbtw / 100, 2, ',', '.');
echo ("$tmp   | ");
echo ("");
if ($urlprijs){
echo ("€  $prijs_formatted");
}else{
echo ("€  $prijs_formatted");
} //End of if ($urlprijs > "")
echo (" | ");
} //End of if ($prijseurinclbtw > 0)
if ($sketchtext) { //Link to OWN SOFTWARE
$tmp = get_txt($con1, 9, $koekje[2], 3738); //Fieldname at screen
echo ("$tmp   | ");
if ($sketchlink) {
echo ("$sketchtext");
}else{
echo ("$sketchtext");
} //End of if ($sketchlink)
echo (" | ");
} //End of if ($sketchtext)
$tmp = get_txt($con1, 9, $koekje[2], 3744); //Fieldname at screen
if ($pinouttext) { //Pinout
echo ("$tmp   | ");
if ($pinoutlink) {
echo ("$pinouttext");
}else{
echo ("$pinouttext");
} //End of if ($pinoutlink)
echo (" | ");
}
$tmp = get_txt($con1, 9, 1, 3735); //Fieldname at screen
$tmp2 = get_txt($con1, 26, 1, $row4[16]); //Group name in its language
echo ("$tmp   | "); //MENU GROUP
echo ("");
echo ("$tmp2 | ");
if ($koekje[0]>0 and $koekje[3]>0){ //User is logged in and translates
$tmp = get_txt($con1, 9, $koekje[2], 3918); //Fieldname at screen LANGUAGE
if ($row4[26] == 1) {$imgname = "nederland.png";}
if ($row4[26] == 2) {$imgname = "deutschland.png";}
if ($row4[26] == 3) {$imgname = "uk.png";}
if ($row4[26] == 4) {$imgname = "france.png";}
echo ("$tmp | ");
echo ("");
echo (" | ");
$tmp = get_txt($con1, 9, $row4[27], 3919); //Fieldname at screen
$statuskey = get_status_key($con1, $status);
$tmp2 = get_txt($con1, 61, $koekje[2], $statuskey); //Fieldname at screen
echo ("$tmp   | "); //STATUS
echo ("$status $tmp2 | ");
}
$tmp = get_txt($con1, 9, 1, 3966); //Fieldname at screen
echo ("$tmp   | "); //LAST CHANGED
$tmp = substr($changeddate, 0, 10);
echo ("$tmp | ");
echo (" ");
return;
////////////////////////////////////////////////////////////////////////////////
function get_status_key($con, $num){
$qry = ("SELECT id FROM status WHERE nr = $num ;");
$result = pg_query($con, $qry);
$row = pg_fetch_array($result);
return ("$row[0]");
}
// LIST ALL SOFTWARE THAT WILL RUN OM THIS PART *****************************
// INCLUDED IN ALL ROBOTS TO CHECK THEIR DIRECTORY
$qry2=("SELECT tblsystext.txtname
, software.urlmain
, software.sftname
, software.sftid
, software.controller
FROM software
LEFT JOIN tblsystext ON software.controller = tblsystext.recordid
WHERE position(' $idpart ' in software.prtlst) > 0
AND software.rstid = 2
AND tblsystext.lanid = $koekje[2]
AND tblsystext.txlid = 19
ORDER BY sftname ASC
, controller ASC
;");
$result = pg_query($con1, $qry2);
if (pg_num_rows($result)>0) { //Only if at least 1 program exists
echo (' ');
echo (""); //Header
$tmp = get_txt($con1, 9, $koekje[2], 3922); //Fieldname at screen
if ($koekje[3]=="1") { //If this user wants to translate then
echo ("$tmp"); //If desired
}else{ echo ("$tmp"); } //Else just show if no need to translate
echo (" |
---|
");
while($col = pg_fetch_array($result)){
$col[0] = ("$col[0]");
echo (" ");
echo ("$col[2]   | ");
echo ("$col[0] | ");
if ($koekje[0] > 0 and $koekje[3]>0){ //User is logged in and translates
echo ("");
echo ("(Edit) | ");
}
echo (' ');
}
echo (' ');
}
?>
// LIST ALL ROBOTS THAT INCLUDE THIS PART ***********************************
// $idpart
// INCLUDED IN ALL ROBOTS TO CHECK THEIR DIRECTORY
$qry_bpp=("SELECT farmbotversie.id
, farmbotversie.part_list
, tblsystext.txtname
FROM farmbotversie
LEFT JOIN tblsystext ON farmbotversie.id = tblsystext.recordid
WHERE tblsystext.txlid = 27
AND tblsystext.lanid = $koekje[2]
ORDER BY tblsystext.txtname ASC
;");
$result_bpp = pg_query($con1, $qry_bpp);
$header = 0;
while($colbpp = pg_fetch_array($result_bpp)){ //Only if at least 1 robot exists
if(substr_count($colbpp[1], " $idpart ") > 0){
if ($header == 0) {
echo ("");
echo (""); //Header
$tmp = get_txt($con1, 9, $koekje[2], 3879); //Fieldname at screen
if ($koekje[3]=="1") { //If this user wants to translate then
echo ("$tmp"); //If desired
}else{ echo ("$tmp"); } //Else just show if no need to translate
echo (" | ");
$header++;
} //End of if ($header == 0)
echo ("");
echo ("$colbpp[2]");
//Show software van deze bot
$qry=("SELECT software_basedir
FROM farmbotversie
WHERE id = $colbpp[0];");
$result = pg_query($con1, $qry);
$col = pg_fetch_array ($result ,0);
$dir1 = "/var/www/html/robotigs/software/$col[0]";
$arr1 = glob($dir1, GLOB_ONLYDIR);
if (isset($arr1[0])){
$folder = $arr1[0];
$countertmp = 0;
echo ("");
foreach (scandir($folder, 1) as $filename) {
if ($filename<>"." AND $filename <>".."){
if ($countertmp == 0){
$countertmp++;
} //End of if ($countertmp == 0)
$tmp = "/";
$dir = substr($folder, 14) ."/";
$link = $tmp .$dir .$filename;
$showname = substr($filename, 0 ,-4);
echo ("- ");
echo "$showname";
echo ("
");
} //End of if ($filename<>"." AND $filename <>".."){
} //End of foreach (scandir($folder, 1) as $filename)
echo ("");
} //End of if (isset($arr1[0]))
echo (" | ");
}
}
if ($header > 0){
echo (" ");
}
?>
Warning: include(/var/www/html/robotigs/includes/wishlistperpart.php): Failed to open stream: No such file or directory in /var/www/html/robotigs/includes/parts_header.php on line 128
Warning: include(): Failed opening '/var/www/html/robotigs/includes/wishlistperpart.php' for inclusion (include_path='.:/usr/share/php') in /var/www/html/robotigs/includes/parts_header.php on line 128
| //Location:/robotigs/includes/ picts_show.php
// Requieres: $pict_list and several picture handling javascript routines
//*** FROM HERE ON THE PICTURE CORNER STARTS -----------------------------------
if ($pict_list){ //There are 1 or more pictures to be printed
echo ("");
$counter = 0;
$aantal = str_word_count($pict_list, 0, "0123456789");
$lijst = str_word_count($pict_list, 1, "0123456789");
while($counter < $aantal){
$pictkey = $lijst[$counter];
$qryy1 = ("SELECT *
FROM illustrations
WHERE ill_id = $pictkey
;");
$resulty1 = pg_query($con1, $qryy1);
while($row = pg_fetch_array($resulty1)){ //Fields accesible by their name
foreach( $row AS $key => $val ){
$$key = stripslashes( $val );
}
}
$image = ("/robotigs/documentatie/plaatjes/$ill_name");
$pictfull = str_replace('_small', '', $image); //Pictfull=biggest version
$alt = ("Just wait till your 57....");
$txtname = get_txt($con1, 18, $koekje[2], $ill_id); //Robot illustrations subtitles
echo ("");
echo (' ');
$nummer = $counter + 1;
$txtAfb = get_txt($con1, 9, $koekje[2], 3865); //Pic Afb
if ($koekje[0]>0 and $koekje[3]>0){ //User is logged in and translates
echo ("$txtAfb $nummer: ");
echo ("$txtname");
echo (" ");
echo ("Ontkoppelen ");
if ($counter==0){
echo ("Up ");
}else{
echo ("Up ");
}
if ($nummer == $aantal){
echo ("Down");
}else{
echo ("Down");
}
}else{ //Else of if Meaning the current useer is logged in
echo ("$txtAfb $nummer: $txtname");
}
echo (''); //End of this picture
$counter++; //Prepair for next picture
}
echo (' | '); //All pictures are printed
} //End of if there are 1 or more pictures to be printed ----------------------
?>