Thursday, December 8, 2016

Arduino and MCP23S17 Port Expander (Part 1)

MCP23S17 Port Expander — 
The Microchip MCP23x17 Port Expander can be used to add 16 digital pins to an Arduino. The MCP23S17 uses SPI while the MCP23017 uses I2C to communicate with the Arduino. In this series of blogs, I will examine the MCP23S17 SPI chip, with the goals of: 1) gaining experience with the device; 2) better understanding how the device works on an Arduino with SPI; and, 3) using the device in several prototypic applications.
Devices and pins can get very confusing when adding hardware chips and their associated software to an Arduino. In this blog, I will use  some codes to help identify pins and devices. MCP will refer to an MCP23S17 chip. A "device" will equate to either the Arduino board (device = 0) or any one MCP chip (device 1, 2, ..., n). A pin identified as D"x" will be the common term for an Arduino digital pin. The Uno has 14 digital pins and 6 analog pins. The Mega 2560 has 54 digital and 16 analog pins. Microchip identifies MCP pins as GPA"x" or GPB"x" which refers to the Port (A or B) and pin (0-7). The MCP is an 8 bit device, and each device has a Port A (pins GPA0...GPA7, which are chip pins 21-28) and Port B (pins GPB0...GPB7, which are chip pins 1-8). A port value of "0" refers to Port A. Port values of >="1" refers to Port B. The Arduino sketch and MCP23S17 library that is used will also dictate how a chip device, a port, and a pin are identified.
Majenko MCP23S17 library -- I have adopted the Majenko MCP23S17 library (on github) which has worked well with the Arduino IDE 1.6.5 SPI library. I tested other libraries, but none measured up to the Majenko library. All code presented in this series of blogs will use the Majenko MCP23S17 library. Download the library (revised 2017-Jan-23), add it to the Arduino libraries, and restart the Arduino IDE.
The Majenko library provides both 16-bit (Port A and Port B) and 8-bit (Port A or Port B) functions. This library uses the MCP "pin" term to mean one of 16 digital pins ranging from 0 to 15, where [0...7] are the GPA"x" pins and [8-15] are the GPB"x" pins. The Microchip GPA"x" and GPB"x" terms are not used to identify pins by the Majenko library, just as pins 0-15.
The MCP23S17 chip used for this work was the 28-pin narrow dual inline package which can be easily used with 0.1" breadboards for prototype testing. Eight (8) MCP23S17 chips (SPI variant) can be used together on one SPI CS pin address. SPI on the Arduino uses 3 SPI pins (Uno pins D11, D12, D13, or on Mega 2560 pins D50, D51, D52). SPI also requires a CS pin which is used to select which SPI device is active at any given time. The default Arduino CS pin is D10 on Uno and D53 on Mega 2560. Any dedicated digital pin can be used as the CS pin. Since 8 MCP23S17 chips can be multiplexed together, three other pins (MCP23S17 pins A0, A1, A2), must be either connected to ground or +V to give each chip a unique address of [0,1,2...,7]. The software can then identify which chip to access by this address. Eight chips could provide 8x16=128 digital pins while only using the 3 SPI pins plus 1 CS pin of the Arduino for each group of 8 chips.

Wiring the MCP23S17 for testing is a bit of a challenge, just due to the large number of pins. Connect MCP chip pin 10 (VSS) to 0V (ground); pin 9 (VDD) to +V; pin 18 (Reset) through a 10K resistor to +V; pins 15 (A0), 16 (A1), and 17 (A2) to either 0V or +V (unique for each chip); pin 11 (CS) to D10/D53; pin 12(SCK) to D13/D52; pin 13 (SI) to D11/D51; and pin 14 (SO) to D12/D50. Each of the MCP digital pins 0-15 are wired as inputs to some digital sensor (button or switch), or can be used as output pins to drive other devices. The MCP interrupt pins, if used, are connected to Arduino interrupt pins, to trigger the Arduino to take instant action.
  

Pictures: Arduino Mega 2560 with MCP23S17 Port Expander on breadboard. The sketch MCP23S17_SingleTest_01.ino will display an LED ON (left picture) while MCP digital pin 8 (IC pin 1) is not grounded. Connecting MCP digital pin 8 to ground (right picture, purple wire) will switch the LED OFF. As a sketch option, a wire from MCP pin 7 to Arduino pin D6 (yellow wire) can demonstrate a hard-wired connection from the MCP to another device. In this case, "another device" is the Arduino input pin D6.

