Wednesday, October 26, 2016

Arduino with FSX (Part 4)

Rudder Pedals and Brakes — 


Early in the FSX Flight Training, one exercise uses rudder steering and brakes to taxi around an airport. That requirement was the only motivation required to build a set of rudder pedals with toe brakes for use with FSX. To be honest, rudder and brakes have been rarely used in the VFR flight training course, but I have an interest in flying older military aircraft that do not have auto-pilot, auto-rudder, and many modern conveniences. It has taken a bit of work to make the rudders and brakes function as desired, mainly because I want pedals that have some resistance to being applied, that they re-center as pressure is reduced, and that the distance and angle of my feet and legs provide good ability to apply the rudders and brakes in a comfortable manner.

The prototype pedals followed the design of Bruce May (How To Build Rudder Pedals, 2006). After using those pedals a short while, I was unhappy with the lack of smoothness of the pedals sliding when pressure was applied by my feet. The pedals worked smoothly when moved by hand, but the pressure of my feet and legs made the prototype fail to work satisfactorily. On the other hand, the rubber balls under the toe brakes provided excellent press tension and return  when pressure was released.


I chose to use potentiometers as the rotational sensor devices. A modified version of the Arduino Uno 8-axis, 40-button Joystick discussed in Part 3 of this blog series was used as the sensor controller. Only 3 axes (rudder, left brake, right brake) were attached to pins A0, A1, and A2. The potentiometers were connected between +5V and ground with the wiper being connected to an analog pin. To reduce noise, all other axes were connected to ground. No digital pins were connected as buttons.

In FSUIPC, the 3 axes were calibrated and worked fairly well. The largest issues I experienced were discussed above--pedals not operating smoothly.

In the second iteration, rotary encoders were used in place of the potentiometers. That test failed to be satisfactory, since the hardware linkage connecting to the encoders did not make them rotate through sufficient detents (clicks) to register a satisfactory signal. While a 90 degree turn of a potentiometer makes a big change in its resistance value, the same 90 degree turn of a rotary encoder is just 5 clicks which was not sufficient to provide an adequate signal. To use rotary encoders would require re-design of the sensor linkage, such as, using a track and gear. I found gears that fit 6mm rotary encoder shafts fairly easily at a reasonable cost on eBay, and the matching track was not too expensive, but the shipping was like $50 for 12 lbs of track. I just need about 2 feet of track, not 12 lbs. I might have to try to fabricate some track.

I reviewed the design of rudder pedals in FlightSim Rudder Pedals. That design used small plastic rollers to provide smooth pedal movements. In addition, springs were used to provide tension and centering. Those seemed like really needed improvements.

I also reviewed the design of FlightSim "RogerioS" DIY Rudder pedals and toe brakes. That design included drawer slides to provide smooth pedal action. The slides look pretty heavy duty, but I suspect they provide good rolling action to handle the strong pressure from legs and feet.

Another concern I have with my prototype is that as the aluminum bar rotates due to leg/foot pressure, the whole bar is also under pressure to the rear from both legs pushing towards the rear, with some relief when pressure is reduced by one or both legs. The rotational axis of that bar must be under quite a bit of pressure where the center axis bolt is located.

I have also been reducing friction between various connections that rub as the pedals are moved, such as, the pins that move the potentiometers. Milled edges in aluminum tend to be rough and need to be polished to reduce friction. Aluminum against aluminum caused much friction, steel against aluminum reduced that friction, and the next improvement will test steel against steel which I believe will reduce friction even further. This may not be a revelation to people with metal working experience.

The third iteration re-installed the potentiometers, improved the strength of the overall hardware and their fittings, added some small plastic rollers to improve movement of the pedals under pressure, and added screen door springs to: 1) add tension against leg/foot pressure for a more realistic feel; and, 2) to help re-center the pedals to their neutral position as leg/foot pressure is reduced. Lastly, I added an upright shield that provides a foot rest for moving the pedals. The foot has to be rotated over that rest to apply the toe brakes but the motion feels satisfactory. These changes made big improvements in my satisfaction with the pedals and brakes. The pedal-brake may be re-designed in a future iteration.

Arduino 3-Axis Sketch for Pedals and Rudder --

