Sunday, September 10, 2017

FSX - Add a Keypad for ATC (Part 3)

Tutorial for Adding a Keypad Mapper for ATC to FSX — 

Purpose: Connect 5V Pro Micro Shield to 3.3V USB Shield

In "FSX - Add a Keypad for ATC (Part 2)" the hardware and software for a functional USB key stroke mapper or translator were presented. The hardware consisted of a 3.3V USB Shield that used a 3421E processor chip, and an Arduino-compatible microprocessor Pro Micro shield that was a 5V device. The Pro Micro shield was modified to include a 5V to 3.3V voltage converter so that the SPI wiring between the shields could be directly connected without concern for the 5V device burning out the 3.3V device which was not designed to withstand 5V signals. The Arduino-compatible Pro Micro shield seems to perform as expected on the lower 3.3V voltage even though the Arduino IDE programmer was set for a 5V Leonardo device.


Alternative: Use a Pro Micro as 5V Device

My initial testing and development using the MAX3421E USB shield was conducted with an Arduino Mega 2560 which was a 5V board. A hardware solution was needed to interface the SPI signals between the two boards, so that the Mega 5V SPI signals would not burn out the MAX3421E 3.3V processor.

One solution was to use a 74LVC245 8-bit level shifter chip that I had purchased from AdaFruit. Per the data sheet: 

"The SN74LVC245A is designed for asynchronous communication between data buses. The device transmits data from the A bus to the B bus or from the B bus to the A bus, depending on the logic level at the direction-control (DIR) input. The output-enable (OE) input can be used to disable the device so the buses effectively are isolated."

If "DIR" is set to Vcc, then the voltage levels are shifted from the "A" pins to the "B" pins, and if "DIR" is set to Gnd, then the voltage is shifted from the "B" pins to the "A" pins. The supply voltage to the 74LVC245 is 3.3Vcc, not 5V, and the chip will convert the 5V SPI signals to 3.3V SPI signals. It does not convert 3.3V to 5V, in reverse.



Image 1. Top view of 74LVC245 voltage level converter. Connect Vcc and DIR pins to 3.3V. Connect OE and GND to Gnd (0V, common) between the boards. Connect 5V Pro Micro SPI pins to three of the "A" pins and the corresponding 3421E USB shield SPI pins to "B" pins. The SPI MISO pins were initially directly connected between the two shields to allow SPI to function.

For this project, the Pro Micro is the SPI master, so that signals on three SPI pins (CS, MOSI, and CLK) are transmitted from the Pro Micro at 5V to the corresponding SPI pins (SS, MOSI, SCK) on the 3421E USB shield through the 74LVC245. The Pro Micro RST pin was also connected across a 74LVC245 "A-B" pin pair to the MAX3421E RESET pin. The MAX3421E RESET pin can be connected directly to Vcc rather than the Pro Micro RST pin. The MAX3421E INT pin (3.3V), which would send out an interrupt signal, was not connected to the Pro Micro.

The down side of using the 74LVC245 for SPI is that the MISO signal from the 3.3V 3421E shield must be direct connected to the Pro Micro SPI MISO pin since the 74LVC245 is not a bi-directional device. However, this wiring did allow SPI to function between the two boards and the 3421E shield was not damaged during initial testing. The MISO signal voltage was later buffered using a simple resistor voltage divider--comprised of a 5K resistor from the Pro Micro MISO pin to the 3421E MISO pin and a 10K resistor from the E3421E MISO pin to GND. This simple divider passed the 3.3V MISO signal to the 5V MISO pin. The MAX3421E INT pin could also be connected using a voltage divider.

Bi-directional Level Shifter