This fritzing drawing might help show the wiring for this example.


/*
 * MCP23S17_SingleTest_01.ino
 * Library Ref: github.com/MajenkoLibraries/MCP23S17
 *
 * Lowell Bahner
 * 2016-12-11
 *
 * Hardware: Arduino UNO/Mega2560
 * IDE: Arduino 1.6.5
 *
 * Useful SPI Ref: gammon.com.au/spi
 *
 * Wire the SPI Interface common lines (Mega2560 SPI pins shown in [ ]):
 * Arduino SPI_MOSI pin 11 [51] <->   SI  MCP23S17 pin 13
 * Arduino SPI_MISO pin 12 [50] <->   SO  MCP23S17 pin 14
 * Arduino SPI_CLOCK pin 13 [52]<->   SCK MCP23S17 pin 12
 * Arduino SPI_CS pin 10 [53]   <->   SS  MCP23S17 pin 11
 * MCP23S17 VDD pin 9 to +5V
 * MCP23S17 VSS pin 10 to Gnd
 * MCP23S17 Reset pin 18 to +5V
 * MCP23S17 Interrupt pins 19-20
 * MCP23S17 A0,A1,A2 pins connect to Gnd or +5V (000 = Address 0, 100 = Address 1, 010 = Address 2, etc)
 * MCP23S17 Port A pins are chip pins 21-28
 * MCP23S17 Port B pins are chip pins 1-8
 *
 * This sketch runs a simple example to demonstrate library capabilities.
 * This code will run if the MCP23S17 is properly wired to Arduino.
 * The sketch reads a Port Expander input pin,
 *    writes that value to a Port Expander output pin.
 *    That output pin value is written to an Arduino pin
 *    which can turn an LED on and off as the Port Expander
 *    input pin is grounded or not grounded.
 *    
 * This sketch does not demonstrate interrupt functionality.
 *
 * This sketch instantiates 1 MCP23S17 chip.
 * Bank0 (MCP23S17 address "000") Port B pins are set as INPUT_PULLUP.
 *    Normally HIGH, use switch to set a pin LOW.
 * Bank0 (MCP23S17 address "000") Port A pins are set as OUTPUT.
 *    MCP23S17 outputs will power LEDs up to 25ma through 220-1000ohm resistor to ground.
 *
 * Majenko MCP23S17 library functions (see .h,.cpp files for details):
 *
 *  bank.pinMode(uint8_t pin, uint8_t mode)
 *    where modes are:
 *      OUTPUT - sets pin HIGH
 *      INPUT  - sets pin LOW for sensing change to HIGH +V
 *      INPUT_PULLUP - sets pin HIGH for sensing change to LOW 0V
 *
 *  bank.digitalWrite(uint8_t pin, uint8_t value)
 *    if mode=OUTPUT, pin value = LOW (0) or HIGH (1)
 *    if mode=INPUT, value LOW disables pullup on pin, value HIGH enables pullup on pin
 *
 *  uint8_t value = bank.digitalRead(uint8_t pin)
 *    read the state of the pin
 *
 *  uint8_t value = bank.readPort(uint8_t port)
 *    read entire 8-bit port of all INPUT pins on Port A (port=0) or Port B (port>=1)
 *
 *  uint16_t longValue = bank.readPort()
 *    read both ports as 16-bit combined
 *
 *  bank.writePort(uint8_t port, uint8_t val)
 *    write val (0 or 1, hex example 0x55) to all OUTPUT pins on Port A (port=0) or Port B (port>=1)
 *
 *  bank.writePort(uint16_t val)
 *    write val (hex example 0x55AA) to all OUTPUT pins on Port A and Port B
 *
 *  Also, interrupt functions are defined:
 *      enableInterrupt(uint8_t pin, uint8_t type);
 *      void disableInterrupt(uint8_t pin);
 *      void setMirror(boolean m);
 *      uint16_t getInterruptPins();
 *      uint16_t getInterruptValue();
 *      void setInterruptLevel(uint8_t level);
 *      void setInterruptOD(boolean openDrain);
 *
 *
 *
 *
Run Sketch: Serial Monitor Output

 -- RWpins() --

Bank0: Input  pin 8: connect MCP pin 8 to 0V (ground)
Bank0: Output pin 7: MCP pin 7 will switch to LOW.
                     LED on pin D7 will switch OFF.
                     Let MCP pin 8 go HIGH, LED will switch ON.

(ground MCP pin 8)
 -- RWpins01() --

Bank0: Input pin 8: 0
Bank0: Output pin 7: 0
Pin D7 0

(un-ground MCP pin 8)
 -- RWpins01() --

Bank0: Input pin 8: 1
Bank0: Output pin 7: 1
Pin D7 1

 *
 *
 */

