Joystick

Joystick zur Steuerung von Lufschiffen

Kleiner Joystick:

 
Nur zwei Achsen und zwei Taster. Analogpotis sehr unlinear.

Nicht in Betrieb

Großer Joystick:

Der große Joystick hat vier Achsen und bis zu 14 Tasten (davon werden zurzeit nur die acht am Knüppel genutzt)
Zuerst wurde die ursprüngliche Ansteuerhardware entfernt.
Die Achsen haben je ein Poti, deren Mittelanschlüsse auf die ADC-Eingänge des Arduino FIO gehen. Die anderen zwei Anschlüsse gehen auf Masse und VCC, so kann am Mittelanschluss eine Spannung zwischen Vcc und 0V eingestellt werden. Die Tasten haben einen gemeinsamen Anschluss der auf Masse gelegt ist. Die anderen Anschlüsse der Tasten sind über einen Widerstand jeweils auf Vcc geschalten. Ein weiterer Widerstand führt auf die Digitaleingänge des Arduino. (Standard Tasterbeschaltung)
Der Controller und die anderen Komponenten sind im inneren fliegend verdrahtet. Die Adruino-programmierschnittstelle ist nach außen geführt, bringt aber nicht so viel, da man zum Programmieren das Xbee-Modul abstecken muss, und dazu den Joystick sowieso aufschrauben muss. Mit einem Adapterkabel kann man das Xbee-Modul auch an der Programmierschittstelle anschließen (macht das Debuggen einfacher/bessere Reichweite?)
Ein Akku ist eingebaut, und kann mit einem Schalter auf der Unterseite eingeschalten werden. ("Analog" = AUS, "Digital" = AN). Eine Ladeschaltung ist integriert, aber der Anschluss dafür noch nicht von Außen zugänglich.
Die rote LED zeigt an, dass der Joystick aktiv ist. (unregelmäßiges Blinken - normal Mode; Dauerlicht - legacy Mode)
Die grüne LED zeigt an, dass der Schubregler in der Neutralstellung ist.

    
Von links nach rechts: Original Joystick, Platine entfernt, Widerstände für die Tasten angeschlossen, Controller angeschlossen
    
Von links nach rechts: Fertiger Joystick offen, Oberteil, Unterteil, Außen

Schaltplan (Anhang unten)

Betriebsmodi

  1. normal Mode: Default beim Einschalten; Das Protokoll implementiert eine Authentifikation der Steuerdaten.
  2. legacy Mode: Der Joystick verwendet das alte Datenprotokoll ohne Sicherheitsfunktionen (wird aktiviert durch gedrückthalten der linken und rechten Taste am Knüppel beim Einschalten des Joysticks.)

 

Datenprotokoll - legacy Mode

Die Tasten werden einzelnen Bits eines Bytes zugeordnet: Taste gedrückt => Bit = 1. Die ADC-Werte werden durch 4 geteilt. Damit ergeben sich fünf Bytes an Steuerdaten. Übertragen werden sie als ASCII-Codierte Dezimalzahlen mit einem Leerzeichen als Trennzeichen, sowie einem Linefeed (\n) als Endzeichen. Die Daten werden in folgender Reihenfolge gesendet: "X-Achse Y-Achse Z-Achse W-Achse Tastenbyte\n" Die Firmware implementiert noch eine Neutralzone (128-x bis 128+y -> 128 (x,y individuell für jede Aschse festgelegt)) und Endanschläge (0 bis a -> 0 bzw b bis 255 -> 255), da diese Werte schwer bis unmöglich zu erreichen sind. Es werden ca. 20 Datentelegramme pro Sekunde über die serielle Schnittstelle zum Xbee-Modul gesendet.

Bsp. Datentelegramm:

128 128 190 50 0   // X,Y-Achsen neutral, Z,W-Achsen ausgelenkt, keine Taste gedrückt
0 255 128 128 74   // X,Y-Achsen Endanschlag, Z,W-Achsen neutral, drei Tasten gedrückt

 

Datenprotokoll - normal Mode

Das Protokoll ähnelt dem legacy Mode, nur werden die Daten als Hexadezimalzahlen übertragen. Dazu kommt eine AES-CMAC Signatur mit Sequenznummer. Die Sequenznummer hat 32-Bit und wird Hexadezimal gesendet. Danach folgt, getrennt durch einen Strichpunkt (kein Leerzeichen), der CMAC (16 Byte). Dieser wird escaped. "\", " ", "\r" und "\n" werden ersetzt durch einen Backslash (Escape-Zeichen) und das Originalzeichen plus 10.