An alternative to the 74LVC245 or  resistor voltage divider circuit is a bi-directional voltage level shifter (http://randomnerdtutorials.com/how-to-level-shift-5v-to-3-3v/ , http://www.hobbytronics.co.uk/mosfet-voltage-level-converter).



Image 2. This level shifter purchased from eBay will shift four signals from 5V high to 3.3V low or low to high. Each signal is handled by a separate MOSFET/resistor circuit.

For this project, the Pro Micro SPI pins connect to the HV1 to HV4 pins, 5V to HV, and GND to GND. The 3421E USB Shield SPI pins connect to the LV1 to LV4 pins, 3.3V to LV, and GND to GND. A second board or resistor voltage dividers are needed for the INT and RESET connections between the boards.

Level shifters did work for this project but added components, boards, and wiring. Using shields with matching voltages greatly simplified the prototype.

(September 10, 2017)

Saturday, September 9, 2017

FSX - Add a Keypad for ATC (Part 2)

Tutorial for Adding a Keypad Mapper for ATC to FSX — 

Purpose: Convert a USB Keypad to enter numbers for FSX ATC menu responses

In "FSX - Add a Keypad for ATC (Part 1)" the notion of converting a USB keypad for use with Microsoft Flight Simulator (FSX) was introduced. In this Part 2, the hardware and software for a functional USB key stroke mapper or translator is presented.

The primary credit for this USB re-mapper goes to Oleg Mazurov who developed the original hardware and software (https://www.circuitsathome.com/usb-host-shield-hardware-manual/). Oleg developed his USB Host Mini board (using the 3421E USB chip) which was subsequently cloned and sold on eBay. The 3421E board provided the platform for Oleg to examine USB on a number of devices in detail (https://www.circuitsathome.com/mcu/usb/visualizing-hid-device-reports-and-report-descriptors/). The software for that preliminary work is available in the GitHub version 1 library (https://github.com/felis/USB_Host_Shield). That library has been superseded with version 2 (https://github.com/felis/USB_Host_Shield_2.0) which was used for this project. However, working through the version 1 project development as published by Oleg will help understanding how USB works with these small microprocessor boards.


HARDWARE


3421E USB Shield clone:




Image 1. Modified 3421E USB shield which connects to USB keypad.






Image 2. Circuits@Home USB shield pin labels. Note the VBUS 
pin wiring options at the top-left and RAW pin at bottom-right.


The 3421E shield clone (Image 1) was wired using the "USB Host Shield Hardware Manual and Image 2" as a guide. In Image 1, the red wire (lower right) provides 5V to the RAW pin which is also jumped to the MAX3421E USB VBUS pin (connector below the brown wire INT pin connector). Just left of the 2K2 label, the copper trace was cut to separate the 5V VBUS connector from the MAX3421E circuit which requires 3.3V rather than 5V.

A 1700-3302E (250mA) 3-pin 5V to 3.3V converter was soldered to the shield to provide 3.3V for the board: 1700-3302E pin 1 Gnd (black) to shield Gnd, 1700-3302E pin 2 +5V (red) to shield RAW, and 1700-3302E pin 3 (orange) to shield 3.3V which provides the 3.3Vcc for the shield or other boards connected to the shield that require 3.3Vcc. Filter capacitors were added: 1uF from 5V to Gnd, and 10uF from 3.3V to Gnd.

The 3421E shield communicates through SPI. The SPI wires used in this project are shown in Image 1 and labels in Image 2: blue to SCK, green to MISO, green/white to MOSI, and blue/white to SS. A brown wire to INT was not connected. A grey wire to Reset connects to 3.3Vcc or the microprocessor RST pin.


Pro Micro (Sparkfun Pro Micro Arduino-compatible 32U4 clone):




Image 3: Pro Micro shield with 32U4 microprocessor 
and 5V to 3.3V voltage converter





Image 4: Sparkfun Pro Micro pins


The Arduino-compatible microprocessor used in this project was a Pro Micro shield clone with 32U4 microprocessor (not the standard 328 or 2560 Arduino chips) and also converted from 5V to 3.3V for compatibility with the 3.3V 3421E USB shield.

Converting the shield to 3.3V followed the steps by Adafruit (https://learn.adafruit.com/arduino-tips-tricks-and-techniques/3-3v-conversion). The Pro Micro 5-pin 5V converter chip was first snipped out. The 5 solder pads for the original 5V converter are shown just to the left of the 32U4 microprocessor in Image 3. A 1700-3302E 3-pin converter was used rather than to try to solder in the much smaller 5-pin 3.3V converter chip to replace the 5V chip removed from the Pro Micro. The 1700-3302E pin 1 (black) Gnd was soldered to the shield GND, 1700-3302E pin 2 (red) 5V was soldered to RAW (which obtains the USB +5V from the host PC), and 1700-3302E pin 3 to VCC which provides 3.3V to the Pro Micro shield. A 5V to 3.3V voltage converter was added to both the Pro Micro shield and 3421E USB shield, however one converter may have provided sufficient power for both shields. The Pro Micro shield has filter capacitors on the board, so no additional capacitors were added.

The wiring between the Pro Micro (PM) shield and the 3421E USB (3421) shield includes: 
PM Gnd to 3421 Gnd, 
PM RAW 5V to 3421 RAW 5V, 
SPI PM SCLK pin 15 to 3421 SCK, 
SPI PM MISO pin 14 to 3421 MISO, 
SPI PM MOSI pin 16 to 3421 MOSI, 
SPI PM (CS) pin 10 to 3421 SS, 
PM pin 9 to 3421 INT (not currently used),
PM RST to 3421 Reset (can also be connected to 3.3V).


SOFTWARE

Software for this project used USB Host Shield Version 2 (https://github.com/felis/USB_Host_Shield_2.0) with slight modifications to provide the desired functionality for the FSX Keypad Mapper. The USBHIDBootKbd sketch provided with the Host Shield software was used for the basis of the Arduino sketch used for this project. Arduino IDE 1.8.4 was used to compile and program the Pro Micro computer. The Pro Micro boards obtained from eBay were 5V, 16MHz, and came pre-loaded with the Leonardo boot loader software. The Pro Micro shield converted to 3.3V performed without problems with the Arduino programming software set to use either the Leonardo (a 5V board) setting or the Pro Micro, 5V, 16MHz setting. Using the un-modified 5V Pro Micro board with the 3.3V Pro Micro Arduino programmer setting will brick the Pro Micro. If the Pro Micro gets bricked, the Leonardo or Pro Micro 5V, 16MHz boot loader will need to be reloaded to make the 5V board operational.

USB Scan Codes to ASCII Code Mapper

Following is a table of keypad key Scan Codes and the ASCII codes that are sent to the Pro Micro for transmitting through another USB conversion to the PC, with NumLock ON or OFF. The current configuration is to use the first two sets of codes from the table.




Using the Software

To use this software, connect the Pro Micro to the PC through a micro-USB cable. Connect the 3421E Shield to the keypad through a USB-A cable.
Modify and save the C:ProgramFiles/Arduino/Libraries/USB_Host_Shield_Library_2.0/hidboot.h and hidboot.cpp files as described below.
Run the Arduino IDE and choose the Leonardo device and port.
Open the Arduino Serial Monitor window and set the speed to 115200.
Compile and load the USBHIDBootKbd_Micro_00.ino provided below.
The sketch should print:

     Start

     USB OSC started OK.

See examples of Serial Monitor output at the bottom of this page.
USBHIDBootKbd sketch

Following is the modified USBHIDBootKbd sketch used for this project:


/*
 * USBHIDBootKbd_Micro_00.ino
 * Uses "USB Host Library Rev.2.0"
 * https://github.com/felis/USB_Host_Shield_2.0
 *
 * 2017-09-06
 * Lowell Bahner
 * 
 */
#include <hidboot.h>
#include <usbhub.h>

// add the keyboard output functions.
// Be careful, sketch keyboard output will write to
//  this sketch if it is active on screen!!
#include <Keyboard.h>

// Satisfy the IDE, which needs to see the include statment in the ino too.
//#ifdef dobogusinclude
//#include <spi4teensy3.h>
#include <SPI.h>
//#endif

class KbdRptParser : public KeyboardReportParser
{
    void PrintKey(uint8_t mod, uint8_t key);

  protected:
    void OnControlKeysChanged(uint8_t before, uint8_t after);

    void OnKeyDown (uint8_t mod, uint8_t key);
    void OnKeyUp (uint8_t mod, uint8_t key);
    void OnKeyPressed(uint8_t key);
};

void KbdRptParser::PrintKey(uint8_t m, uint8_t key)
{
  MODIFIERKEYS mod;
  *((uint8_t*)&mod) = m;
  Serial.print((mod.bmLeftCtrl   == 1) ? "C" : " ");
  Serial.print((mod.bmLeftShift  == 1) ? "S" : " ");
  Serial.print((mod.bmLeftAlt    == 1) ? "A" : " ");
  Serial.print((mod.bmLeftGUI    == 1) ? "G" : " ");

  Serial.print(" >");
  PrintHex<uint8_t>(key, 0x80);
  Serial.print("< ");

  Serial.print((mod.bmRightCtrl   == 1) ? "C" : " ");
  Serial.print((mod.bmRightShift  == 1) ? "S" : " ");
  Serial.print((mod.bmRightAlt    == 1) ? "A" : " ");
  Serial.println((mod.bmRightGUI    == 1) ? "G" : " ");
};

void KbdRptParser::OnKeyDown(uint8_t mod, uint8_t key)
{
  Serial.print("\nDN Keycode");

  PrintKey(mod, key);

  Serial.print("Keycode Decimal: ");
  Serial.println(key, DEC);

  uint8_t c = OemToAscii(mod, key);

  if (c)
    OnKeyPressed(c);
}

void KbdRptParser::OnControlKeysChanged(uint8_t before, uint8_t after) {

  MODIFIERKEYS beforeMod;
  *((uint8_t*)&beforeMod) = before;

  MODIFIERKEYS afterMod;
  *((uint8_t*)&afterMod) = after;

  if (beforeMod.bmLeftCtrl != afterMod.bmLeftCtrl) {
    Serial.println("LeftCtrl changed");
  }
  if (beforeMod.bmLeftShift != afterMod.bmLeftShift) {
    Serial.println("LeftShift changed");
  }
  if (beforeMod.bmLeftAlt != afterMod.bmLeftAlt) {
    Serial.println("LeftAlt changed");
  }
  if (beforeMod.bmLeftGUI != afterMod.bmLeftGUI) {
    Serial.println("LeftGUI changed");
  }

  if (beforeMod.bmRightCtrl != afterMod.bmRightCtrl) {
    Serial.println("RightCtrl changed");
  }
  if (beforeMod.bmRightShift != afterMod.bmRightShift) {
    Serial.println("RightShift changed");
  }
  if (beforeMod.bmRightAlt != afterMod.bmRightAlt) {
    Serial.println("RightAlt changed");
  }
  if (beforeMod.bmRightGUI != afterMod.bmRightGUI) {
    Serial.println("RightGUI changed");
  }

}

void KbdRptParser::OnKeyUp(uint8_t mod, uint8_t key)
{
  //Serial.print("UP ");
  //PrintKey(mod, key);
}

void KbdRptParser::OnKeyPressed(uint8_t key)
{
 
Serial.print(" >> Send ASCII Key Hex: ");
  Serial.print(key, HEX);
  Serial.print(", Dec: ");
  Serial.print(key, DEC);
  Serial.print(", ASCII Char: ");
  Serial.println((char)key);

  // write key press to host app that is active (such as TextEdit)
  Keyboard.write((char)key);
};

USB     Usb;
//USBHub     Hub(&Usb);
HIDBoot<USB_HID_PROTOCOL_KEYBOARD>    HidKeyboard(&Usb);

uint32_t next_time;

int pinState = 0;         // current state of the button
int lastPinState = 0;     // previous state of the button

const int pinD5 = 5;

KbdRptParser Prs;

void setup()
{
  Serial.begin( 115200 );
#if !defined(__MIPSEL__)
  while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection
#endif
  Serial.println("Start");

  if (Usb.Init() == -1) {
    Serial.println("USB OSC did not start.");
  } else {
    Serial.println("USB OSC started OK.");
  }

  delay( 200 );

  next_time = millis() + 5000;

  HidKeyboard.SetReportParser(0, &Prs);

  // Read Arduino pin d5 for value from 74LVC245 pin 9
  // Connect 74LVC245 input pin 11 to gnd or +3.3V and read
  // corresponding value on pin 9
  // pinMode(pinD5, INPUT);
}

void loop()
{
  Usb.Task();

  /* do not read pin5 in this keypad sketch
  pinState = digitalRead(pinD5);
  //Serial.println(pinState);
  if (pinState != lastPinState) {
    if (pinState == HIGH) {
      Serial.println("pinD5 HIGH");
    }
    else {
      Serial.println("pinD5 LOW");
    }
    delay(50);
    lastPinState = pinState;
  }
  */

}


The USB Host library code also needs to be modified to make the key strokes map to the new desired codes. Both hidboot.h  and hidboot.cpp must be edited as follows:

hidboot.h

In hidboot.h, line 30, add additional keypad key define statements:

...
...
Circuits At Home, LTD
Web      :  http://www.circuitsathome.com
e-mail   :  support@circuitsathome.com
 */
#if !defined(__HIDBOOT_H__)
#define __HIDBOOT_H__

#include "usbhid.h"

#define UHS_HID_BOOT_KEY_ZERO           0x27
#define UHS_HID_BOOT_KEY_ENTER          0x28
#define UHS_HID_BOOT_KEY_SPACE          0x2c
#define UHS_HID_BOOT_KEY_CAPS_LOCK      0x39
#define UHS_HID_BOOT_KEY_SCROLL_LOCK    0x47
#define UHS_HID_BOOT_KEY_NUM_LOCK       0x53
#define UHS_HID_BOOT_KEY_ZERO2          0x62
#define UHS_HID_BOOT_KEY_PERIOD         0x63

// Add the following lines to define additional keypad functions
#define UHS_HID_BOOT_KEY_END            0x59
#define UHS_HID_BOOT_KEY_BKSP           0x2A
#define UHS_HID_BOOT_KEY_DOWN           0x5A
#define UHS_HID_BOOT_KEY_PGDN           0x5B
#define UHS_HID_BOOT_KEY_LEFT           0x5C
#define UHS_HID_BOOT_KEY_CEN            0x5D
#define UHS_HID_BOOT_KEY_RIGHT          0x5E
#define UHS_HID_BOOT_KEY_HOME           0x5F
#define UHS_HID_BOOT_KEY_UP             0x60
#define UHS_HID_BOOT_KEY_PGUP           0x61
#define UHS_HID_BOOT_KEY_ENT            0x58
// Duplicate values with ZERO2 and PERIOD; these will be handled in hidboot.cpp
//#define UHS_HID_BOOT_KEY_INS            0x62
//#define UHS_HID_BOOT_KEY_DEL            0x63

... continue with the rest of hidboot.h and save the file.


hidboot.cpp

in hidboot.cpp:

> Fix the Enter key to send 0x0A rather than 0x13

line 163, change:
const uint8_t KeyboardReportParser::padKeys[5] PROGMEM = {'/', '*', '-', '+', 0x13};
to:
const uint8_t KeyboardReportParser::padKeys[5] PROGMEM = {'/', '*', '-', '+', 0x0A};

> if NumLock is ON, then send the numbers 0-9. Make this an 'and' condition

line 184, change:
}// Keypad Numbers
        else if(VALUE_WITHIN(key, 0x59, 0x61)) {
                if(kbdLockingKeys.kbdLeds.bmNumLock == 1)
                        return (key - 0x59 + '1');
to:
}// Keypad Numbers 1-9
        else if ( (VALUE_WITHIN(key, 0x59, 0x61)) &&
                (kbdLockingKeys.kbdLeds.bmNumLock == 1) ) {
                        return (key - 0x59 + '1');

> add code for the additional keypad keys

lines 193-201:
                switch(key) {
                        case UHS_HID_BOOT_KEY_SPACE: return (0x20);
                        case UHS_HID_BOOT_KEY_ENTER: return (0x13);
                        case UHS_HID_BOOT_KEY_ZERO2: return
                                ((kbdLockingKeys.kbdLeds.bmNumLock == 1) ? '0': 0);
                        case UHS_HID_BOOT_KEY_PERIOD: return
                                ((kbdLockingKeys.kbdLeds.bmNumLock == 1) ? '.': 0);
                }
        }
        return ( 0);
}

to:
                switch(key) {
                        case UHS_HID_BOOT_KEY_SPACE: return (0x20);
                        case UHS_HID_BOOT_KEY_ENTER: return (0x0A); // 0x13 change to line feed
                        case UHS_HID_BOOT_KEY_ZERO2:
                           { 
                        if (kbdLockingKeys.kbdLeds.bmNumLock == 1) { 
                          return ('0');
                          } else { // INS key + NumLock OFF send 'space'
                          return (0x20);
                          }
                          }

                        case UHS_HID_BOOT_KEY_PERIOD: 
                       
                        if (kbdLockingKeys.kbdLeds.bmNumLock == 1) { 
                          return ('.');
                          } else { // DEL key
                          // works for Text Wrangler send (0xD4); // delete forward
                          return (0xEA); // keypad 0
                          }
                          }
/* the following codes work for sending keypad keys to Text Wrangler with numlock off
   cursor controls, such as, left arrow, right arrow, pgdn, home, etc
case UHS_HID_BOOT_KEY_END: return ((kbdLockingKeys.kbdLeds.bmNumLock == 0) ? 0xD5: 0);
                        case UHS_HID_BOOT_KEY_BKSP: return (0x08);
                        case UHS_HID_BOOT_KEY_DOWN: return ((kbdLockingKeys.kbdLeds.bmNumLock == 0) ? 0xD9: 0);
                        case UHS_HID_BOOT_KEY_PGDN: return ((kbdLockingKeys.kbdLeds.bmNumLock == 0) ? 0xD6: 0);
                        case UHS_HID_BOOT_KEY_LEFT: return ((kbdLockingKeys.kbdLeds.bmNumLock == 0) ? 0xD8: 0);
                        // send 5 key for num lock off also
                        case UHS_HID_BOOT_KEY_CEN: return ((kbdLockingKeys.kbdLeds.bmNumLock == 0) ? 0x35: 0);
                        case UHS_HID_BOOT_KEY_RIGHT: return ((kbdLockingKeys.kbdLeds.bmNumLock == 0) ? 0xD7: 0);
                        case UHS_HID_BOOT_KEY_HOME: return ((kbdLockingKeys.kbdLeds.bmNumLock == 0) ? 0xD2: 0);
                        case UHS_HID_BOOT_KEY_UP: return ((kbdLockingKeys.kbdLeds.bmNumLock == 0) ? 0xDA: 0);
                        case UHS_HID_BOOT_KEY_PGUP: return ((kbdLockingKeys.kbdLeds.bmNumLock == 0) ? 0xD3: 0);
*/
// FSX: send keyboard numbers with numlock ON
// FSX: send the following keypad numbers for changing views with numlock OFF
                        case UHS_HID_BOOT_KEY_END: return ((kbdLockingKeys.kbdLeds.bmNumLock == 0) ? 0xE1: 0);
                        case UHS_HID_BOOT_KEY_BKSP: return (0x08);
                        case UHS_HID_BOOT_KEY_DOWN: return ((kbdLockingKeys.kbdLeds.bmNumLock == 0) ? 0xE2: 0);
                        case UHS_HID_BOOT_KEY_PGDN: return ((kbdLockingKeys.kbdLeds.bmNumLock == 0) ? 0xE3: 0);
                        case UHS_HID_BOOT_KEY_LEFT: return ((kbdLockingKeys.kbdLeds.bmNumLock == 0) ? 0xE4: 0);
                        // send 5 key for num lock off also
                        case UHS_HID_BOOT_KEY_CEN: return ((kbdLockingKeys.kbdLeds.bmNumLock == 0) ? 0xE5: 0);
                        case UHS_HID_BOOT_KEY_RIGHT: return ((kbdLockingKeys.kbdLeds.bmNumLock == 0) ? 0xE6: 0);
                        case UHS_HID_BOOT_KEY_HOME: return ((kbdLockingKeys.kbdLeds.bmNumLock == 0) ? 0xE7: 0);
                        case UHS_HID_BOOT_KEY_UP: return ((kbdLockingKeys.kbdLeds.bmNumLock == 0) ? 0xE8: 0);
                        case UHS_HID_BOOT_KEY_PGUP: return ((kbdLockingKeys.kbdLeds.bmNumLock == 0) ? 0xE9: 0);
                        //case UHS_HID_BOOT_KEY_ENT: return (0x0A); // Handled above in padKeys
                        //case UHS_HID_BOOT_KEY_DEL: return ((kbdLockingKeys.kbdLeds.bmNumLock == 0) ? 0x08: 0);
                        //case UHS_HID_BOOT_KEY_INS: return ((kbdLockingKeys.kbdLeds.bmNumLock == 1) ? 0x01: 0);
                }
        }
        return ( 0);
}