// Majenko MCP23S17 Library
#include <MCP23S17.h>

// Arduino Library SPI.h
#include <SPI.h>

// SPI CS/SS chipselect pin can be changed by user as desired
const uint8_t chipSelect = 53; // Mega2560
//const uint8_t chipSelect = 10; // Uno

// example Arduino board pins
uint8_t pin5 = 5;
uint8_t pin6 = 6;
uint8_t pin7 = 7;

// Create an object for each chip
// Bank0 is address 0. Pins A0,A1,A2 grounded.

MCP23S17 Bank0(&SPI, chipSelect, 0);

void setup() {

  Serial.begin(9600);

  Bank0.begin();

  // LED connected to Arduino pin 7 to test inter-device communication
  pinMode(pin5, INPUT); // input pin normally LOW
  pinMode(pin6, INPUT_PULLUP); // input pin normally HIGH
  pinMode(pin7, OUTPUT);

  //
  // Set MCP23S17 pin modes:
  // example set one pin: chip.pinMode(0, OUTPUT); // sets chip pin 0 (Port A 0) to OUTPUT mode
  //
  setPortPins(); // example: set pins on each Port on each chip to different modes.
  //

}

// Set Port pins to desired mode
void setPortPins() {
  // Example usage

  // Set all Bank0 Port A pins to OUTPUT
  for (uint8_t i = 0; i <= 7; i++) {
    Bank0.pinMode(i, OUTPUT);
    Bank0.digitalWrite(i, 0); // set value LOW
  }

  // Set all Bank0 Port B pins to INPUT_PULLUP
  for (uint8_t i = 8; i <= 15; i++)
    Bank0.pinMode(i, INPUT_PULLUP);
}

//
// Change LED On/Off with button push
// 1) Switch an INPUT_PULLUP pin to ground
// 2) Read the pin value
// 3) Write the value to an OUTPUT pin
// 4) Read the OUTPUT pin value
// 5) Write the value to Arduino OUTPUT pin D7 with LED=>r1000=>Ground
// 6) Turn LED On/Off
// Use functions digitalRead(pin) and digitalWrite(pin,value)
//
void RWpins01() {

  Serial.print("\n -- RWpins01() --"); Serial.println("");

  uint8_t bValue = 0;   // Bank0 Port B input pin value (0 push LOW, 1 normally open HIGH) with push button switch to gnd
  uint8_t bPin  = 8;    // Bank0 Port B pin number
  uint8_t aValue = 0;   // Bank0 Port A output pin value
  uint8_t aPin  = 7;    // Bank0 Port A pin number
  uint8_t d6Value = 0;  // pin d6
  uint8_t d7Value = 0;  // pin d7


  // read the switch value into b15
  bValue = Bank0.digitalRead(bPin);
  Serial.print("\nBank0: Input pin "); Serial.print(bPin); Serial.print(": ");  Serial.println(bValue);

  // write the switch bPin, bValue, to the output aPin
  Bank0.digitalWrite(aPin, bValue);

  aValue = Bank0.digitalRead(aPin);
  Serial.print("Bank0: Output pin "); Serial.print(aPin); Serial.print(": ");  Serial.println(aValue);

  // // -----------------------------------
  // do one of the following
  // comment out the code lines after 1) or 2)
  //
  // // example 1: write the aValue from port expander output pin
  // // to Arduino output pin to switch LED.
  // // 1) Turn on/off LED connected to Arduino pin 7 through resistor to ground
  digitalWrite(pin7, aValue);

  // // example 2: connect port expander output pin to
  // // Arduino input pin using wire. Read that pin value and write
  // // it to Arduino output pin to switch LED.
  // // or, 2) wire pin D6 to Bank0 pin 7; retain LED on pin D7
  //          d6Value = digitalRead(pin6); // pin6 normally HIGH
  //          digitalWrite(pin7, d6Value);
  // // end do one of the following
  // // -----------------------------------

  d7Value = digitalRead(pin7); // pin7 follows pin6
  Serial.print("Pin D7 "); Serial.println(d7Value);

}