Für die Signatur wird ein 16 Byte Block zusammengestellt:

X-Achse Y-Achse Z-Achse W-Achse
tasten (MSB)seq-Nr. seq-Nr. seq-Nr.
seq-Nr.(LSB) 'D' 'a' 'e'
'd' 'a' 'l' 'u'


Und dann mit dem 128-Bit AES-Verfahren verschlüsselt.


Bsp. Datentelegramm:

80 80 80 80 23 53a7c4;fdsgzu8i47hgdj3s 
a3 3f 80 80 00 53a7c5;54,fs;asu.-agfdd


Firmware

 

Neu (mit Crypto):

/* Joystick Ansteurung - Sender */

#include <aes.h>
#include <EEPROM.h>

//Pin Tastenzuordnung
#define TASTE1 2
#define TASTE2 3
#define TASTE3 4
#define TASTE4 8
#define TASTE5 6
#define TASTE6 7
#define TASTE7 5
#define TASTE8 9

//Pin-Achsenzuordnung
#define ACHSEX A5
#define ACHSEY A6
#define ACHSEZ A4
#define ACHSEW A7

//AES init
aes128_ctx_t ctx;
//AES-Key
uint8_t key[16]  = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
//AES-Block
uint8_t data[16] = { 
  ' ', '\r', '\n', '\\', 'a', '*', 0x00, 0x00, 0x00, 'D', 'a', 'e', 'd', 'a', 'l', 'u' };

//Tasten und Achsen
uint8_t temp = 0;
uint8_t taste1 = 0;

//0: normaler Modus, 1: legacy Modus
uint8_t legacy = 0;

//Sequenznummer
uint32_t seq = 0;


