Arduino Trimmrad zur Einstellung der Flugzeug-Trimmung (Teil 2)
Grundlagen
In diesem Artikel geht es um den Nachbau eines Trimmrades für Flugsimulatoren auf Basis eines Arduino-Controllers. Die Grundlagen können in vorhergehenden Artikeln nachgelesen werden.
Arduino Trimmrad zur Einstellung der Flugzeug-Trimmung (Teil 1)
Quelltext
Der Quelltext im ersten Teil dieser Artikel-Serie hat die grundlegenden Möglichkeiten aufgezeigt. Es hat sich jedoch herausgestellt, dass die Steuerung mit der Simulation von Buttons zur Änderung der Trimmung (NOSE UP, NOSE DOWN) nur sehr unzuverlässig funktioniert und teilweise völlig unvermittelt maximale Trimmungen eingestellt werden.
Anstelle des “Drückens von Buttons” beim Drehen des Drehwinkelgebers hat es sich als zielführender erwiesen, Eingaben über die Z-Achse durchzuführen. Das Arduino HID-Project, auf welchem diese Anwendung basiert, ermöglicht es, Werte zwischen -128 und 127 einzustellen. Diese Funktionalität kann genutzt werden, um die Trimmung zu steuern.
Der Flugsimulator erwartet Werte wischen -100 und 100 Prozent. Dies ist ein Wertebereich, mit dem man gut arbeiten kann, weshalb im nachfolgenden Script der Wertebereich entsprehend limitiert ist.
Der verwendete Drehwinkelgeber KY-040 besitzt zusätzlich einen Knopf, der ausgelöst wird, wenn der Drehknauf gedrückt wird. Diesen Knopf habe ich dazu verwendet, den aktuellen Z-Wert auf 0 zurückzusetzen. Es ist somit quasi ein Reset-Knopf für die aktuell eingestellte Trimmung.
/* * Arduino HID Trim Wheel * * Author Jan Wischniowski * URL https://www.metanox.de/ * Version 0.6.0 * License GNU General Public License, Version 3 */ #include "HID-Project.h" #define CLOCKWISE true #define COUNTERCLOCKWISE false // Arduino const int arduino_led_1 = LED_BUILTIN; // Rotary 1 const int rotary_1_switch = 2; // Pin 2 to SW (switch pin) on rotary encoder. const int rotary_1_clock = 9; // Pin 9 to CLK (clock) on rotary encoder. const int rotary_1_data = 8; // Pin 8 to DT (data) on rotary encoder. int rotary_1_position = 0; int rotary_1_z_axis = 0; int rotary_1_clock_old; int rotary_1_clock_new; boolean rotary_1_direction; boolean rotary_1_switch_old = true; boolean rotary_1_switch_new = true; // LED 1 const int led_1_red = 5; const int led_1_green = 6; const int led_1_blue = 7; void setup() { pinMode(arduino_led_1, OUTPUT); pinMode(rotary_1_switch, INPUT_PULLUP); pinMode(rotary_1_clock, INPUT); pinMode(rotary_1_data, INPUT); pinMode(led_1_red, OUTPUT); pinMode(led_1_green, OUTPUT); pinMode(led_1_blue, OUTPUT); rotary_1_clock_old = digitalRead(rotary_1_clock); // Sends a clean report to the host. This is important on any Arduino type. Gamepad.begin(); Serial.begin(9600); } void loop() { rotary_1_switch_new = digitalRead(rotary_1_switch); if (rotary_1_switch_new != rotary_1_switch_old) { if (!rotary_1_switch_new) { digitalWrite(arduino_led_1, HIGH); // Button 1 drücken und Z-Achse auf 0 zurücksetzen. Gamepad.press(1); rotary_1_z_axis = 0; Gamepad.write(); Serial.println ("Encoder Button: Pressed"); digitalWrite(led_1_red, LOW); digitalWrite(led_1_green, LOW); digitalWrite(led_1_blue, HIGH); } else { digitalWrite(arduino_led_1, LOW); Gamepad.release(1); Gamepad.write(); Serial.println ("Encoder Button: Released"); digitalWrite(led_1_red, LOW); digitalWrite(led_1_green, LOW); digitalWrite(led_1_blue, LOW); } } rotary_1_switch_old = rotary_1_switch_new; // Beim Drehen werden zwischen jedem Einrasten zwei Zustände durchlaufen. // // Stufe 1: Drehung im Uhrzeigersinn (rechts) // rotary_1_clock: 0 -> 1 // rotary_1_data: 1 -> 0 // // Stufe 2: Drehung gegen Uhrzeigersinn (links) // rotary_1_clock: 0 -> 1 // rotary_1_data: 0 -> 1 rotary_1_clock_new = digitalRead(rotary_1_clock); // Bei jeder Drehung in eine beliebige Richtung soll bei Stufe 2 eine Aktion ausgeführt werden. Stufe 1 wird übersprungen. Andernfalls würden bei jeder Drehung zwei Aktionen ausgeführt werden. if ((rotary_1_clock_new != rotary_1_clock_old) && (true == rotary_1_clock_new)) //if ((rotary_1_clock_new != rotary_1_clock_old)) { //Serial.print("VAR rotary_1_clock: "); //Serial.println(digitalRead(rotary_1_clock)); //Serial.print("VAR rotary_1_data: "); //Serial.println(digitalRead(rotary_1_data)); if (digitalRead(rotary_1_data) != rotary_1_clock_new) { // Clockwise rotary_1_position++; rotary_1_direction = CLOCKWISE; } else { //Counterclockwise rotary_1_position--; rotary_1_direction = COUNTERCLOCKWISE; } if (CLOCKWISE == rotary_1_direction) // Turning right will turn on red led. { Serial.println ("Encoder Rotation: Clockwise"); digitalWrite(led_1_red, HIGH); digitalWrite(led_1_green, LOW); rotary_1_z_axis += 1; if (100<rotary_1_z_axis) { rotary_1_z_axis = 100; } } else // Turning left will turn on green led. { Serial.println("Encoder Rotation: Counterclockwise"); digitalWrite(led_1_red, LOW); digitalWrite(led_1_green, HIGH); rotary_1_z_axis -= 1; if (-100>rotary_1_z_axis) { rotary_1_z_axis = -100; } } Serial.print("VAR rotary_1_position: "); Serial.println(rotary_1_position); Serial.print("VAR rotary_1_z_axis: "); Serial.println(rotary_1_z_axis); } rotary_1_clock_old = rotary_1_clock_new; Gamepad.rzAxis(rotary_1_z_axis); Gamepad.write(); }
Einstellungen im Flugsimulator
Ausblick
In den nächsten Teilen der Serie soll die Anordnung der Jumper Wires (Überbrückungskabel) geordnet werden. Außerdem ist geplant mit einem zweiten Drehregler, nicht nur die Höhen-, sondern auch die Quertrimmung anpassen zu können. Eine Quertrimmung kann verwendet werden, um bei Flugzeugen mit mehreren Triebwerken die Drehung bei einem einseitigen Ausfall eines Triebwerkes auszugleichen.