void loop() {

  // Change LED On/Off with button push
  // Switch or Short pin Bank0, Port B, pin 8 to ground
  // LED=>r1000=>ground on Arduino pin D7 will turn on/off
  RWpins01();

  delay(100);

}


// ++++++++++++++++++++++++++++++++++++++++++++++++++++
// print binary8 binary16 and hex functions
// ++++++++++++++++++++++++++++++++++++++++++++++++++++

//---------------------------------------------------------------------------------
// print 8-bit byte as 8 bit binary string
//---------------------------------------------------------------------------------

void print8Bits(uint8_t myByte) {
  for (uint8_t mask = 0x80; mask; mask >>= 1) {
    if (mask  & myByte)
      Serial.print('1');
    else
      Serial.print('0');
  }
}

//---------------------------------------------------------------------------------
// print 16-bit word as 16 bit binary string
//---------------------------------------------------------------------------------

void print16Bits(uint16_t myWord) {
  for (uint16_t mask = 0x8000; mask; mask >>= 1) {
    if (mask  & myWord)
      Serial.print('1');
    else
      Serial.print('0');
  }
}


//---------------------------------------------------------------------------------
// crPrintHEX print value as hex with specified number of digits
//---------------------------------------------------------------------------------

void crPrintHEX(unsigned long DATA, unsigned char numChars) {
  unsigned long mask  = 0x0000000F;
  mask = mask << 4 * (numChars - 1);
  Serial.print("0x");
  for (unsigned int eID = numChars; eID > 0;  --eID) {
    Serial.print(((DATA & mask) >> (eID - 1) * 4), HEX);
    mask = mask >> 4;
  }
  Serial.print("  ");
}

Monday, October 31, 2016

Arduino with FSX (Part 5)

An 8-Axis 256-Button Arduino Joystick— 

The Arduino has been successfully programmed to act as a 128 or 256 button HID-USB device that can communicate through USB with a PC. Unfortunately, FSX has the limitation of 32 buttons/switches per USB device. However, if the 128/256 buttons can be partitioned as multiple joystick data streams, then the one Arduino can appear to be multiple 32 button joysticks to FSX. A different USB driver must be used on the PC that divides the Arduino data string into data strings from multiple joysticks, each with a different ID, and sends those data strings to FSX. FSX acts on the data as coming from individual joysticks. A 256-button joystick  programmed as dual 128-button joysticks is described later. Thirty-two buttons from each joystick can be used by FSX. Extra buttons beyond 32 are just ignored.

The original 40-button Arduino joystick was modified to 256-Buttons by Eagle forum member "overpro"  (ref: forums.eagle.ru/showthread.php?p=2522938). "overpro" developed Arduino hardware based on a pin matrix (16 x 16 = 256 pins) to provide the pins necessary to handle 256 buttons/switches. He also includes some dedicated pins for rotary encoder inputs. In post #150, "overpro" provides an Arduino sketch "CVP_JoystickController.ino" which creates the 256 button data stream sent over HID-USB to a PC. The associated "CVP_Joystick_1HID256Btns.hex" file is used to flash the HID-USB (see post #142 of the above forum). Also in the forum comments are references to Pointy's Joystick Test and Peter Dowson's HID device scanner (HID Scanner.exe). This forum has a wealth of information about the development of a large joystick but the forum discussion is lengthy, making it sometimes difficult to understand the best path forward.