"riclamer" modified Darran's Arduino Joystick code to output axis (A0-A5) and pin (D2-D13) data that are sensed by an Arduino Uno. Some users had problems with the code which turned out to be an array not being defined properly. That bug is fixed in the code below. While testing the code, I realized that the axis values (potentiometer readings) were extremely variable due to noise from the inexpensive devices. I have added some code to average 5 readings of each analog pin (potentiometer wiper) and use the average as the reading. I also compare the current reading to the past reading to determine if there is a change greater than 100. If so, the value gets used. Otherwise the value is ignored. If any one of the analog values (analog A0, A1, A2) varies by more than 100 from its previous value (or any D pin changes value), then the joyReport gets transmitted to FSX with the updated values. Using averaged values seems to have reduced the sending of widely fluctuating values to FSX while not causing delays to the controls. Ground any unused analog pin to minimize noise on the axes. The digital pins do not need to be grounded if they are not connected, since they are set to INPUT_PULLUP.


/*
   riclamer_big_joystik_mod

   Lowell Bahner
   October, 2016

   This sketch includes a fix for array dimension btn[] from 12 to 13.
   Riclamer's code is limited to use only 12 pins on the Uno (pins D2-D13)
   even though joyReport is dimensioned for 40 pins.
   Riclamer added an offset of 16 in the original code, which moves the
   pin number up by 16 in the total of 32 pins used by FSX. Offset has been
   reset to 0, since the Arduino Joystick is a separate device to FSX
   which provides an additional 32 pins (only 12 used in this sketch).

   The axis values were quite variable due to noisy signals of potentiometers.
   The axis values are now read 5 times and averaged to reduce variability
   of the pot values due to noise.
   A change of > 100 is also required from the previous value
   before a new value is transmitted.

   Both of these changes can be adjusted by the user.

   Only 3 Axes are programmed in for use with rudder pedals (1) and brakes (2).
   Digital pins are not used by the foot pedals.

   joyReport is only transmitted to FSX when there is a change of an axis or pin

*/


/* Arduino USB Joystick HID demo */

/* Author: Darran Hunt
   Released into the public domain.
   Update by RICLAMER in 25/03/2014 to use Analog ports and digital ports
   This code is to be used with Arduino UNO (6 axis and 12 Buttons)
   This code is compatible with Arduino Mega.
*/

/* INSTALLATION
  Just install POT to each analog port. Using the _Grnd _Analog _5V Arduino.
  Like this image: http://arduino.cc/en/uploads/Tutorial/joy_sch_480.jpg
  To setup the buttons, just install your prefered button switch between GND and Port Digital 02~13.
  Use Flip to erease and burn this firmware DFU: https://github.com/harlequin-tech/arduino-usb/blob/master/firmwares/Arduino-big-joystick.hex
  I used Arduino R3 with Atmega 16U2.
*/

// to turn on DEBUG, define DEBUG. Make sure to undef DEBUG for joystick use
//#undef DEBUG
#define DEBUG

#define NUM_BUTTONS 40     // you don't need to change this value
#define NUM_AXES 8        // 6 axes to UNO, and 8 to MEGA. If you are using UNO, don't need to change this value.

typedef struct joyReport_t {
  int16_t axis[NUM_AXES];
  uint8_t button[(NUM_BUTTONS + 7) / 8]; // 8 buttons per byte
} joyReport_t;

joyReport_t joyReport;
joyReport_t prevjoyReport;
uint8_t sendFlag;

uint8_t btn[13]; // fix riclamer's [12] dimension to [13]
// the array counts from 0 for 13 values, [0,1,2,...12]. 12 pins (D2-D13) are counted as 1,2,3...12.
int fulloff = 0;
uint8_t offset = 0;

void setup(void);
void loop(void);
void setButton(joyReport_t *joy, uint8_t button);
void clearButton(joyReport_t *joy, uint8_t button);
void sendJoyReport(joyReport_t *report);


void setup()
{
  //set pin to input Button
  for ( int portId = 02; portId < 13; portId ++ )
  {
    pinMode( portId, INPUT_PULLUP);
  }
  Serial.begin(115200);
  delay(200);
  sendFlag = 0; // clear one shot sendFlag

  for (uint8_t ind = 0; ind < 8; ind++) {
    joyReport.axis[ind] = ind * 1000;
  }
  for (uint8_t ind = 0; ind < sizeof(joyReport.button); ind++) {
    joyReport.button[ind] = 0;
  }
}