Keypad USB output to Text Wrangler with NumLock ON

0123456789./*-+
enter (new line) (space)

Keypad Sketch output to Serial Monitor with NumLock ON

Start
USB OSC started OK.

DN Keycode     >53<     (Turn on NumLock)
Keycode Decimal: 83

DN Keycode     >62<     
Keycode Decimal: 98
 >> Send ASCII Key Hex: 30, Dec: 48, ASCII Char: 0

DN Keycode     >59<     
Keycode Decimal: 89
 >> Send ASCII Key Hex: 31, Dec: 49, ASCII Char: 1

DN Keycode     >5A<     
Keycode Decimal: 90
 >> Send ASCII Key Hex: 32, Dec: 50, ASCII Char: 2

DN Keycode     >5B<     
Keycode Decimal: 91
 >> Send ASCII Key Hex: 33, Dec: 51, ASCII Char: 3

DN Keycode     >5C<     
Keycode Decimal: 92
 >> Send ASCII Key Hex: 34, Dec: 52, ASCII Char: 4

DN Keycode     >5D<     
Keycode Decimal: 93
 >> Send ASCII Key Hex: 35, Dec: 53, ASCII Char: 5

DN Keycode     >5E<     
Keycode Decimal: 94
 >> Send ASCII Key Hex: 36, Dec: 54, ASCII Char: 6

DN Keycode     >5F<     
Keycode Decimal: 95
 >> Send ASCII Key Hex: 37, Dec: 55, ASCII Char: 7

DN Keycode     >60<     
Keycode Decimal: 96
 >> Send ASCII Key Hex: 38, Dec: 56, ASCII Char: 8

DN Keycode     >61<     
Keycode Decimal: 97
 >> Send ASCII Key Hex: 39, Dec: 57, ASCII Char: 9

DN Keycode     >54<     
Keycode Decimal: 84
 >> Send ASCII Key Hex: 2F, Dec: 47, ASCII Char: /

DN Keycode     >55<     
Keycode Decimal: 85
 >> Send ASCII Key Hex: 2A, Dec: 42, ASCII Char: *

DN Keycode     >56<     
Keycode Decimal: 86
 >> Send ASCII Key Hex: 2D, Dec: 45, ASCII Char: -

DN Keycode     >57<     
Keycode Decimal: 87
 >> Send ASCII Key Hex: 2B, Dec: 43, ASCII Char: +

DN Keycode     >63<     
Keycode Decimal: 99
 >> Send ASCII Key Hex: 2E, Dec: 46, ASCII Char: .

DN Keycode     >58<     
Keycode Decimal: 88
 >> Send ASCII Key Hex: A, Dec: 10, ASCII Char: (insert enter line feed)

DN Keycode     >2A<     
Keycode Decimal: 42
 >> Send ASCII Key Hex: 8, Dec: 8, ASCII Char: (backspace delete)


>>>>> NumLock OFF

DN Keycode     >53<     NumLock OFF
Keycode Decimal: 83

DN Keycode     >62<     
Keycode Decimal: 98
 >> Send ASCII Key Hex: 20, Dec: 32, ASCII Char:  (ins)

DN Keycode     >59<     
Keycode Decimal: 89
 >> Send ASCII Key Hex: E1, Dec: 225, ASCII Char: (end)

DN Keycode     >5A<     
Keycode Decimal: 90
 >> Send ASCII Key Hex: E2, Dec: 226, ASCII Char: (down arrow)

DN Keycode     >5B<     
Keycode Decimal: 91
 >> Send ASCII Key Hex: E3, Dec: 227, ASCII Char: (pgdn)

DN Keycode     >5C<     
Keycode Decimal: 92
 >> Send ASCII Key Hex: E4, Dec: 228, ASCII Char: (left arrow)

DN Keycode     >5D<     
Keycode Decimal: 93
 >> Send ASCII Key Hex: E5, Dec: 229, ASCII Char: (5)

DN Keycode     >5E<     
Keycode Decimal: 94
 >> Send ASCII Key Hex: E6, Dec: 230, ASCII Char: (right arrow)

DN Keycode     >5F<     
Keycode Decimal: 95
 >> Send ASCII Key Hex: E7, Dec: 231, ASCII Char: (home)

DN Keycode     >60<     
Keycode Decimal: 96
 >> Send ASCII Key Hex: E8, Dec: 232, ASCII Char: (up arrow)

DN Keycode     >61<     
Keycode Decimal: 97
 >> Send ASCII Key Hex: E9, Dec: 233, ASCII Char: (pgup)

DN Keycode     >54<     
Keycode Decimal: 84
 >> Send ASCII Key Hex: 2F, Dec: 47, ASCII Char: /

DN Keycode     >55<     
Keycode Decimal: 85
 >> Send ASCII Key Hex: 2A, Dec: 42, ASCII Char: *

DN Keycode     >56<     
Keycode Decimal: 86
 >> Send ASCII Key Hex: 2D, Dec: 45, ASCII Char: -

DN Keycode     >57<     
Keycode Decimal: 87
 >> Send ASCII Key Hex: 2B, Dec: 43, ASCII Char: +

DN Keycode     >58<     
Keycode Decimal: 88
 >> Send ASCII Key Hex: 0A, Dec: 10, ASCII Char: (line feed)

DN Keycode     >2A<     
Keycode Decimal: 42
 >> Send ASCII Key Hex: 08, Dec: 8, ASCII Char: (backspace delete)

DN Keycode     >63<     
Keycode Decimal: 99
 >> Send ASCII Key Hex: EA, Dec: 234, ASCII Char: (forward delete)

<end>

(September 9, 2017)

Friday, September 8, 2017

FSX - Add a Keypad for ATC (Part 1)

Tutorial for Adding a Keypad for ATC to FSX — 

Purpose: Microsoft Flight Simulator (FSX) uses numbers to select ATC menu choices. It must be easy to add an additional USB keypad to enter numbers for ATC. Right? No, not so much...

Impulse Buy:  I saw a USB keypad on sale at K-Mart and thought it was a cheap and easy fix for number entry for FSX. When the keypad was plugged into the computer running FSX, the keypad on the keyboard and the new USB keypad both sent the same key strokes to FSX. The PC did not differentiate between USB keyboard/keypad devices, and key strokes from any one will mingle with key strokes from another. The second revelation was that keypad numbers are not the numbers on the top of the keyboard. Numbers are not numbers??? That was not what I expected. Numbers entered on the keypad move the FSX 3D views.

Simple Fix - a software key stroke translator: After searching Google for a simple fix to the problem, it looked like a simple software fix of translating characters from USB on a PC was not going to happen. Many people were asking for translators, for a variety of uses, but nothing simple seemed to exist for PCs.

Maybe a hardware solution: During my searching for a key stroke translator, I saw multiple web pages that either had requests for key stroke translators or some that showed various solutions which all seemed pretty involved.

Other Options: I reviewed my options, which included: 1) abandon the USB keypad idea and go back to flying the simulator; 2) buy a non-USB keypad and wire it up to Mobiflight or other microcomputer solution using the keys to send single button commands to FSX; 3) continue down the USB path in an attempt to tame the beast. I had the idea that having a key stroke translator might have other uses than for FSX. I also wanted the solution to be as inexpensive as possible.

Solution: An Arduino-based key stroke mapper was successfully built to connect the USB keypad to communicate with FSX. The solution will first be presented, then other lessons learned during the process will be presented in future blogs.

Implementation: The key stroke mapper is a device that connects to the PC/Mac USB port on one side and to the USB keypad on the other side. Keypad keystroke signals (USB) are intercepted by the translator, re-mapped, then sent out as USB keystroke signals to the PC/Mac. In this way, any keystroke from the keypad can be mapped to another value and sent to the PC. The hardware/software combination handles the conversion of the key strokes.

Hardware: The key stroke mapper is comprised of two small circuit boards. A MAX3241E circuit board with a built-in USB-A port has built-in USB translation firmware. The keypad plugs into that board, and key strokes from the keypad will be received and signals are sent out through the SPI pins.

The second circuit board is a small Arduino-type Pro Micro which can be programmed to do the key stroke translations. The Pro Micro, first developed by Sparkfun, uses a 32U4 processor that includes USB communication hardware. The Pro Micro board has a micro-USB port that can be connected to a PC for programming using the Arduino IDE. IDE 1.8.4 was used for this project.

Clones of both the MAX3421E and Pro Micro can be purchased for a few dollars from eBay  and were used in this project. Care must be taken with eBay purchases to obtain the Pro Micro with 32U4 processor (with USB), which is not the same as a Pro Mini with an ATmega328 micro controller (same as UNO, without USB).

The MAX3421E is a 3.3V device and the Pro Micro can be obtained as either 5V or 3.3V versions from US vendors. I could not find 3.3V Pro Micro boards on eBay and 5V boards were purchased. This decision added a fair amount of extra work during development. Getting both boards as 3.3V devices would have reduced construction and development time. It turns out that the 5V Pro Micro boards can be converted to 3.3V using a 3.3V voltage regulator, which was done. This allowed the SPI pins to be directly connected between the two boards without the risk of burning up the MAX3421E board with 5V signals. 

The final translator hardware is a MAX3421E circuit board connected through the four SPI pins to the Pro Micro SPI pins, and sharing common ground connections. The PC USB provides the 5V to power the 5V to 3.3V converters for the Pro Micro and MAX3421E boards and the 5V is also routed out the MAX3421E USB connector to power the USB keypad.

Software: USB is not easy. The MAX3421E and Pro Micro 32U4 processors contain USB firmware to allow USB communication through the on-board USB hardware connectors. Serial keyboards were created before USB existed, then ASCII codes were created to standardize codes used in computers. Keyboards were then standardized to send out ASCII-coded values for numbers, letters, and limited hardware controls (carriage return, form feed, end of line, etc). When the USB standard was created, a different coding was created which is referred to as Scan Codes and has many more codes than ASCII. The USB Scan Codes are not the same as ASCII keyboard/keypad codes. HID Keyboard drivers take care of the mapping of USB Scan Codes to/from ASCII characters. Scan Codes for keypad keys are different than keyboard keys, even if the keys seem to be for the same number or function. For this keypad mapper project, the Arduino sketch software handles the translation of USB Scan Codes for specific keys to the desired ASCII codes which are sent from the Pro Micro through USB to the PC, thus re-mapping some of the keypad keys.

End Result: The keypad mapper hardware/software does the following: When the keypad Numlock LED is ON, numbers 0,1,2,...8,9 are sent to the PC as 'keyboard' numbers which will activate FSX ATC numeric commands. This was the original goal of the project: to use a keypad for sending FSX ATC number commands (which is the same as sending 'keyboard' numbers to FSX ATC). Recall that the default USB keypad numbers are not 'numbers' to ATC but rather they change the 3D views in FSX.

When Numlock is OFF, a default USB keypad will send flight stick controls to FSX, or those keys will send cursor controls, such as, move left, move up, home, etc., to a text editor.

For FSX using the mapper (as programmed), the Numlock OFF keypad keys will not send cursor controls, but will send the 3D view controls that were originally sent from the keypad to FSX when the Numlock was ON. This might seem to be a bit of busy work, but to me, when the Numlock LED is ON, numbers are sent, and when the LED is OFF, view commands are sent. However, it does conflict with the normal FSX keypad mapping. If a user does not want to have the Numlock ON-OFF keys changed, that can be handled in the Arduino sketch software.

The USB keypad can be mounted where it is convenient to use for making FSX ATC menu choices. Since the keypad with translator sends out numbers, it could also be used as a generic number entry keypad for other FSX uses, such as, entering frequencies. A selector switch would be needed to select the desired function that handled the numbers from the keypad which would be retained in a register or digital readout. The selector switch could also be programmed as one of the keypad keys.

Part 2 of this series will present the functional hardware and software used in this keypad translator.

(September 8, 2017)





Wednesday, July 19, 2017

FSX with Live Camera Windowed Views (Part 1)



Tutorial for OpusFSI Live Camera Window Views on Client Displays for FSX — 

Purpose: Microsoft Flight Simulator (FSX) provides a means for displaying multiple graphics views on a PC with one or more extended displays. Using extended displays spanning 2 or more monitors provides a mechanism for viewing outside the aircraft, both left and right, as an aid for making the simulation more realistic. Additional views are especially useful for landing approaches, both VFR and IFR. Extended displays across two or more monitors is set up in Windows:Control Panel:...Display:Screen Resolution.

FSX on one PC with multiple views on several displays can easily overwhelm the system causing software crashes during a flight simulation. One approach to fixing this problem is to run FSX on two or more PCs which spreads the load and reduces the occurence of crashes.

Having FSX run on more than one PC requires some intermediate software to coordinate the simulations and displays between the PCs. One such Product is WidevieW, and a second is OpusFSI.

Wideview was purchased and tested but had issues with aircraft bouncing around on the ground and the software was abandoned without much luck.

OpusFSI was purchased and installed and provides good integration of views across two PCs. However, I was not able to find a detailed example of how to create multiple views in a fairly seamless manner. Opus Software provides a vast amount of documentation, but the documentation lacks examples of how to implement specific uses of the software. This tutorial describes one way of using the OpusFSI software to create side views from the perspective of the pilot of the simulator. The software vendor or other users may have other approaches to achieve the same end result.


Hardware/Software Configuration:

The hardware used for this tutorial include two PCs: 1) Intel Core-2 with Win7 32-bit Laptop with 2 displays (laptop display and Mini Display Port to VGA display); 2) Intel i7 with Win8.1 64-bit desktop with 2 displays (HDMI and Display Port, both 1080P). 

The Win7 laptop PC software includes: 1) FSX with Acceleration, 2) Mobiflight for home-built controls, 3) FSUIPC; and, 4) OpusFSI_V5 Server for FSX.

The Win8.1 desktop PC software includes:  1) FSX with Acceleration, 2) FSUIPC; and, 3) OpusFSI_V5 Client for FSX.

The OpusFSI documentation was followed for installing the Server and Client software, for setting the file sharing and permissions, and for establishing the communication via ethernet on home network between the Server and Client. The Live View Test, run on the Client, then the Server, will demonstrate whether the Server is communicating with the Client over the local network. Those tests must work before further installation tasks can be accomplished. These tests do not test if file sharing and permissions are set correctly, but will establish that the Server actually can control the Client. The Opus documentation discusses in detail the requirements for setting sharing and permissions.

The Server window "Configure" button is used to configure the number of Client machines, to enable Live Camera, and to provide the path to the FSX install directory.

The Server window "Cameras" button provides a window for defining and managing views. For this tutorial, 2D Windowed views will be created for the Cessna 172 aircraft or for all aircraft. Using the views for all aircraft is a bit easier for first use.


Software Start-up:

Start both the Server and Client PCs.
Run the FSX software with FSUIPC on both machines.
Run the FSI Server on the Server PC using a shortcut target "C:\OpusFSI_v5\FSISERVER.EXE FSX" as Administrator.
Run the FSI Client on the Client PC using a shortcut target "C:\OpusFSI_v5\FSICLIENT.EXE FSX" as Administrator.

The FSIServer and FSIClient will add an Add-On to the FSX Add-Ons menu along with the FSUIPC Add-On.

Have a copy of the same FSX flight on both machines. Load that same FSX flight on the Server FSX and the Client FSX. Both PCs should display the same view on startup if FSI Live Views have not been created. On the Client machine, use menu FSX:Options:Settings:Display and set the 2D display as the default and, if desired, set the transparency to 100%. This will hide the dashboard panel from showing, however, it will still exist and can cause some confusion when moving views that overlap with the transparent panel display. Save the changes to the Client aircraft flight.


Live Views:

Client Live Views are created using the FSI Server window, "Cameras" button, and Camera Management window. 
1) On the Camera Management window, first click the "Select Computer System" button and choose the Client PC by name. There should be both the Server PC and Client PC names displayed on the list of choices. The field to the right of the button will display the chosen Client PC name. 
2) Choose "All Aircraft Types", and "Create" to create a new view. 
3) Click "2D Cockpit", "OK", and the "Camera Editing" window will display. 
4) Rename the "Camera" named "Camera View x" to something meaningful to you. 
5) Choose "Windowed View", "2D Cockpit", set X to "-3", set Y to "1", set Pitch to "6.4", and set Yaw to "-40". You should see a full window view on the Client screen with a view 40 degrees to the left of front from the cockpit. The X=-3 moves the view to the left outside the cockpit and the pitch and yaw rotate the view to the desired angle. Push "OK" and "OK" again to return to the FSI Server window.

What will seem odd at this point is that the Client display will not show the new view that was just created. To get the view to display requires that a different aircraft (Not the same type, just choose the Piper Cub or something else) and the new view will display. Now go back and choose your desired aircraft and the rotated view should display. The Instrument Panel or 3D Virtual Cockpit may also show, you just have to remove those from the display.


Adjust the Views on the Client Displays:

For one or more displays, set your FSX window to "not full screen" and expand it to span across your multiple displays, as big as you want your FSX window to be. 

According to the OpusFSI documentation, you can have a view that is either "docked" or "undocked". A docked view will reside inside the FSX main window, while an undocked view can be moved outside the FSX main window. However, FSX will not load a view as "undocked" and any view that you want as undocked will require manual intervention each time the software loads. To fix that problem, the FSX window can be made as large as the multiple displays will allow. Then the "docked" views can be loaded, sized, and moved to the desired locations automatically as the flight loads.

1) Resize the Client FSX window as large as you desire.
2) Move and resize your Windowed View(s) to where you want it on your Client display inside of the FSX window.
3) Save your aircraft using the FSX:Flights:Save... using your current flight name. At this point the Client flight is a bit different than the Server flight of the same name, but that is OK. Saving the aircraft will save the large size of the FSX window.
4) On the Client FSX menu choose "Add-Ons:OpusFSI:Save Windowed Views" to save the view size and location within the FSX window. If a Client view is then moved, clicking "Add-Ons:OpusFSI:Restore Windowed Views" will move the views back to the locations and sizes when they were last saved.

This process can be used to create multiple views that will load automatically on the Client when FSX/FSUIPC/FSI are started on the Server and Client.

Issues:

As mentioned earlier, "undocked" views must be manually undocked. This can be mostly overcome by expanding the FSX window and just displaying the Windowed Views as "docked" views within the FSX window. 

According to Opus Support, if an "undocked" view is desired, a "docked" view can be "undocked" (right mouse click) and the "Add-Ons:OpusFSI:Restore Windowed Views" will move the views back to the locations and sizes when they were last saved, including the now "undocked" views which may be moved to locations outside the FSX window. I was not able to get that functionality to work, so development focused on using docked views within the FSX window spanned across multiple displays.

A second issue arises if the Client FSX goes down by a crash or through manually stopping the FSX software. If I am in a lengthy simulation and the Client PC FSX crashes, I would prefer to restart the Client and continue with the flight. What happens when the FSX Client stops on my system is that the C172 aircraft.cfg file gets over-written when the FSX Client is restarted. At that point, the aircraft.cfg no longer contains the Windowed View definitions, so they can not load and display on the Client.

I have found a couple of fixes for this problem that may be a bug in my system that I do not know how to fix at this time.

1) After restarting the Client FSX but before selecting the flight/aircraft, replace the aircraft.cfg file without the view configurations with a copy of the file that contains the view configurations. Use Notepad++ to edit the .cfg files and you will find where the missing view configurations should be.

The default aircraft.cfg file used by FSI will have the following lines at about line number 476:

=============== Example aircraft.cfg without Windowed Views ===============
[deice_system]
structural_deice_type=0     //0 = None, 1 = Heated Leading Edge, 2 = Bleed Air Boots, 3 = Eng Pump Boots

[CameraDefinition.899]
Title = "OpusFSI Aircraft View"
Guid = {01021987-E220-6507-1024-462840738899}
Description = OpusFSI Aircraft View
Origin = Center
SnapPbhAdjust = Swivel
SnapPbhReturn = FALSE
PanPbhAdjust = Swivel
PanPbhReturn = FALSE
ShowAxis = FALSE
ShowWeather = TRUE
Category = Aircraft
MomentumEffect = FALSE
ShowLensFlare = FALSE
CycleHidden = No
PitchPanRate = 30
HeadingPanRate = 75
PanAcceleratorTime = 0
SmoothZoomTime = 2.0
AllowZoom = TRUE
XyzAdjust = TRUE
AllowPbhAdjust = TRUE
InitialZoom = 0.8
InitialXyz = 0,6,-30
InitialPbh = 6,0,0
Track = None

[CameraDefinition.0]
Title = "Right Wing"
Guid = {C690EAFD-223A-42d0-99E0-681ADF93BB59}
Description = View from the right wing tip looking at the cockpit
Origin = Center
SnapPbhAdjust = Swivel
...

===============================================================


=============== Example aircraft.cfg with Windowed Views =============
[deice_system]
structural_deice_type=0     //0 = None, 1 = Heated Leading Edge, 2 = Bleed Air Boots, 3 = Eng Pump Boots

[CameraDefinition.899]
Title = "OpusFSI Aircraft View"
Guid = {01021987-E220-6507-1024-462840738899}
Description = OpusFSI Aircraft View
Origin = Center
SnapPbhAdjust = Swivel
SnapPbhReturn = FALSE
PanPbhAdjust = Swivel
PanPbhReturn = FALSE
ShowAxis = FALSE
ShowWeather = TRUE
Category = Aircraft
MomentumEffect = FALSE
ShowLensFlare = FALSE
CycleHidden = No
PitchPanRate = 30
HeadingPanRate = 75
PanAcceleratorTime = 0
SmoothZoomTime = 2.0
AllowZoom = TRUE
XyzAdjust = TRUE
AllowPbhAdjust = TRUE
InitialZoom = 0.8
InitialXyz = 0,6,-30
InitialPbh = 6,0,0
Track = None

[CameraDefinition.990]
Title = "OpusFSI Windowed View 1"
Guid = {01021987-E220-6507-1024-462840738990}
Description = OpusFSI Customized Scenic View
Origin = Cockpit
SnapPbhAdjust = Swivel
SnapPbhReturn = FALSE
PanPbhAdjust = Swivel
PanPbhReturn = FALSE
ShowAxis = FALSE
ShowWeather = TRUE
Category = Custom
MomentumEffect = FALSE
CycleHidden = No
PitchPanRate = 30
HeadingPanRate = 75
PanAcceleratorTime = 0
SmoothZoomTime = 2.0
AllowZoom = TRUE
XyzAdjust = TRUE
AllowPbhAdjust = TRUE
InitialZoom = 1.0
InitialXyz = -3,1,0
InitialPbh = 6.4,0,-40.5595
Track = None

[CameraDefinition.0]
Title = "Right Wing"
Guid = {C690EAFD-223A-42d0-99E0-681ADF93BB59}
Description = View from the right wing tip looking at the cockpit
Origin = Center
SnapPbhAdjust = Swivel
SnapPbhReturn = FALSE
...
=============================================================

On my Client PC, anytime the Client FSX and FSI software is shutdown, all of the aircraft.cfg files revert to the default copies. To fix this problem, make backup copies of the files that contain the additional view definitions.

In addition, the view definitions should also exist in the Client C:\OpusFSI_V5\OpusFSI_Server_CamDefs.txt file. The contents of this file can be pasted into the default aircraft.cfg to add the view definitions manually. See the above example for the location of these definitions in the .cfg file.

To prepare files for this fix on the Client, when the Client views are properly displayed, navigate to "C:\Program Files\Microsoft Games\Microsoft Flight Simulator X\SimObjects\Airplanes\(your aircraft)\aircraft.cfg", copy the file, paste the file as a copy, and rename it so that you have a copy to use when required (after a Client FSX shutdown or crash). If this copy is recopied over the file without the view definitions, and then the FSX flight is chosen on the Client, the flight and aircraft will load with the saved views. You may need to "Open" or "Restore" the Client Windowed views, but the views should display properly and the current flight on the Server can resume with little impact.

You can force the Client to retain the aircraft.cfg file with the added Live View definitions by changing the Properties:Read Only and maybe also turning off Everyone permissions on the file or folder. This fix just stops the modified files from being over-written by the Server so the views are always defined. Any desired changes to the views would require an update of the aircraft.cfg files with the revised view definitions. (Just one more thing to have to remember)

A second fix to the missing aircraft.cfg view definitions, is to use FSI to write the proper files from the Server back to the Client. To do this, have the Client FSX/FSUIPC/FSI running,  but do not load the flight, yet.

On the Server, use the FSI Server window, click "Cameras" button, choose any one of the 2D Windowed views on the Client, "Edit", to display the edit window, then push "OK", "OK" twice to exit the Edit window back to the FSI Server window. The Server will update the necessary files on the Client. If the flight has not yet been loaded on the client, do so now. If a flight was already active, select a different type of aircraft to force the loading of the modified files, then select your aircraft, and use "Open" or "Restore" if nessary to put the views in the desired locations.

If you find a better method or fix a bug in what has been described above, please share your knowledge.

Thanks