In another forum thread (ref: forums.eagle.ru/showthread.php?t=117011, post #1), member "overpro" provides a fairly comprehensive explanation of a "composite" USB device that creates two joysticks from one Arduino HID-USB device.

I followed the above procedures using a Mega 2560 but without the 16x16 pin matrix. Both Pointy's Joystick Test and Peter Dowson's HID Scanner revealed the two 128 pin joysticks, and it was confirmed (by shorting pins to ground) that only the first 32 pins from either joystick were sensed by FSX. However, FSX did sense the two as separate joysticks, and each could have their pins assigned to different FSUIPC functions. Using "overpro's" code does provide a means of adding two 32-pin joysticks to FSX. One of the joysticks also handles the 8-axes which can be connected to potentiometers for control surfaces.

Sorting through all of the above details took several days of reading, prototyping, and testing.

Refinements I want to investigate to what has been developed by "overpro", "riclamer", and "Darran", include integration of rotary encoders as input devices, and port expanders for adding additional pins without using the pin matrix approach. I have no opinion whether port expanders are any better or worse than pin matrices, I just want to learn about port expanders and to integrate them into the discussion.

For FSX, it seems that the easiest approach using Arduino Joysticks is using 32 digital pins of a Mega 2560 for buttons/switches and 8 analog pins for control axes. Rotary encoders each require 2 digital pins, so 32 pins translates to a maximum of 16 encoders per Arduino joystick. Encoders are easily handled by MobiFlight, so maybe that is a better solution. The topic of LED displays used to show the state of a switch is discussed in various forums. I have read that USB can handle two-way traffic, but I have yet to read how LED's can be controlled on the Arduino by FSX. MobiFlight handles both LED and digital displays, which makes that even more attractive from that perspective. Future blogs in this series will investigate rotary encoders and port expanders in more detail. If anyone has examples of how to control LED's over HID-USB, please let me know. Thanks.

Useful references -




(Oct 31, 2016)

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)

Friday, October 21, 2016

Arduino with FSX (Part 3)

Arduino as a HID Joystick — 
To a new user this will be confusing--USB, HID, joysticks, hardware ports, and USB drivers. We all know that USB just works--you plug it in and by magic it works. Well, that is true if there is a USB driver on the PC which works with the device at the other end of the USB cable. The following discussion is about making the Arduino look like a USB device (not a vanilla Arduino serial device) and getting it to work on a USB port with a USB driver on the PC. 
A vanilla Arduino Uno or Mega uses the on-board USB chip (ATmega 16U2) for communicating through a USB cable with the Arduino IDE on a Mac/PC/Unix computer. The Arduino IDE communicates with the Arduino through Serial-USB and is used to download programs to the Arduino, and for sending and receiving data between the computer and the Arduino.
On a PC, a driver is required to connect an external USB device to a particular software. Windows uses different HID-USB drivers to connect to keyboards, mice, game controls, or joysticks  (HID, Human Input Device). FSX can accept data from a HID-USB joystick connected to a USB port using the Microsoft Game Controller driver in Windows. Some companies provide their own USB drivers to interface to their hardware, rather than relying on the MS drivers included with Windows. 
The MS Game Controller driver provided with Windows allows the PC to communicate through a USB port to external joystick hardware and obtain data from potentiometers for adjusting rotating axes (joystick up-down, left-right, back-and-forth) or from buttons and switches. In FSX, there is a limit of 32 buttons/switches per USB device. Those who are building large and complex aircraft cockpits run into this limitation of 32 buttons/switches pretty quickly. A future blog will examine alternatives so that additional buttons/switches might be accommodated.
As previously stated, the ATmega 16U2 USB chip on an Arduino Uno or Mega is normally programmed as a Serial-USB device. On the PC, a serial-USB driver is used to connect the Arduino through USB to the Arduino IDE. To use an Arduino as an FSX device, that USB chip has to be reprogrammed as a HID-USB device that can communicate through a USB port using the MS Game Controller driver. FSX can then accept data from the external Arduino hardware through USB to FSX running on the PC.
I run FSX on a dedicated PC, but I do my software development work primarily on a Mac. In my case, all of the process of writing and testing Arduino sketches, and programming the Arduino USB chip is conducted on the Mac. The Arduino (as a joystick controller) is then plugged into the PC USB for use with FSX.
Why not just use MobiFlight as the controller for joysticks that use rotational controls (potentiometers)? As of 2016, MobiFlight software did not connect to potentiometers. There have been some requests by users with the MobiFlight developer about adding that functionality. At this time, lacking MobiFlight as an option, adapting an Arduino to act as a joystick device is a workable, but somewhat tedious, solution.
The process of creating an Arduino Joystick— 
Several things are needed to implement an Arduino joystick:
  1. Arduino R3 Uno or Mega with ATmega 16U2 USB chip (not a CH340 USB chip).
  2. Arduino sketch that converts potentiometer values and button/switch signals to a defined string of data that is transmitted through USB to the PC.
  3. Software and/or hardware for reprogramming the 16U2 USB chip.
  4. The Arduino USB chip reprogrammed as a HID-USB device that can send and receive formatted data strings to/from a PC. The PC USB driver adjusts to that USB data definition and delivers the string of data to FSX.
  5. Patience to deal with technical issues.