void setup() {

  //für for-Schleifen
  int i = 0;
  //Serielle Schnittstelle
  Serial.begin(9600);
  //pins initialisieren
  pinMode(13, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(ACHSEY, INPUT);
  pinMode(ACHSEX, INPUT);
  pinMode(ACHSEZ, INPUT);
  pinMode(ACHSEW, INPUT);
  pinMode(TASTE1, INPUT);
  pinMode(TASTE2, INPUT);
  pinMode(TASTE3, INPUT);
  pinMode(TASTE4, INPUT);
  pinMode(TASTE5, INPUT);
  pinMode(TASTE6, INPUT);
  pinMode(TASTE7, INPUT);
  pinMode(TASTE8, INPUT);


  //legacy-Mode = altes Datenformat
  if ((digitalRead(TASTE6) == LOW) &&  (digitalRead(TASTE7) == LOW))
    {
      legacy = 1;
      Serial.println("legacy mode activ");
    }


  //key aus dem EEPROM lesen
  for (i=0;i<16;i++)
  {
    key[i] = EEPROM.read(i+6);
  }
  //print_data(key);
  aes128_init(key, &ctx); /* generating the round keys from the 128 bit key */


  //Sequenznummer aus EEPROM lesen
  seq = EEPROM.read(0);
  seq = seq << 8;
  seq = seq + EEPROM.read(1);
  seq = seq << 8;
  seq = seq + EEPROM.read(2);
  seq = seq << 8;
  seq = seq + EEPROM.read(3);
  
  //Sequenznummer manuell erhöhen
  if ((digitalRead(TASTE5) == LOW) &&  (digitalRead(TASTE7) == LOW))
  {
    seq = seq + 10500;
  } 
//  Serial.println(seq); //DEBUG
  seq = seq + 22000; //sicher ist sicher
  
  
  //print_escape(data);
  //DEBUG
//  delay(1000);
//  Serial.println();
//  Serial.println();
  //
  
  //init Abgeschlossen
  digitalWrite(13, HIGH);

}


void loop() {
  
  
  //digitalWrite(13, LOW); //Debug
  
  //Sequenznummer erhöhen
  seq++;
  
  //analoge Achsen auslesen
  temp = 0;

  
  //X-Achse
  temp = (analogRead(ACHSEX)>>2);
  // Neutrale Zone 128 und Endanschläge 0 bzw. 255 
  if ((temp <= 138) && (temp >= 122))
    temp = 128;
  if (temp < 52)
    temp = 0;
  if (temp > 217)
    temp = 255;
    
  data[0] = temp;
  if (legacy == 0) {
    Serial.print(temp, HEX);
  } else {
    Serial.print(temp);
  }
  Serial.print(" ");


  //Y-Achse
  temp = (analogRead(ACHSEY)>>2);
  if ((temp <= 138) && (temp >= 122))
    temp = 128;
  if (temp < 45)
    temp = 0;
  if (temp > 215)
    temp = 255;

  data[1] = temp;
  if (legacy == 0) {
    Serial.print(temp, HEX);
  } else {
    Serial.print(temp);
  }
  Serial.print(" ");


  //Z-Achse
  temp = (analogRead(ACHSEZ)>>2);
  if ((temp <= 135) && (temp >= 100))
    temp = 128;
  if (temp < 40)
    temp = 0;
  if (temp > 220)
    temp = 255;

  data[2] = temp; 
  if (legacy == 0) {
    Serial.print(temp, HEX);
  } else {
    Serial.print(temp);
  }
  Serial.print(" ");


  //W-Achse
  temp = (analogRead(ACHSEW)>>2);
  if ((temp <= 135) && (temp >= 120))
  {
    temp = 128;
    digitalWrite(12, HIGH);
  } 
  else {
    digitalWrite(12, LOW);
  }
  if (temp < 25)
    temp = 0;
  if (temp > 225)
    temp = 255;

  data[3] = temp;
  if (legacy == 0) {
    Serial.print(temp, HEX);
  } else {
    Serial.print(temp);
  }
  Serial.print(" ");


  //Tasten auslesen
  taste1 = 0;
  if (digitalRead(TASTE1) == LOW)
    bitSet(taste1, 0);   
  if (digitalRead(TASTE2) == LOW)
    bitSet(taste1, 1);
  if (digitalRead(TASTE3) == LOW)
    bitSet(taste1, 2);   
  if (digitalRead(TASTE4) == LOW)
    bitSet(taste1, 3);
  if (digitalRead(TASTE5) == LOW)
    bitSet(taste1, 4);   
  if (digitalRead(TASTE6) == LOW)
    bitSet(taste1, 5);
  if (digitalRead(TASTE7) == LOW)
    bitSet(taste1, 6);   
  if (digitalRead(TASTE8) == LOW)
    bitSet(taste1, 7);

  if (legacy == 0) {
    Serial.print(taste1, HEX);
  } else {
    Serial.print(taste1);
  }

  
  if (legacy == 0) 
  {
    Serial.print(" ");
    Serial.print(seq, HEX);

    //AES-Block erstellen
    data[4] = (uint8_t)taste1;
    data[8] = (uint8_t)(seq & 0x000000ff);
    data[7] = (uint8_t)((seq>>8) &0x000000ff);
    data[6] = (uint8_t)((seq>>16) & 0x000000ff);
    data[5] = (uint8_t)((seq>>24) & 0x000000ff);
    data[9] = 'D';
    data[10] = 'a';
    data[11] = 'e';
    data[12] = 'd';
    data[13] = 'a';
    data[14] = 'l';
    data[15] = 'u';

    //Signatur erstellen
    aes128_enc(data, &ctx);

    //restliche Daten abschicken
    Serial.print(";");
    print_escape(data);
  
  
  

    //Serial.println(seq % 16384);

    //Sequenznummer regelmässig im EEPROM sichern
    if ((seq % 32768) == 0)
    {
      /* Serial.println();
       Serial.println();
       Serial.println();
       Serial.println("EEPROM Write");
       Serial.println();
       Serial.println();
       Serial.println();
       Serial.println();
       */

      EEPROM.write(0, (uint8_t)((seq>>24) & 0x000000ff));
      EEPROM.write(1, (uint8_t)((seq>>16) & 0x000000ff));
      EEPROM.write(2, (uint8_t)((seq>>8) & 0x000000ff));
      //EEPROM.write(3, (uint8_t)(seq & 0x000000ff));

    }
  
    //Blinkende LED
    if ((seq % 17) == 0)
      digitalWrite(13, LOW);
    if ((seq % 23) == 0)
      digitalWrite(13, HIGH);
  }
  

  
  Serial.print('\n');
  //Warten
  delay(50);

}


//daten raw und escaped ausgeben
void print_escape(uint8_t *buffer)
{
  int i = 0;
  for (i = 0; i<16; i++)
  {
    if ((buffer[i] != '\n') && (buffer[i] != '\r') && (buffer[i] != '\\') && (buffer[i] != ' '))
    { 
      Serial.write(buffer[i]);
    } 
    else {
      Serial.write('\\');
      Serial.write(buffer[i]+10);
    }
  }

}


/*
//16-Byte Datenarry seriel ausgeben
void print_data(uint8_t *buffer)
{
  int i = 0;
  for (i = 0; i<16; i++)
  {
    Serial.print(buffer[i], HEX);
    Serial.print(" ");
  }
  // Serial.println();

}
*/

 

Alt:

/* Joystick Ansteurung - Sender */

#define TASTE1 2
#define TASTE2 3
#define TASTE3 4
#define TASTE4 8
#define TASTE5 6
#define TASTE6 7
#define TASTE7 5
#define TASTE8 9

#define ACHSEX A5
#define ACHSEY A6
#define ACHSEZ A4
#define ACHSEW A7


int temp = 0;
byte taste1 = 0;
//byte taste2 = 0;


void setup() {

  Serial.begin(9600);
  pinMode(13, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(ACHSEY, INPUT);
  pinMode(ACHSEX, INPUT);
  pinMode(ACHSEZ, INPUT);
  pinMode(ACHSEW, INPUT);
  pinMode(TASTE1, INPUT);
  pinMode(TASTE2, INPUT);
  pinMode(TASTE3, INPUT);
  pinMode(TASTE4, INPUT);
  pinMode(TASTE5, INPUT);
  pinMode(TASTE6, INPUT);
  pinMode(TASTE7, INPUT);
  pinMode(TASTE8, INPUT);

  digitalWrite(13, HIGH);
}


void loop() {

  //analoge Achsen auslesen
  temp = 0;

  temp = (analogRead(ACHSEX)>>2);
  // Neutrale Zone 128 und Endanschläge 0 bzw. 255 
  if ((temp <= 138) && (temp >= 122))
    temp = 128;
  if (temp < 52)
    temp = 0;
  if (temp > 217)
    temp = 255;
  Serial.print(temp);
  Serial.print(" ");

  temp = (analogRead(ACHSEY)>>2);
  if ((temp <= 138) && (temp >= 122))
    temp = 128;
  if (temp < 45)
    temp = 0;
  if (temp > 215)
    temp = 255;
  Serial.print(temp);
  Serial.print(" ");

  temp = (analogRead(ACHSEZ)>>2);
  if ((temp <= 135) && (temp >= 100))
    temp = 128;
  if (temp < 40)
    temp = 0;
  if (temp > 220)
    temp = 255;
  Serial.print(temp);
  Serial.print(" ");

  temp = (analogRead(ACHSEW)>>2);
  if ((temp <= 135) && (temp >= 120))
  {
    temp = 128;
    digitalWrite(12, HIGH);
  } 
  else {
    digitalWrite(12, LOW);
  }
  if (temp < 25)
    temp = 0;
  if (temp > 225)
    temp = 255;
  Serial.print(temp);
  Serial.print(" ");

  //Tasten auslesen
  taste1 = 0;
  if (digitalRead(TASTE1) == LOW)
    bitSet(taste1, 0);   
  if (digitalRead(TASTE2) == LOW)
    bitSet(taste1, 1);
  if (digitalRead(TASTE3) == LOW)
    bitSet(taste1, 2);   
  if (digitalRead(TASTE4) == LOW)
    bitSet(taste1, 3);
  if (digitalRead(TASTE5) == LOW)
    bitSet(taste1, 4);   
  if (digitalRead(TASTE6) == LOW)
    bitSet(taste1, 5);
  if (digitalRead(TASTE7) == LOW)
    bitSet(taste1, 6);   
  if (digitalRead(TASTE8) == LOW)
    bitSet(taste1, 7);

  Serial.print(taste1);
  Serial.print('\n');


  delay(50);

}


TODO:

  • Ladeanschluss (Mini-USB)
  • LED des Ladereglers nach außen legen (ERLEDIGT, scheint durch die vorderen Tasten durch)
  • Crypto (ERLEDIGT)
Anhänge:
Diese Datei herunterladen (joystick-schaltplan.ps)joystick-schaltplan.ps[ ]%2012-%10-%12 %1:%Okt%+02:00