// Send an HID report to the USB interface
void sendJoyReport(struct joyReport_t *report)
{
#ifndef DEBUG
  //Serial.write((uint8_t *)report, sizeof(joyReport_t));
  // do not send duplicate values
  //
  if (memcmp( report, &prevjoyReport, sizeof( joyReport_t ) ) != 0)
  {
    Serial.write((uint8_t *)report, sizeof(joyReport_t));
    memcpy ( &prevjoyReport, report, sizeof( joyReport_t ) );
    sendFlag = 1; // set sendFlag=1 report was different
  } else {
    sendFlag = 0; // report did not change
  }
  //
  // end do not send duplicate values
#else
  // dump human readable output for debugging
  for (uint8_t ind = 0; ind < NUM_AXES; ind++) {
    Serial.print("axis[");
    Serial.print(ind);
    Serial.print("]= ");
    Serial.print(report->axis[ind]);
    Serial.print(" ");
  }
  Serial.println();
  for (uint8_t ind = 0; ind < NUM_BUTTONS / 8; ind++) {
    Serial.print("button[");
    Serial.print(ind);
    Serial.print("]= ");
    Serial.print(report->button[ind], HEX);
    Serial.print(" ");
  }
  Serial.println();
#endif
}

// turn a button on
void setButton(joyReport_t *joy, uint8_t button)
{
  uint8_t index = button / 8;
  uint8_t bit = button - 8 * index;

  joy->button[index] |= 1 << bit;
}

// turn a button off
void clearButton(joyReport_t *joy, uint8_t button)
{
  uint8_t index = button / 8;
  uint8_t bit = button - 8 * index;

  joy->button[index] &= ~(1 << bit);
}

/*
   Read Digital port for Button
   Read Analog port for axis
*/
void loop()
{
  // Create Array with value of switch button
  for (int bt = 1; bt < 13; bt ++)
  {
    btn[bt] = digitalRead(bt + 1); // reads pins D2-D13
  }
  // Power ON button if it is pressed. buton 17 at 28
  for (int on = 1; on < 13; on++)
  {
    if (btn[on] == LOW)
    {
      setButton(&joyReport, on + offset - 1);
      //  Serial.println("botao ON");
      //  Serial.println(on+16);
    }
    //else {
    //  fulloff = fulloff++;
    //}   //Power off all Button, if all button are not press
    //if (fulloff == 11)
    //{
    //  for (uint8_t cont = 0; cont < 40; cont++)
    //  {
    //    clearButton(&joyReport, cont);
    //  }
    //  Serial.println("Every button is off");
    //}
  }

  uint8_t diffCopy = 0;

  /* Adjust Analog axes on A0, A1, A2*/
  /* Arduino UNO has 6 analog pins of 8 possible. Set pin to 0 if not used */
  for (uint8_t axis = 0; axis < 3; axis++) {
    int tmp = joyReport.axis[axis]; // copy previous axis value
    // Average 5 readings of port to get better values from noisy pots
    int sumAxis = 0;
    int avg = 0;
    for (int i = 0; i < 5; i++) {
      sumAxis = sumAxis + analogRead(axis);
    }
    avg = sumAxis / 5;
    joyReport.axis[axis] = map(avg, 0, 1023, -32768, 32767 );
    //joyReport.axis[axis] = map(analogRead(axis), 0, 1023, -32768, 32767 );

    // only change axis if avg reading changes by > 100
    // flag change in axis value > 100
    if (abs(joyReport.axis[axis] - tmp) > 100) diffCopy = 1;
  }
  //Set un-used analog pins to 0 to reduce spurious values in joyReport.
  //joyReport.axis[0] = 0;
  //joyReport.axis[1] = 0;
  //joyReport.axis[2] = 0;
  joyReport.axis[3] = 0;
  joyReport.axis[4] = 0;
  joyReport.axis[5] = 0;
  joyReport.axis[6] = 0;
  joyReport.axis[7] = 0;

  if (diffCopy > 0) {
    //Send Data to HID
    sendJoyReport(&joyReport);
    diffCopy = 0;
    
    // clear the digital pins from re-sending data
    for (uint8_t cont = 0; cont < 40; cont++)
    {
      clearButton(&joyReport, cont);
    }
  }

  delay(100);
  fulloff = 0;
}



Useful references -

(Oct 26, 2016)

1 comment:

  1. Schick Quattro Titanium Hd Glasses for Sale - The Titanium
    Buy Schick Quattro Titanium Hd Glasses and Crafted Glasses for Sale titanium bracelet at The titanium flask mokume gane titanium TINOHO - TINOHO - CIB. $18.89. In stock. $18.99. In stock. $36.89. In stock. $16.29. Out of stock. $18.49. Out microtouch titanium of stock. oakley titanium glasses $18.89.

    ReplyDelete