The simplest approach for using an Arduino as a joystick, is to use one Arduino as one joystick controller, each with a unique ID. FSX can connect to each Arduino joystick as a separate device with axis controls and/or 32 buttons/switches. Each Arduino would connect through a USB cable. The 8-axes 40-button joystick described below, is such a device. Since the Uno does not have 32 digital pins, a Mega 2560 with 54 digital and 16 analog pins can to be used. For development and testing, an Uno with 14 digital and 6 analog pins can be used with the understanding that the number of functional pins will be limited. Additional pins can be added to an Arduino, either by using a matrix or port expanders. Adding port expanders will be discussed in future blogs.

The 8-Axis 40-Button Arduino Joystick— 
The original code for a 40-button Arduino joystick was published on the web by Darran (ref: http::hunt.net.nz/users/darran/weblog/15f92/Arduino_UNO_Big_Joystick_HID_firmware.html, Jan 22, 2013), however that web page was discontinued during 2016. A copy of that webpage was found at (ref: archive.is/KbXom). Darran's web page is a must read since he clearly outlined the steps for creating an Arduino HID-USB joystick with 8 axes and 40 buttons. That software will form the basis for many of the discussions in this blog series. 
Joystick Process Steps:
1) Load your sketch into Arduino using the standard USB cable and the Arduino 16U2 chip programmed as Serial-USB. (the hex files for Uno and Mega can be downloaded from https::github.com/arduino/Arduino/tree/master/hardware/arduino/avr/firmwares/atmegaxxu2/arduino-usbserial. No need to "make" any files. )
2) Put the R3 Uno or Mega into DFU mode. This simply requires momentarily shorting two pins of the USB-ICSP nearest the USB jack with a metal wire.
3) Flash (program) the Arduino USB 16U2 chip as HID-USB using Darran's "Arduino-big-joystick.hex".
4) Attach Arduino Joystick to PC USB.
Copies of Darran's sketch and USB .hex firmware are online at github.com/harlequin-tech.
-- An Example Sketch
The example sketch "big_joystick_demo.ino" provided by Darran is a bit confusing at first. To see the code work, change the "#undef DEBUG" to "#define DEBUG" so the debugging Print.serial statements will display on the Serial Monitor. That will demonstrate that the code does in fact work. The code loops to sequentially turn on and off one button after another, so you might wonder "How can I use this with FSX? Hold that thought for a while, while you perfect the other steps required for making an Arduino joystick work. Remember to "#undef DEBUG" before flashing the USB chip, since the data sent through USB can not be corrupted with the Serial.print debug data that can be viewed on Serial Monitor.
-- Put the Arduino into DFU mode and Flash the chip
The 16U2 chip is placed in dfu mode by shorting two pins of the USB-ICSP. On a PC, use "FLIP" to program the 16U2 chip. On a Mac, the dfu software needs to be downloaded and installed. Then Terminal app is used for entering the dfu commands for flashing the 16U2 chip. Flashing the 16U2 chip is an easy process once you do it a couple of times. See ref: www.instructables.com/id/DFU-programmer-on-Mac-OS-X/ for the steps for using dfu. FLIP or dfu will be used to flash the desired .hex file into the 16U2 chip.
-- Test the configuration to make sure axes and buttons are working as desired
At the hardware/firmware level you can check the USB port.


  • On a Mac, System Information:USB can be used to see what USB devices are attached. An Arduino (Serial-USB) will display as "Communication Device":Product ID: 0x0043, etc. If the Arduino has been put into dfu mode, the device will display as "Composite Device":Product ID: 0x2fef, etc. Once the HID-USB hex file is flashed, the device will display as "Arduino Joystick":   Product ID: 0x2043, etc.


  • With "Arduino Joystick" connected to PC USB port: Open "Devices", locate the MS Game Controller icon that represents the "Arduino Joystick", right click the icon and choose "Game controller settings". Choose "Arduino Joystick" from list, and click Properties. The Properties window will display with the 6 Axes (horizontal bars) and 32 buttons. The axes will move in small increments. The 32 buttons will cycle on and off.
