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)