If these tests check out, the HID-USB Arduino Joystick has been successfully installed.

Other tests that can be conducted to determine what the USB configuration looks like to the PC include:



(Oct 21, 2016)



Monday, October 17, 2016

Arduino with FSX (Part 2)

Trim and Flaps Control — 

Trim and flaps are basic to flying any aircraft. I found trying to adjust trim and flaps using the mouse or keyboard keys while flying with a stick was very difficult and unnatural. I wanted to adjust trim using trim 
wheels and the flaps to be controlled by moving a lever up and down to provide the various flap positions required for flying.


The options that I found on the internet were: 1) commercial control boxes; or, 2) various plans for DIY constructed control boxes. Since I like to build things, I chose the latter option. The next question was how would it work? 


I found that most users referenced and relied on a software product, FSUIPC, and that it was an essential software tool. 

I found another software product, MobiFlight, which used an Arduino Mega 2560 with the MobiFlight software through USB to interact with FSX via FSUIPC. This seemed like an attractive approach since it used an Arduino as the controller computer, it had a user-friendly interface, and it connected to the PC running FSX through USB.


The first road block I found was that MobiFlight did not connect to potentiometers, but did connect to rotary encoders. I had to learn about rotary encoders.


I ordered KY040 rotary encoders from eBay, and while I was waiting for them to arrive, I watched the Mobiflight you-tube training about constructing and testing hardware for FSX. That seemed like a solid approach. When the KY040 rotary encoders arrived, I successfully tested them with Mobiflight to send trim signals to FSX. Once that testing was completed, I ordered Arduino Mega 2560 boards from eBay. The first two boards I received were not Arduino boards even though the description on eBay stated they were R3 Mega 2560 boards. Instead they used CH340 USB chips and the clock ran on a 12 MHz crystal, rather than 16 MHz. It turned out that MobiFlight ran fine on these knock-off boards, but I decided to place another order for R3 Mega 2560 boards from an eBay vendor that documented their boards used the proper USB chip. The critical difference in these boards is the USB controller chip. The CH340 USB chip in the knock-off board is a dedicated serial-USB chip which works for most vanilla Arduino applications. The Arduino Mega uses the ATmega 16U2 USB chip which can be reprogrammed to be serial-USB or HID-USB. HID-USB will be discussed in later blogs about building joyStick devices.

 


The Arduino Uno and Mega 2560 use the ATmega 16U2 USB chip (left picture) which can communicate through, and be programmed through the USB-ICSP pins. A clone board that uses a CH340 Serial-USB chip (right photo, 16-pin IC) does not have the USB-ICSP pins. One other thing, while discussing eBay Arduino boards--some knock-off boards use chips that are not pre-programmed. You may have to program the USB chip or the main processor chip to make the board work as an Arduino. It can be distressing when you get your new board, plug it in, and it just does not work. It takes some time to figure out if there is a hardware or software problem. And it takes more time to fix the problem, if it can be fixed.

Working Trim-Flaps-Gear Controller Prototype --


I fly using a stick in my right hand. I wanted to control trim, flaps, gear, hook, and speed brake with my left hand, as used in some Navy aircraft. The picture shows how I assembled these functions into one box that can be mounted for left hand use. The Trim, Flaps, and Gear Control Box continues to be a work in progress, but it has become fairly mature. The Control Box, with Arduino Mega 2560 and MobiFlight, now provides:
  • Elevator Trim Wheel using KY040
  • Aileron Trim Wheel using KY040
  • Rudder Trim Wheel using KY040
  • Flaps Control using 4 position rotary switch
  • Gear Control using 2 position rotary switch
  • Speed Brake using DPDT toggle switch
  • Tail Hook using DPDT toggle switch
  • 4 LED's for flap position indicators
  • 3 LED's for gear position indicators
  • 1 LED for speed brake position indicator
  • 1 LED for tail hook position indicator
  • 8-digit readouts for elevator trim count and % elevator
  • 8-digit readouts for aileron trim count and % aileron
  • 8-digit readouts for rudder trim count and % rudder
My first build focused on the Trim wheels using rotary encoders. Flap and gear switches and LED's were then added and tested. After struggling with trim during flights, I added the out-board displays so I could better understand how trim was affecting the flight simulations. Initially I had a few problems when controls did not respond quickly while flying simulations, such as, flaps down on approach. This problem was fixed by turning off debug logging (both in FSX and MobiFlight) which interfered with control actions being taken in a timely manner.
MobiFlight uses FSUIPC offset addresses for many actions. Therefore, purchase a license for FSUIPC, so that you will have that tool available for use in all your controllers.

TODO - 

1) Figure out how to check the position of a switch and reset FSX to use that switch setting, in case the initial switch pulse was missed due to the processor being busy and missing the switch.

2) MobiFlight Control Mapping - make a table of the control, the Mobi device, the FSX control, the control mode (keypress, FSUIPC offset, FSX direct), size (byte, word, int4, int8), value (0,1, 16384, etc), transforms, pre-conditions.

(Oct 17, 2016)


    Thursday, October 13, 2016

    Arduino with FSX (Part 1)

    Short History — 

    FSX is flight simulator software that continues to have a large following of professional and non-professional pilots, and gamers who want realistic flying experiences. FS was marketed by Microsoft beginning in 1982 as Flight Simulator 1.0, and progressed through multiple revisions as FS2000, FS2002, FS2004 (aka FS9), and FSX in 2006. Microsoft closed down further work on FS in 2009. Dovetail Games obtained the rights to FS in 2014 and develop the Microsoft Flight Simulator X: Steam Edition. (ref: https://en.wikipedia.org/wiki/Microsoft_Flight_Simulator_X)


    FS has progressed from 8-bit and 16-bit DOS and Windows machines using only a mouse, keyboard, or game controller for flying the simulator missions, to 64-bit PC's with multiple flat-screen views and full-scale 737 cockpits with all the buttons, switches, and instruments.


    This blog shares my experience as a beginner FSX simulator pilot that has added hardware control devices with knobs, buttons, switches, and displays, as flight sticks, rudder pedals, trim controls, and digital readouts. 


    While several sources of hardware and software add-ons for FSX exist, there are a large number of FSX users that build their own hardware and adapt software to use their simulator cockpit hardware with FSX.


    This blog primarily centers on Microsoft FSX Gold Edition as the flight simulation software with add-ons that will be described in later parts of this series of blogs. For these blogs, my FSX runs in a Windows 8.1 Bootcamp partition on a Mac Mini (2012, 16 Gb RAM, 64-bit). Multiple USB 2.0 and 3.0 port expanders are used for connecting hardware to the PC. Two flat-screen displays are used for various FSX windows.


    Future blogs in this series will discuss the Arduino microprocessor as a USB device for adding switches, digital displays, rotary encoders, and port expanders to FSX. These switches and displays then connect through USB to the PC for sending and receiving data that ultimately controls or logs some action of the airplane during the FSX flight simulation.


    The development work discussed in this blog builds on work done by so many and shared on the web through many web sites and discussion forums. Thanks to all of you who share your ideas and software so that others can benefit.


    Arduino and FSX


    I have been tinkering with Arduino's for a few years as a hobby, and when I began using FSX Flight Simulator, I had several thoughts: 1) I can not fly FSX using a keyboard and mouse. That just did not work for me; so, 2) I purchased a Saitek X-52 flight stick which works pretty well, but the button mappings to FSX was a new area of confusion. The FSX software could be used to map the buttons and axes, and the Saitek software could also map the buttons and axes. Then there were drivers from other software companies that seemed to replace the Saitek drivers. Each one had their own interface and means of adjusting and tuning the various controls. All of this may seem pretty simple to someone who has some FSX experience, but to a new user, the goal was to learn to fly the flight school lessons successfully, and not be mired down trying to figure out how to make a button work.

    I had some military flight training many years ago, and I can remember what that felt like. One goal for this flight simulator training is to regain the fun of flying, which translates into hands-on hardware--trim wheels, flight stick, gear controls, rudder pedals, brakes, and at some point, guns, rockets, and bombs (simulated that is).

    It seemed to me that there had to be a way to use the Arduino as a controller for hands-on hardware used with FSX. It took a while to sort through all the scattered bits and pieces on the internet, but several people have succeeded. The work presented here is based on those previous works, of which I am grateful.

    Through this series of blogs, I will try to assemble those various bits and pieces of knowledge that I have gained into one source which may be of use to someone that desires to follow this path. 

    I ask that anyone that has implemented Arduino's with FSX and want to share that information, provide their comments and links.

    (Oct 13, 2016)