Debouncing the ILI9341, is there a simple complete example somewhere?

clinker8

Well-known member
While I am thrashing away, attempting to get lvgl to work (which seems like a longer term project), I still need to de-bounce some buttons on my PJRC ILI9341. What is the recommended way to do this? I have searched the examples, and haven't found any that seem to be applicable. There are somethings which might work, but they seem fine for simple examples, but entirely ugly for complicated situations.

I've got dozens of buttons, in various menus, so I'm a bit overwhelmed thinking about what might work. Each menu is a different screen, but buttons do overlap physically. Therefore I do not want a button press in menu 0 which causes a transition to menu 1, to cause a button press in menu 1, since my finger is still there! The transitions occur faster than a human finger can move. Can bounce2 be adapted to this? I looked at it, but it doesn't really seem compatible with a touch screen controlled by the XPT2046.

Here's a snippet of the buttons that I look at in menu 0. Right now there are 4 menus (screens) and I'd like to add some more. I'd love to make some toggle buttons to simplify the interface, but need a good way to ensure the toggle is clean.
Code:
void processTouch() {
  if (ts.touched()) {
    TS_Point p = ts.getPoint();
    // this maps the touched point to a pixel reference system
    uint16_t px = map(p.x, TS_MINY, TS_MAXY, 0, tft.width());
    p.y = map(p.y, TS_MINX, TS_MAXX, 0, tft.height());
    p.x = px;
    //Serial.printf("Point touched is: p.x = %i, p.y = %i\n", p.x, p.y);
    uint16_t x, y, w, h, nudge = 0;
    switch (menu) { // go thru the menu choices
      case 0: {
        x=1; y=199; w=80, h=40;
        if ((p.y > y)&&(p.y< y+h) && (p.x>x)&&(p.x<x+w))
          setup0touched = true;  // we go to setup mode
        w=80; h=40; x=tft.width()-2*w-1; y=199; 
        if ((p.y > y)&&(p.y< y+h) && (p.x>x)&&(p.x<x+w))
            start0touched = true;  // we go to start mode
        w=80; h=40; x=tft.width()-w-1; y=199;
        if ((p.y > y)&&(p.y< y+h) && (p.x>x)&&(p.x<x+w))
            stop0touched = true;  // we go to stop mode
        // process RH/LH threading, Zero Z, Zero X buttons
        w=80; h=40; x=tft.width()-w-1; y=60; // ZeroZ button
        if ((p.y > y)&&(p.y< y+h) && (p.x>x)&&(p.x<x+w))
            zeroZtouched = true;  // we zero the Z DRO
        w=80; h=40; x=tft.width()-w-1; y=100; // ZeroX button    
        if ((p.y > y)&&(p.y< y+h) && (p.x>x)&&(p.x<x+w))
            zeroXtouched = true;  // we zero the X DRO
        
        // is there a way to debounce these two items here?    
        w=80; h=40; x=tft.width()-3*w-1; y=199;
        if ((p.y > y)&&(p.y< y+h) && (p.x>x)&&(p.x<x+w))
            lefthandtouched = true;  // we activate LH threads
        w=80; h=40; x=tft.width()-3*w-1; y=tft.height()-1 -2*h;
        if ((p.y > y)&&(p.y< y+h) && (p.x>x)&&(p.x<x+w))
            righthandtouched = true;  // we activate RH threads                    
        break;
      }
   // more cases
Apologize for not putting everything in here. This particular file is 845 LOC, and there are 9 other files in the project. This is for my electronic lead screw project. It runs, cutting both metric and imperial threads, and feeding, but the handling of the touchscreen buttons needs improvement in my opinion. Would appreciate any straight forward suggestions. Thanks. My comment in the code about de-bouncing was addressed to the right hand / left hand selection, but the comment applies to all button presses.

Slightly earlier version running on my lathe. https://www.youtube.com/shorts/DSRq1QL-pwc Was scared to death videoing this. Sorry about the poor production value, I was too scared to even talk! Not sure if this is the first Teensy 4.1 powered electronic lead screw or not. Sure has been fun so far. Still need to get everything in cases and clean up the cabling.
 

Attachments

  • PXL_20221004_022517657_1080.jpg
    PXL_20221004_022517657_1080.jpg
    218.2 KB · Views: 26
Personally, I prefer a touch action to immediately generate visual feedback (I'm partial to expanding circles or circular arcs around the touch point), but the event itself is only triggered when the touch is removed. The latest touch location, not the first one, determines what is triggered. This way, if the user finger slips, they can just correct the location before releasing the touch. I like it, but others may disagree; and I'm definitely not an UX specialist!

The other option is to trigger on the initial touch, but not react to the same (area of) touch before the touch has been removed for some specific time.

A simple dead time on the touch itself –– that is, that after the initial touch, no new touch events are generated for the next 500ms or so –– doesn't really work well; sometimes us humans get stuck in our thoughts and the finger happens to dwell much longer than initially intended... Or we might be in a hurry, and tap the display in rapid succession (because we're so used to it the taps are ingrained in our motor nerves).
 
State-driven example of touch processing on PJRC ILI9341 touchscreen TFT

...I still need to de-bounce some buttons on my PJRC ILI9341. What is the recommended way to do this? I have searched the examples, and haven't found any that seem to be applicable. There are somethings which might work, but they seem fine for simple examples, but entirely ugly for complicated situations.

I've got dozens of buttons, in various menus, so I'm a bit overwhelmed thinking about what might work. Each menu is a different screen, but buttons do overlap physically. Therefore I do not want a button press in menu 0 which causes a transition to menu 1, to cause a button press in menu 1, since my finger is still there! The transitions occur faster than a human finger can move. Can bounce2 be adapted to this? I looked at it, but it doesn't really seem compatible with a touch screen controlled by the XPT2046.

@clinker8:

Here's one of my projects that I created some time back (this was originally targeted to an Arduino UNO, but was subsequently transitioned to the Teensy as I ran short of both code space & RAM) which ran on the T4.0 + PJRC ILI9341 touchscreen TFT. It makes use of a touch & display management approach that I created which employed a combination of state driven processing & custom menu definitions. Touch management was one of the challenging aspects of this project. Hopefully, what I created can serve as a useful example to help you with your electronic lead screw project.

The most pertinent aspects of implementing this approach are as follows:

You'll need to determine/measure the constants for your specific touchscreen:

Code:
const int TS_MINX = 260;
const int TS_MINY = 220;
const int TS_MAXX = 3850;
const int TS_MAXY = 3740;


These globals are used as part of the keypress logic:

Code:
char KeyPressed = 0;
char EncodedKey = 0;


This type definition & the corresponding variable are used to manage/control the state machine:

Code:
typedef enum
{
   SPLASH_STATE = 0, OPERATE_STATE, CONFIG_STATE
}  ENIGMA_STATE;

ENIGMA_STATE enigma_state = SPLASH_STATE;


These constants are used in detection of touch actions:

Code:
#define IS_PRESSED  true
#define NOT_PRESSED false


This function is significant to the state-driven drawing of what's on the screen:

Code:
draw_display()


The call to the process_buttons() function is the significant action in the normal loop processing that allows touches to be processed by the state machine, so spend sufficient time with the process_buttons() funciton contents to fully understand how & why it does what it does to determine what to do in response to a specific touch, using the current state to direct specific actions in response:

Code:
void loop()
{
   process_buttons();
}  // loop()


These variables that are local to the process_buttons() function are significant in determing when a touch has occurred:

Code:
   boolean key_pressed = false;
   boolean button_pressed = false;
   boolean wait_for_release = false;


Note that the process_buttons() function calls process_button_inputs(TS_Point p) when a touch is detected. This process_button_inputs(TS_Point p) function is significant to the state-driven determination of what to do in response to a specific touch for a specific state:

Code:
void process_button_inputs(TS_Point p)


Here is the full sketch for my project:

Code:
//
// Teensy Enigma Visual version 1.0 dated 12/21/2020 @1015
//    written by Mark J Culross, KD5RXT (kd5rxt@arrl.net)
//
// HARDWARE:
//
//    Teensy 4.0
//       available from PJRC.com https://www.pjrc.com/teensy-4-0/
//
//    ILI9341 Color 320x240 TFT Touchscreen display
//       available from PJRC.com https://www.pjrc.com/store/display_ili9341_touch.html
//
// Uses touchscreen input & color display to create a visual (graphical) simulation of the Enigma machine
//
// Uses the ILI9341_t3 library, which has been optimized for use with the Teensy TFT display, as well as
//    the XPT2046_Touchscreen.h touscreen library for that same display
//
// The design of the encode/decode processing in this sketch is based upon the original encode/decode engine from
//    the EnigmaSerial.ino sketch (w/ cleanup, optimization, & enhancements, all w/ no apparent adverse effects on
//    equivalent/correct operation)
//
// The versions of the Enigma machine that are simulated in this sketch were verified to operate correctly using
//    the excellent/versatile HTML-code utility titled "Universal Enigma Simulator v2.0" by Daniel Palloks
//    ( http://people.physik.hu-berlin.de/~palloks/js/enigma/index_en.html )
//
// See text at the bottom of this file for sample Enigma messages (http://franklinheath.co.uk) for testing/verification
//

// Uncomment the next line to see XY touch locations printed at the top of the screen for debugging
//#define DEBUG_TS

//
// The following pins are used in this project:
//
// PIN  D2  = (not used)
// PIN  D3  = (not used)
// PIN  D4  = (not used)
// PIN  D5  = (not used)
// PIN  D6  = (not used)
// PIN  D7  = (not used)
// PIN  D8  = TouchScreen chip select
// PIN  D9  = TFT/TS data/command select
// PIN D10  = TFT chip select
// PIN D11  = SPI MOSI (data in)
// PIN D12  = SPI MISO (data out)
// PIN D13  = SPI serial clock
// PIN  A0  = (not used)
// PIN  A1  = (not used)
// PIN  A2  = (not used)
// PIN  A3  = (not used)
// PIN  A4  = (not used)
// PIN  A5  = (not used)

// define constant display strings
#define TITLE             ("Teensy Enigma Visual Simulator")
#define VERSION0          ("ver 1.0 - 12/21/2020 @1015")
#define VERSION0SHORT     ("Teensy v1.0")
#define VERSION1          ("written by: Mark J Culross KD5RXT")
#define VERSION1SHORT     ("MJ Culross")
#define TAPSTART          ("Tap the Enigma touchscreen to begin...")
#define CREDIT1           (" Many thanks to Daniel Palloks for his\n\n excellent/versatile HTML-code utility\n\n Universal Enigma Simulator v2.0 which\n\n was used to validate proper operation\n\n\n Also, thanks & credit to the authors\n\n of EnigmaSerial, whose encode/decode\n\n engine was used as a model to design\n\n my sketch's encode/decode processing")


#include <ILI9341_t3.h>
#include <font_Arial.h> // from ILI9341_t3
#include <XPT2046_Touchscreen.h>
#include <SPI.h>

// This is calibration data for the raw touch data to the screen coordinates
// (NOTE: run the TFTcal-Teensy.ino sketch to determine the calibration values
//        for your specific touchscreen display, by touching the top-left
//        corner to find TS_MINX & TS_MINY, then rouching the bottom-right
//        corner to find TX_MAXX & TS_MAXY)
//const int TS_MINX = 260;
//const int TS_MINY = 220;
//const int TS_MAXX = 3850;
//const int TS_MAXY = 3740;

const int TS_MINX = 260;
const int TS_MINY = 220;
const int TS_MAXX = 3850;
const int TS_MAXY = 3740;


// The display uses hardware SPI, with pin #9 as DataCommand & pin #10 as ChipSelect
// MOSI=11, MISO=12, SCK=13
const int TFT_CHIP_SELECT = 10;
const int TFT_DATA_COMMAND = 9;
ILI9341_t3 tft = ILI9341_t3(TFT_CHIP_SELECT, TFT_DATA_COMMAND);

// The touchscreen uses hardware SPI, with pin #9 as DataCommand & pin #8 as ChipSelect
// MOSI=11, MISO=12, SCK=13
#define TIRQ_PIN  2  // interrupts are not used here
#define TS_CS_PIN  8

XPT2046_Touchscreen ts(TS_CS_PIN);
//XPT2046_Touchscreen ts(CS_PIN);  // Param 2 - NULL - No interrupts
//XPT2046_Touchscreen ts(CS_PIN, 255);  // Param 2 - 255 - No interrupts
//XPT2046_Touchscreen ts(CS_PIN, TIRQ_PIN);  // Param 2 - Touch IRQ Pin - interrupt enabled polling


const int MY_ILI9341_SHADOW_GRAY      = 0x1923;
const int MY_ILI9341_DARK_GRAY        = 0x29C5;
const int MY_ILI9341_MEDIUM_GRAY      = 0x4A89;
const int MY_ILI9341_LIGHT_GRAY       = 0x8410;
const int MY_ILI9341_BRASS            = 0xBC20;
const int MY_ILI9341_PAPER            = 0xFDE7;
const int MY_ILI9341_ORANGE           = 0xFC00;

#define STR_EXPAND(tok) #tok
#define STR(tok) STR_EXPAND(tok)

typedef enum
{
   USE_ROTOR_NONE=0, USE_ROTOR_ETW, USE_ROTOR_1, USE_ROTOR_2, USE_ROTOR_3, USE_ROTOR_4, USE_ROTOR_5, USE_ROTOR_6, USE_ROTOR_7, USE_ROTOR_8,
   USE_ROTOR_UKWA, USE_ROTOR_UKWB, USE_ROTOR_UKWC, USE_ROTOR_B, USE_ROTOR_G, USE_ROTOR_UKWBD, USE_ROTOR_UKWCD,
   USE_ROTOR_ETWD, USE_ROTOR_D1, USE_ROTOR_D2, USE_ROTOR_D3, USE_ROTOR_UKWD,
   USE_ROTOR_ETWR, USE_ROTOR_R1, USE_ROTOR_R2, USE_ROTOR_R3, USE_ROTOR_UKWR,
   USE_ROTOR_ETWS, USE_ROTOR_S1, USE_ROTOR_S2, USE_ROTOR_S3, USE_ROTOR_UKWS,
}  ROTORS;

typedef enum
{
   MACHINE_TYPE_M3=0,        // Standard model Enigma M3
   MACHINE_TYPE_M4,          // Standard model Enigma M4
   MACHINE_TYPE_ENIGMA_D,    // Business model Enigma D
   MACHINE_TYPE_ROCKET_K,    // Enigma Rocket K Railway
   MACHINE_TYPE_SWISS_K      // Enigma Swiss K
}  MACHINE_TYPE;

typedef enum
{
   CONFIG_M3_1939 = 0,
   CONFIG_EXAMPLE_1930,
   CONFIG_ENIGMA_D,
   CONFIG_ROCKET_K_RAILWAY,
   CONFIG_SWISS_K,
   CONFIG_TURING_1940,
   CONFIG_BARBAROSA_1941,
   CONFIG_M4_1942,
   CONFIG_U264_1942,
   CONFIG_SCHARNHORST_1943
}  CONFIG;

int config_setup = CONFIG_M3_1939;

// Rotor definitions, first two letters are the letter at which they rotate the one to the left,
//    the rest are the output given an input letter
//
// Standard Enigma - rotor definitions from Crypto Museum
//                  ABCDEFGHIJKLMNOPQRSTUVWXYZ
#define     ETW   --ABCDEFGHIJKLMNOPQRSTUVWXYZ
#define  ROTOR1   R-EKMFLGDQVZNTOWYHXUSPAIBRCJ
#define  ROTOR2   F-AJDKSIRUXBLHWTMCQGZNPYFVOE
#define  ROTOR3   W-BDFHJLCPRTXVZNYEIWGAKMUSQO
#define  ROTOR4   K-ESOVPZJAYQUIRHXLNFTGKDCMWB
#define  ROTOR5   A-VZBRGITYUPSDNHLXAWMJQOFECK
#define  ROTOR6   ANJPGVOUMFYQBENHZRDKASXLICTW
#define  ROTOR7   ANNZJHGRCXMYSWBOUFAIVLPEKQDT
#define  ROTOR8   ANFKQHTLXOCBJSPDZRAMEWNIUYGV
#define    UKWA   --EJMZALYXVBWFCRQUONTSPIKHGD
#define    UKWB   --YRUHQSLDPXNGOKMIEBFZCWVJAT
#define    UKWC   --FVPJIAOYEDRZXWGCTKUQSBNMHL
#define  ROTORB   --LEYJVCNIXWPBQMDRTAKZGFUHOS
#define  ROTORG   --FSOKANUERHMBTIYCWLQPZXVGJD
#define   UKWBD   --ENKQAUYWJICOPBLMDXZVFTHRGS
#define   UKWCD   --RDOBJNTKVEHMLFCWZAXGYIPSUQ

// Business Enigma D - rotor definitions from Universal Enigma Simulator by Daniel Palloks
//                  ABCDEFGHIJKLMNOPQRSTUVWXYZ
#define    ETWD   --JWULCMNOHPQZYXIRADKEGVBTSF
#define  ROTORD1  Z-LPGSZMHAEOQKVXRFYBUTNICJDW
#define  ROTORD2  F-SLVGBTFXJQOHEWIRZYAMKPCNDU
#define  ROTORD3  O-CJGDPSHKTURAWZXFMYNQOBVLIE
#define    UKWD   --IMETCGFRAYSQBZXWLHKDVUPOJN

// German Rocket K Railway - rotor definitions from Universal Enigma Simulator by Daniel Palloks
//                  ABCDEFGHIJKLMNOPQRSTUVWXYZ
#define    ETWR   --JWULCMNOHPQZYXIRADKEGVBTSF
#define  ROTORR1  O-JGDQOXUSCAMIFRVTPNEWKBLZYH
#define  ROTORR2  F-NTZPSFBOKMWRCJDIVLAEYUXHGQ
#define  ROTORR3  Z-JVIUBHTCDYAKEQZPOSGXNRMWFL
#define    UKWR   --QYHOGNECVPUZTFDJAXWMKISRBL

// Swiss K - rotor definitions from Universal Enigma Simulator by Daniel Palloks
//                  ABCDEFGHIJKLMNOPQRSTUVWXYZ
#define    ETWS   --JWULCMNOHPQZYXIRADKEGVBTSF
#define  ROTORS1  O-PEZUOHXSCVFMTBGLRINQJWAYDK
#define  ROTORS2  F-ZOUESYDKFWPCIQXHMVBLGNJRAT
#define  ROTORS3  Z-EHRVXGAOBQUSIMZFLYNWKTPDJC
#define    UKWS   --IMETCGFRAYSQBZXWLHKDVUPOJN

static const __FlashStringHelper * WHEELSF;

const __FlashStringHelper * LOGOX1;
const __FlashStringHelper * LOGOX2;

char EffSTECKER[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

char KeyPressed = 0;
char EncodedKey = 0;

boolean SerialRead = false;

typedef struct
{
   MACHINE_TYPE machine_type;
   char         STECKER[27];
   byte         WHEELTYPE[6]; // WHEELTYPE[0] REPRESENTS ENTRY CONTACTS (ETW)
                              // WHEELTYPE[1] REPRESENTS RIGHT ROTOR  (NOTE THE REVERSED ORDER VS THE OTHER ARRAYS !!)
                              // WHEELTYPE[2] REPRESENTS MIDDLE ROTOR (NOTE THE REVERSED ORDER VS THE OTHER ARRAYS !!)
                              // WHEELTYPE[3] REPRESENTS LEFT ROTOR   (NOTE THE REVERSED ORDER VS THE OTHER ARRAYS !!)
                              // WHEELTYPE[4] REPRESENTS ADDITIONAL WHEEL (M4 ONLY, OTHERWISE USE_ROTOR_NONE)
                              // WHEELTYPE[5] REPRESENTS REFLECTOR (UKW)
   byte         WHEELPOS[4];  // WHEELPOS[0] REPRESENTS LEFTMOST LETTER, ONLY ON M4, OTHERWISE UKW ON ROCKET, D, & SWISS K
                              // WHEELPOS[1] REPRESENTS LEFTMOST LETTER ON M3
                              // WHEELPOS[2] REPRESENTS MIDDLE LETTER
                              // WHEELPOS[3] REPRESENTS RIGHTMOST LETTER
   byte         ROTORPOS[4];  // ROTORPOS[0] REPRESENTS LEFTMOST ROTOR SETTING, ONLY ON M4, OTHERWISE UKW ON ROCKET, D, & SWISS K
                              // ROTORPOS[1] REPRESENTS LEFTMOST ROTOR SETTING ON M3
                              // ROTORPOS[2] REPRESENTS MIDDLE ROTOR SETTING
                              // ROTORPOS[3] REPRESENTS RIGHTMOST ROTOR SETTING
} EnigmaData;

EnigmaData EnigmaConfigData;
EnigmaData EnigmaCurrentData;

typedef enum
{
   SPLASH_STATE = 0, OPERATE_STATE, CONFIG_STATE
}  ENIGMA_STATE;

ENIGMA_STATE enigma_state = SPLASH_STATE;

#define IS_PRESSED  true
#define NOT_PRESSED false

const int BASE_X = 11; // the x-value origin of the top-left corner of the
                       //    brass-colored rectangle containing the rotor letter
                       //    - used for rotor-relative location calculations
const int BASE_Y = 37; // the y-value origin of the top-left corner of the
                       //    brass-colored rectangle containing the rotor letter
                       //    - used for rotor-relative location calculations

const int KEY_ROW_1 = 225;
const int KEY_ROW_2 = 255;
const int KEY_ROW_3 = 285;

const int LIGHT_ROW_1 = 135;
const int LIGHT_ROW_2 = 165;
const int LIGHT_ROW_3 = 195;

const int TAPE_LENGTH = 40; // number of characters printed on tape
char tape_data[TAPE_LENGTH];

int tape_group_size = 5;
int tape_group_counter = 0;

char start_plug = 0;

const int locs_x[] =
{
    31, // A
   144, // B
    94, // C
    81, // D
    69, // E
   106, // F
   131, // G
   156, // H
   194, // I
   181, // J
   206, // K
   219, // L
   194, // M
   169, // N
   219, // O
    19, // P
    19, // Q
    94, // R
    56, // S
   119, // T
   169, // U
   119, // V
    44, // W
    69, // X
    44, // Y
   144  // Z
};



// add a character to the tape
void add_char_to_tape(char ch)
{
   for (int i = 1; i < TAPE_LENGTH; i++)
   {
      tape_data[i-1] = tape_data[i];
   }
   tape_data[TAPE_LENGTH-1] = ch;

   if (tape_group_size > 0)
   {
      tape_group_counter++;
   }
   else
   {
      tape_group_counter = 0;
   }

   if ((tape_group_size != 0) && (tape_group_counter == tape_group_size))
   {
      tape_group_counter = 0;

      for (int i = 1; i < TAPE_LENGTH; i++)
      {
         tape_data[i-1] = tape_data[i];
      }
      tape_data[TAPE_LENGTH-1] = ' ';
   }

   draw_tape();
}  // add_char_to_tape()


// add a plug wire in config
void add_config_plug(char PlugKey1, char PlugKey2)
{
   EnigmaConfigData.STECKER[PlugKey1 - 65] = PlugKey2;
   EnigmaConfigData.STECKER[PlugKey2 - 65] = PlugKey1;

   draw_config_plugboard();
}  // add_config_plug()


// handle the stecker
void calculate_stecker(void)
{
   for (byte i = 0; i < 26; i++)
   {
      EffSTECKER[i] = EnigmaCurrentData.STECKER[i];
   }

   // this is a "convenient" place to print the machine type to the serial monitor port
   //    right before printing out the contents of all of the wheels

   show_machine_over_serial();

}  // calculate_stecker()


// clear the tape data
void clear_tape(void)
{
   for (int i=0; i < TAPE_LENGTH; i++)
   {
      tape_data[i] = ' ';
   }

   draw_tape();

   tape_group_counter = 0;
}  // clear_tape()


// decrement a wheel position
void decrement_wheel(int wheel)
{
   EnigmaCurrentData.WHEELPOS[wheel]--;

   if ((EnigmaCurrentData.WHEELPOS[wheel] < 'A') || (EnigmaCurrentData.WHEELPOS[wheel] > 'Z'))
   {
      EnigmaCurrentData.WHEELPOS[wheel] = 'Z';
   }

   draw_rotor_letters();
}  // decrement_wheel()


// draw the variable portions of the config display
void draw_config_display(void)
{
   // draw config machine type
   tft.fillRect(170, 85, 70, 15, ILI9341_CYAN);
   tft.setTextSize(1);
   tft.setCursor(178, 89);
   tft.setTextColor(ILI9341_BLACK);

   print_machine_type();

   draw_config_reflector();
   draw_config_tape_group_size();
   draw_config_wheels();
   draw_config_rings();
   draw_config_setup();
   draw_config_plugboard();
}  // draw_config_display()


// draw the plugboard in config
void draw_config_plugboard(void)
{
   char ch = 'A';
   if ((EnigmaConfigData.machine_type == MACHINE_TYPE_M3) || (EnigmaConfigData.machine_type == MACHINE_TYPE_M4))
   {
      tft.setTextSize(2);
      tft.setTextColor(ILI9341_BLACK);

      for (int i = 0; i < 13; i++)
      {
         if (EnigmaConfigData.STECKER[i] != ch)
         {
            tft.fillRect((i * 18) + 5, 195, 14, 18, ILI9341_MAGENTA);
            tft.fillRect((i * 18) + 5, 215, 14, 18, ILI9341_MAGENTA);
         }
         else
         {
            if (start_plug == ch)
            {
               tft.fillRect((i * 18) + 5, 195, 14, 18, MY_ILI9341_ORANGE);
            }
            else
            {
               tft.fillRect((i * 18) + 5, 195, 14, 18, ILI9341_CYAN);
            }
            tft.fillRect((i * 18) + 5, 215, 14, 18, MY_ILI9341_MEDIUM_GRAY);
         }
         tft.setCursor((i * 18) + 7, 197);
         tft.print(ch);
         tft.setCursor((i * 18) + 7, 217);
         tft.print(EnigmaConfigData.STECKER[i]);

         ch++;
      }

      for (int i = 13; i < 26; i++)
      {
         if (EnigmaConfigData.STECKER[i] != ch)
         {
            tft.fillRect(((i - 13) * 18) + 5, 240, 14, 18, ILI9341_MAGENTA);
            tft.fillRect(((i - 13) * 18) + 5, 260, 14, 18, ILI9341_MAGENTA);
         }
         else
         {
            if (start_plug == ch)
            {
               tft.fillRect(((i - 13) * 18) + 5, 240, 14, 18, MY_ILI9341_ORANGE);
            }
            else
            {
               tft.fillRect(((i - 13) * 18) + 5, 240, 14, 18, ILI9341_CYAN);
            }
            tft.fillRect(((i - 13) * 18) + 5, 260, 14, 18, MY_ILI9341_MEDIUM_GRAY);
         }
         tft.setCursor(((i - 13) * 18) + 7, 242);
         tft.print(ch);
         tft.setCursor(((i - 13) * 18) + 7, 262);
         tft.print(EnigmaConfigData.STECKER[i]);

         ch++;
      }
   }
   else
   {
      // erase plugboard area
      for (int y = 190; y < 280; y++)
      {
         tft.drawFastHLine(0, y, 240, ILI9341_BLACK);

         for (int x = random(7); x < 240; x+=random(7)+1)
         {
            int r = random(10);

            if (r < 2)
            {
               tft.drawPixel(x, y, MY_ILI9341_SHADOW_GRAY);
            }
            else
            {
               if (r < 4)
               {
                  tft.drawPixel(x, y, MY_ILI9341_DARK_GRAY);
               }
               else
               {
                  if (r < 7)
                  {
                     tft.drawPixel(x, y, MY_ILI9341_MEDIUM_GRAY);
                  }
               }
            }
         }
      }
   }
}  // draw_config_plugboard()


// draw the reflector in config
void draw_config_reflector(void)
{
   tft.fillRect(170, 105, 70, 15, ILI9341_CYAN);
   tft.setTextSize(1);
   tft.setCursor(178, 109);
   tft.setTextColor(ILI9341_BLACK);
   switch (EnigmaConfigData.WHEELTYPE[5])
   {
      case USE_ROTOR_UKWA:
      case USE_ROTOR_UKWD:
      case USE_ROTOR_UKWR:
      case USE_ROTOR_UKWS:
      {
         tft.print("    A");
      }
      break;

      case USE_ROTOR_UKWB:
      {
         tft.print("    B");
      }
      break;

      case USE_ROTOR_UKWC:
      {
         tft.print("    C");
      }
      break;

      case USE_ROTOR_UKWBD:
      {
         tft.print(" Thin B");
      }
      break;

      case USE_ROTOR_UKWCD:
      {
         tft.print(" Thin C");
      }
      break;
   }
}  // draw_config_reflector()


// draw the rings in config
void draw_config_rings(void)
{
   tft.setTextSize(1);
   tft.setTextColor(ILI9341_BLACK);

   if (EnigmaConfigData.machine_type == MACHINE_TYPE_M3)
   {
      tft.fillRect(110, 170, 25, 15, MY_ILI9341_MEDIUM_GRAY);
   }
   else
   {
      tft.fillRect(110, 170, 25, 15, ILI9341_CYAN);
   }

   tft.fillRect(140, 170, 25, 15, ILI9341_CYAN);
   tft.fillRect(170, 170, 25, 15, ILI9341_CYAN);
   tft.fillRect(200, 170, 25, 15, ILI9341_CYAN);

   if (EnigmaConfigData.machine_type != MACHINE_TYPE_M3)
   {
      tft.setCursor(117, 174);
      tft.print(EnigmaConfigData.ROTORPOS[0] / 10);
      tft.print(EnigmaConfigData.ROTORPOS[0] % 10);
   }

   tft.setCursor(147, 174);
   tft.print(EnigmaConfigData.ROTORPOS[1] / 10);
   tft.print(EnigmaConfigData.ROTORPOS[1] % 10);

   tft.setCursor(177, 174);
   tft.print(EnigmaConfigData.ROTORPOS[2] / 10);
   tft.print(EnigmaConfigData.ROTORPOS[2] % 10);

   tft.setCursor(207, 174);
   tft.print(EnigmaConfigData.ROTORPOS[3] / 10);
   tft.print(EnigmaConfigData.ROTORPOS[3] % 10);
}  // draw_config_rings()


// draw the rotor numbers in config
void draw_config_rotor_numbers(void)
{
   byte rotor;
   int base_x = 120;

   for (int i = 0; i < 4; i++, base_x += 30)
   {
      rotor = EnigmaConfigData.WHEELTYPE[4 - i];

      switch (rotor)
      {
         case USE_ROTOR_1:
         {
            tft.setCursor(base_x, 154);
            tft.print("I");
         }
         break;

         case USE_ROTOR_D1:
         {
            tft.setCursor(base_x - 3, 154);
            tft.print("ID");
         }
         break;

         case USE_ROTOR_R1:
         {
            tft.setCursor(base_x - 3, 154);
            tft.print("IR");
         }
         break;

         case USE_ROTOR_S1:
         {
            tft.setCursor(base_x - 3, 154);
            tft.print("IS");
         }
         break;

         case USE_ROTOR_2:
         {
            tft.setCursor(base_x - 3, 154);
            tft.print("II");
         }
         break;

         case USE_ROTOR_D2:
         {
            tft.setCursor(base_x - 6, 154);
            tft.print("IID");
         }
         break;

         case USE_ROTOR_R2:
         {
            tft.setCursor(base_x - 6, 154);
            tft.print("IIR");
         }
         break;

         case USE_ROTOR_S2:
         {
            tft.setCursor(base_x - 6, 154);
            tft.print("IIS");
         }
         break;

         case USE_ROTOR_3:
         {
            tft.setCursor(base_x - 6, 154);
            tft.print("III");
         }
         break;

         case USE_ROTOR_D3:
         {
            tft.setCursor(base_x - 9, 154);
            tft.print("IIID");
         }
         break;

         case USE_ROTOR_R3:
         {
            tft.setCursor(base_x - 9, 154);
            tft.print("IIIR");
         }
         break;

         case USE_ROTOR_S3:
         {
            tft.setCursor(base_x - 9, 154);
            tft.print("IIIS");
         }
         break;

         case USE_ROTOR_4:
         {
            tft.setCursor(base_x - 3, 154);
            tft.print("IV");
         }
         break;

         case USE_ROTOR_5:
         {
            tft.setCursor(base_x, 154);
            tft.print("V");
         }
         break;

         case USE_ROTOR_6:
         {
            tft.setCursor(base_x - 3, 154);
            tft.print("VI");
         }
         break;

         case USE_ROTOR_7:
         {
            tft.setCursor(base_x - 6, 154);
            tft.print("VII");
         }
         break;

         case USE_ROTOR_8:
         {
            tft.setCursor(base_x - 9, 154);
            tft.print("VIII");
         }
         break;

         case USE_ROTOR_B:
         {
            if (EnigmaConfigData.machine_type == MACHINE_TYPE_M4)
            {
               tft.setCursor(base_x, 154);
               tft.print("B");
            }
         }
         break;

         case USE_ROTOR_G:
         {
            if (EnigmaConfigData.machine_type == MACHINE_TYPE_M4)
            {
               tft.setCursor(base_x, 154);
               tft.print("G");
            }
         }
         break;
      }
   }
}  // draw_config_rotor_numbers()


// draw pre-defined setups in config
void draw_config_setup(void)
{
   tft.fillRect(80, 40, 102, 30, ILI9341_CYAN);
   tft.setTextSize(1);
   tft.setCursor(83, 51);
   tft.setTextColor(ILI9341_BLACK);

   switch (config_setup)
   {
      case CONFIG_M3_1939:
      {
         tft.print("STANDARD M3 1939");
      }
      break;

      case CONFIG_EXAMPLE_1930:
      {
         tft.print("  EXAMPLE 1930");
      }
      break;

      case CONFIG_ENIGMA_D:
      {
         tft.print("    ENIGMA D");
      }
      break;

      case CONFIG_ROCKET_K_RAILWAY:
      {
         tft.print("ROCKET K RAILWAY");
      }
      break;

      case CONFIG_SWISS_K:
      {
         tft.print("    SWISS K");
      }
      break;

      case CONFIG_TURING_1940:
      {
         tft.print("  TURING 1940");
      }
      break;

      case CONFIG_BARBAROSA_1941:
      {
         tft.print(" BARBAROSA 1941");
      }
      break;

      case CONFIG_M4_1942:
      {
         tft.print("STANDARD M4 1942");
      }
      break;

      case CONFIG_U264_1942:
      {
         tft.print("  U-264 1942");
      }
      break;

      case CONFIG_SCHARNHORST_1943:
      {
         tft.print("SCHARNHORST 1943");
      }
      break;
   }
}  // draw_config_setup()


// draw the tape groups in config
void draw_config_tape_group_size(void)
{
   tft.fillRect(170, 125, 70, 15, ILI9341_CYAN);
   tft.setTextSize(1);
   tft.setCursor(178, 129);
   tft.setTextColor(ILI9341_BLACK);

   if (tape_group_size == 0)
   {
      tft.print("  NONE");
   }
   else
   {
      tft.print("    ");
      tft.print(tape_group_size);
   }
}  // draw_config_tape_group_size()


// draw the wheels in config
void draw_config_wheels(void)
{
   if (EnigmaConfigData.machine_type != MACHINE_TYPE_M4)
   {
      tft.fillRect(110, 150, 25, 15, MY_ILI9341_MEDIUM_GRAY);
   }
   else
   {
      tft.fillRect(110, 150, 25, 15, ILI9341_CYAN);
   }
   tft.fillRect(140, 150, 25, 15, ILI9341_CYAN);
   tft.fillRect(170, 150, 25, 15, ILI9341_CYAN);
   tft.fillRect(200, 150, 25, 15, ILI9341_CYAN);

   tft.setTextSize(1);
   tft.setTextColor(ILI9341_BLACK);

   draw_config_rotor_numbers();
}  // draw_config_wheels()


// draw the entire operational display
void draw_display(void)
{
   switch (enigma_state)
   {
      case SPLASH_STATE:
      {
         erase_background();

         // draw the Enigma logo (2x size)
         const char *logobptr = (const char *)LOGOX2;
         int pixel_x, pixel_y, px, ndx = 0;

         for (byte y = 0; y < 50; y++)
         {
            for (byte x = 0; x < 15; x++)
            {
               pixel_x = 60 + x * 8;
               pixel_y = 5 + y;
               px = pgm_read_byte(logobptr + ndx);

               for (byte i = 0; i < 8; i++)
               {
                  if ((px >> (7 - i)) & 0x01)
                  {
                     tft.drawPixel(pixel_x + i, pixel_y, ILI9341_WHITE);
                  }
               }

               ndx++;
            }
         }

         tft.setTextColor(ILI9341_YELLOW);
         tft.setTextSize(1);

         tft.setCursor(30, 70);
         tft.println(TITLE);

         tft.setTextColor(ILI9341_GREEN);

         tft.setCursor(40, 100);
         tft.print(VERSION0);
         tft.setCursor(20, 120);
         tft.println(VERSION1);

         tft.setTextColor(ILI9341_CYAN);

         tft.setCursor(1, 150);
         tft.println(CREDIT1);

         tft.setTextColor(ILI9341_ORANGE);
         tft.setCursor(5, 295);
         tft.println(TAPSTART);

         show_init_over_serial();
      }
      break;

      case OPERATE_STATE:
      {
         erase_background();

         tft.setTextSize(1);
         tft.setTextColor(ILI9341_GREEN);
         tft.setCursor(167, 5);
         tft.print(VERSION0SHORT);
         tft.setCursor(170, 15);
         tft.print(VERSION1SHORT);

         // draw the rotors
         for (int i = 0; i < 4; i++)
         {
            if (!((EnigmaCurrentData.machine_type == MACHINE_TYPE_M3) && (i == 0)))
            {
               tft.fillRect(BASE_X - 11 + (i * 40),  BASE_Y - 12,  36, 44, MY_ILI9341_LIGHT_GRAY);
               tft.drawRect(BASE_X -  2 + (i * 40),  BASE_Y -  1,  18, 22, ILI9341_BLACK);
               tft.drawRect(BASE_X -  3 + (i * 40),  BASE_Y -  2,  20, 24, ILI9341_BLACK);
               tft.drawRect(BASE_X -  4 + (i * 40),  BASE_Y -  3,  22, 26, MY_ILI9341_LIGHT_GRAY);
               tft.drawRect(BASE_X -  5 + (i * 40),  BASE_Y -  4,  24, 28, MY_ILI9341_LIGHT_GRAY);
               tft.drawRect(BASE_X -  6 + (i * 40),  BASE_Y -  5,  26, 30, MY_ILI9341_DARK_GRAY);
               tft.drawRect(BASE_X -  7 + (i * 40),  BASE_Y -  6,  28, 32, MY_ILI9341_DARK_GRAY);
               tft.drawRect(BASE_X -  8 + (i * 40),  BASE_Y -  7,  30, 34, MY_ILI9341_DARK_GRAY);

               tft.fillCircle(BASE_X + 6 + (i * 40), BASE_Y - 25, 12, MY_ILI9341_LIGHT_GRAY);
               tft.fillCircle(BASE_X + 6 + (i * 40), BASE_Y + 44, 12, MY_ILI9341_LIGHT_GRAY);

               for (int x = 0; x < 14; x++)
               {
                  tft.drawLine(BASE_X -  7 + (i * 40), BASE_Y - 26, BASE_X - 10 + x + (i * 40), BASE_Y - 13, MY_ILI9341_LIGHT_GRAY);
                  tft.drawLine(BASE_X + 19 + (i * 40), BASE_Y - 26, BASE_X + 10 + x + (i * 40), BASE_Y - 13, MY_ILI9341_LIGHT_GRAY);

                  tft.drawLine(BASE_X -  7 + (i * 40), BASE_Y + 45, BASE_X - 10 + x + (i * 40), BASE_Y + 32, MY_ILI9341_LIGHT_GRAY);
                  tft.drawLine(BASE_X + 19 + (i * 40), BASE_Y + 45, BASE_X + 10 + x + (i * 40), BASE_Y + 32, MY_ILI9341_LIGHT_GRAY);
               }

               tft.drawCircle(BASE_X + 6 + (i * 40), BASE_Y - 29, 6, ILI9341_BLACK);
               tft.fillCircle(BASE_X + 6 + (i * 40), BASE_Y - 29, 5, MY_ILI9341_DARK_GRAY);
               tft.fillCircle(BASE_X + 6 + (i * 40), BASE_Y - 29, 3, MY_ILI9341_MEDIUM_GRAY);
               tft.drawCircle(BASE_X + 6 + (i * 40), BASE_Y + 48, 6, ILI9341_BLACK);
               tft.fillCircle(BASE_X + 6 + (i * 40), BASE_Y + 48, 5, MY_ILI9341_DARK_GRAY);
               tft.fillCircle(BASE_X + 6 + (i * 40), BASE_Y + 48, 3, MY_ILI9341_MEDIUM_GRAY);

               tft.drawLine(BASE_X + 3 + (i * 40), BASE_Y - 29, BASE_X + 9 + (i * 40), BASE_Y - 29, ILI9341_BLACK);
               tft.drawLine(BASE_X + 3 + (i * 40), BASE_Y + 48, BASE_X + 9 + (i * 40), BASE_Y + 48, ILI9341_BLACK);

               draw_rotor_letters();
            }
         }

         // draw the Enigma logo (1x size)
         const char *logobptr = (const char *)LOGOX1;
         int pixel_x, pixel_y, px, ndx = 0;

         for (byte y = 0; y < 25; y++)
         {
            for (byte x = 0; x < 8; x++)
            {
               pixel_x = 170 + x * 8;
               pixel_y =  30 + y;
               px = pgm_read_byte(logobptr + ndx);

               for (byte i = 0; i < 8; i++)
               {
                  if ((px >> (7 - i)) & 0x01)
                  {
                     tft.drawPixel(pixel_x + i, pixel_y, ILI9341_WHITE);
                  }
               }

               ndx++;
            }
         }

         tft.drawRect(168, 65, 64, 18, MY_ILI9341_LIGHT_GRAY);
         tft.drawRect(169, 66, 62, 16, MY_ILI9341_LIGHT_GRAY);
         tft.drawRect(170, 67, 60, 14, MY_ILI9341_LIGHT_GRAY);
         tft.fillRect(171, 68, 58, 12, ILI9341_BLACK);

         tft.setTextSize(1);
         tft.setTextColor(ILI9341_GREEN);
         tft.setCursor(174, 70);

         print_machine_type();

         for (int ch = 'A'; ch <= 'Z'; ch++)
         {
            draw_lights(ch, NOT_PRESSED);
            draw_keys(ch, NOT_PRESSED);
         }

         clear_tape();

         // draw the operational plugboard
         char ch = 'A';

         if ((EnigmaConfigData.machine_type == MACHINE_TYPE_M3) || (EnigmaConfigData.machine_type == MACHINE_TYPE_M4))
         {
            tft.setTextSize(1);
            tft.setTextColor(ILI9341_BLACK);

            for (int i = 0; i < 26; i++)
            {
               if (EnigmaCurrentData.STECKER[i] != ch)
               {
                  tft.fillRect((i * 9) + 3, 300, 7, 9, ILI9341_MAGENTA);
                  tft.fillRect((i * 9) + 3, 311, 7, 9, ILI9341_MAGENTA);
               }
               else
               {
                  tft.fillRect((i * 9) + 3, 300, 7, 9, MY_ILI9341_LIGHT_GRAY);
                  tft.fillRect((i * 9) + 3, 311, 7, 9, MY_ILI9341_MEDIUM_GRAY);
               }
               tft.setCursor((i * 9) + 4, 301);
               tft.print(ch);
               tft.setCursor((i * 9) + 4, 312);
               tft.print(EnigmaCurrentData.STECKER[i]);

               ch++;
            }
         }
      }
      break;

      case CONFIG_STATE:
      {
         erase_background();

         tft.setTextSize(1);
         tft.setTextColor(ILI9341_GREEN);
         tft.setCursor(173, 5);
         tft.print(VERSION0SHORT);
         tft.setCursor(176, 15);
         tft.print(VERSION1SHORT);

         tft.setTextSize(2);
         tft.setCursor(88, 7);
         tft.setTextColor(ILI9341_GREEN);
         tft.print("CONFIG");
         tft.drawRect(78, 1, 91, 26, ILI9341_RED);
         tft.drawRect(79, 2, 89, 24, ILI9341_RED);

         tft.setTextSize(2);
         tft.setCursor(10, 85);
         tft.setTextColor(ILI9341_YELLOW);
         tft.print("Machine Type");

         tft.setTextSize(2);
         tft.setCursor(10, 105);
         tft.setTextColor(ILI9341_YELLOW);
         tft.print("Reflector");

         tft.setTextSize(2);
         tft.setCursor(10, 125);
         tft.setTextColor(ILI9341_YELLOW);
         tft.print("Tape Groups");

         tft.setTextSize(2);
         tft.setCursor(10, 150);
         tft.setTextColor(ILI9341_YELLOW);
         tft.print("Wheels");

         tft.setTextSize(2);
         tft.setCursor(10, 170);
         tft.setTextColor(ILI9341_YELLOW);
         tft.print("Rings");

         tft.setTextSize(2);
         tft.setCursor(10, 39);
         tft.setTextColor(ILI9341_YELLOW);
         tft.print("Quick");
         tft.setCursor(10, 57);
         tft.print("Setup");

         draw_config_display();

         tft.setTextSize(1);
         tft.setTextColor(ILI9341_BLACK);

         tft.fillRect(190, 40, 50, 30, MY_ILI9341_ORANGE);
         tft.drawRect(192, 42, 46, 26, ILI9341_BLACK);
         tft.setCursor(203, 51);
         tft.print("LOAD");

         tft.fillRect(30, 290, 60, 30, ILI9341_RED);
         tft.drawRect(32, 292, 56, 26, ILI9341_BLACK);
         tft.setCursor(40, 301);
         tft.print("DISCARD");

         tft.fillRect(150, 290, 60, 30, ILI9341_GREEN);
         tft.drawRect(152, 292, 56, 26, ILI9341_BLACK);
         tft.setCursor(157, 301);
         tft.print("ACTIVATE");
      }
      break;
   }
}  // draw_display()


// draw the keyboard
void draw_keys(char ch, boolean is_pressed)
{
   int key_locs_y[26] =
   {
      KEY_ROW_2, // A
      KEY_ROW_3, // B
      KEY_ROW_3, // C
      KEY_ROW_2, // D
      KEY_ROW_1, // E
      KEY_ROW_2, // F
      KEY_ROW_2, // G
      KEY_ROW_2, // H
      KEY_ROW_1, // I
      KEY_ROW_2, // J
      KEY_ROW_2, // K
      KEY_ROW_3, // L
      KEY_ROW_3, // M
      KEY_ROW_3, // N
      KEY_ROW_1, // O
      KEY_ROW_3, // P
      KEY_ROW_1, // Q
      KEY_ROW_1, // R
      KEY_ROW_2, // S
      KEY_ROW_1, // T
      KEY_ROW_1, // U
      KEY_ROW_3, // V
      KEY_ROW_1, // W
      KEY_ROW_3, // X
      KEY_ROW_3, // Y
      KEY_ROW_1  // Z
   };

   int index = ch - 'A';

   if (is_pressed)
   {
      tft.fillCircle(locs_x[index], key_locs_y[index], 9, ILI9341_RED);
      tft.drawChar(locs_x[index] - 5, key_locs_y[index] - 6, ch, ILI9341_WHITE, ILI9341_RED, 2);
   }
   else
   {
      tft.fillCircle(locs_x[index], key_locs_y[index], 9, ILI9341_BLACK);
      tft.drawChar(locs_x[index] - 5, key_locs_y[index] - 6, ch, ILI9341_WHITE, ILI9341_BLACK, 2);
   }
   tft.drawCircle(locs_x[index], key_locs_y[index], 10, MY_ILI9341_LIGHT_GRAY);
   tft.drawCircle(locs_x[index], key_locs_y[index], 11, MY_ILI9341_LIGHT_GRAY);
}  // draw_keys()


// draw the indicator lights
void draw_lights(char ch, boolean is_pressed)
{
   int light_locs_y[26] =
   {
      LIGHT_ROW_2, // A
      LIGHT_ROW_3, // B
      LIGHT_ROW_3, // C
      LIGHT_ROW_2, // D
      LIGHT_ROW_1, // E
      LIGHT_ROW_2, // F
      LIGHT_ROW_2, // G
      LIGHT_ROW_2, // H
      LIGHT_ROW_1, // I
      LIGHT_ROW_2, // J
      LIGHT_ROW_2, // K
      LIGHT_ROW_3, // L
      LIGHT_ROW_3, // M
      LIGHT_ROW_3, // N
      LIGHT_ROW_1, // O
      LIGHT_ROW_3, // P
      LIGHT_ROW_1, // Q
      LIGHT_ROW_1, // R
      LIGHT_ROW_2, // S
      LIGHT_ROW_1, // T
      LIGHT_ROW_1, // U
      LIGHT_ROW_3, // V
      LIGHT_ROW_1, // W
      LIGHT_ROW_3, // X
      LIGHT_ROW_3, // Y
      LIGHT_ROW_1  // Z
   };

   int index = ch - 'A';

   if (is_pressed)
   {
      tft.fillCircle(locs_x[index], light_locs_y[index], 9, MY_ILI9341_MEDIUM_GRAY);
      tft.drawChar(locs_x[index] - 5, light_locs_y[index] - 6, ch, ILI9341_YELLOW, MY_ILI9341_MEDIUM_GRAY, 2);
   }
   else
   {
      tft.fillCircle(locs_x[index], light_locs_y[index], 9, ILI9341_BLACK);
      tft.drawChar(locs_x[index] - 5, light_locs_y[index] - 6, ch, MY_ILI9341_SHADOW_GRAY, ILI9341_BLACK, 2);
   }
   tft.drawCircle(locs_x[index], light_locs_y[index], 10, MY_ILI9341_MEDIUM_GRAY);
   tft.drawCircle(locs_x[index], light_locs_y[index], 11, MY_ILI9341_MEDIUM_GRAY);
}  // draw_lights()


// draw the letters in the rotors
void draw_rotor_letters(void)
{
   if (EnigmaCurrentData.machine_type != MACHINE_TYPE_M3)
   {
      tft.fillRect(BASE_X,  BASE_Y,  14, 20, MY_ILI9341_BRASS);
      tft.drawChar(BASE_X + 2,  BASE_Y + 3, EnigmaCurrentData.WHEELPOS[0], ILI9341_BLACK, MY_ILI9341_BRASS, 2);
   }

   for (int i = 1; i < 4; i++)
   {
      tft.fillRect(BASE_X + (i * 40),  BASE_Y,  14, 20, MY_ILI9341_BRASS);
      tft.drawChar(BASE_X + 2 + (i * 40),  BASE_Y + 3, EnigmaCurrentData.WHEELPOS[i], ILI9341_BLACK, MY_ILI9341_BRASS, 2);
   }
}  // draw_rotor_letters


// draw the tape
void draw_tape(void)
{
   int space_counter = 0;

   while (tape_data[space_counter] == ' ')
   {
      space_counter++;
   }

   tft.fillRect(0, 100, 240, 15, MY_ILI9341_SHADOW_GRAY);

   if (space_counter == 0)
   {
      tft.fillRect(0, 100, 240, 15, MY_ILI9341_PAPER);
   }
   else
   {
      tft.fillRect((space_counter - 1) * 6, 100, 240, 15, MY_ILI9341_PAPER);
      tft.drawPixel((space_counter - 1) * 6 - 1, 102, MY_ILI9341_PAPER);
      tft.drawPixel((space_counter - 1) * 6 - 2, 103, MY_ILI9341_PAPER);
      tft.drawPixel((space_counter - 1) * 6 - 1, 105, MY_ILI9341_PAPER);
      tft.drawPixel((space_counter - 1) * 6 - 2, 106, MY_ILI9341_PAPER);
      tft.drawPixel((space_counter - 1) * 6 - 1, 108, MY_ILI9341_PAPER);
      tft.drawPixel((space_counter - 1) * 6 - 2, 109, MY_ILI9341_PAPER);
      tft.drawPixel((space_counter - 1) * 6 - 1, 111, MY_ILI9341_PAPER);
      tft.drawPixel((space_counter - 1) * 6 - 2, 112, MY_ILI9341_PAPER);
   }

   tft.setCursor(0, 104);
   tft.setTextSize(1);
   tft.setTextColor(MY_ILI9341_MEDIUM_GRAY);

   for (int i = 0; i < TAPE_LENGTH; i++)
   {
      tft.print(tape_data[i]);
   }
}  // draw_tape()


// calculate the letter translation
char encode_key(char key)
{
   const char *charptr = (const char *)WHEELSF;
   char k, k1 = 0;
   unsigned int wheeltype;

   serial_monitor(0);
   serial_monitor(key);

   k = EffSTECKER[key - 'A'];

   serial_monitor(k);

   for (byte i = 0; i < 6; i++)
   {
      if (EnigmaCurrentData.WHEELTYPE[i] != 0)
      {
         if ((i > 0) && (i < 5))
         {
            byte p = EnigmaCurrentData.WHEELPOS[4 - i] - (EnigmaCurrentData.ROTORPOS[4 - i] - 1);

            if (p < 'A')
            {
               p += 26;
            }

            k += (p - 'A');
         }
         else
         {
            // use WHEELPOS[0] & ROTORPOS[0] for UKW on Enigma D, Rocket K, & Swiss K
            if ((i == 5) &&
                ((EnigmaCurrentData.machine_type == MACHINE_TYPE_ENIGMA_D) ||
                 (EnigmaCurrentData.machine_type == MACHINE_TYPE_ROCKET_K) ||
                 (EnigmaCurrentData.machine_type == MACHINE_TYPE_SWISS_K)))
            {
               byte p = EnigmaCurrentData.WHEELPOS[0] - (EnigmaCurrentData.ROTORPOS[0] - 1);

               if (p < 'A')
               {
                  p += 26;
               }

               k += (p - 'A');
            }
         }


         if (k > 'Z')
         {
            k -= ('Z' + 1);
         }
         else
         {
            k -= 'A';
         }

         wheeltype = ((EnigmaCurrentData.WHEELTYPE[i] - 1) * 28) + k + 2;

         k = pgm_read_byte(charptr + wheeltype);

         if ((i > 0) && (i < 5))
         {
            byte p = EnigmaCurrentData.WHEELPOS[4 - i] - (EnigmaCurrentData.ROTORPOS[4 - i] - 1);

            if (p < 'A')
            {
               p += 26;
            }

            k -= (p - 'A');
         }
         else
         {
            // use WHEELPOS[0] & ROTORPOS[0] for UKW on Enigma D, Rocket K, & Swiss K
            if ((i == 5) &&
                ((EnigmaCurrentData.machine_type == MACHINE_TYPE_ENIGMA_D) ||
                 (EnigmaCurrentData.machine_type == MACHINE_TYPE_ROCKET_K) ||
                 (EnigmaCurrentData.machine_type == MACHINE_TYPE_SWISS_K)))
            {
               byte p = EnigmaCurrentData.WHEELPOS[0] - (EnigmaCurrentData.ROTORPOS[0] - 1);

               if (p < 'A')
               {
                  p += 26;
               }

               k -= (p - 'A');
            }
         }

         if (k < 'A')
         {
            k += 26;
         }

         serial_monitor(k);
      }
   }

   // after reflector

   for (byte i = 0; i < 5; i++)
   {
      if (EnigmaCurrentData.WHEELTYPE[4 - i] != 0)
      {
         if (i < 4)
         {
            byte p = EnigmaCurrentData.WHEELPOS[i] - (EnigmaCurrentData.ROTORPOS[i] - 1);
            if (p < 'A')
            {
               p += 26;
            }

            k += (p - 'A');
         }

         if (k > 'Z')
         {
            k -= 26;
         }

         wheeltype = (EnigmaCurrentData.WHEELTYPE[4 - i] - 1) * 28;

         for (byte j = 0; j < 26; j++)
         {
            if ((pgm_read_byte(charptr + wheeltype + j + 2)) == k)
            {
               k1 = 'A' + j;
            }
         }

         k = k1;

         if (i < 4)
         {
            byte p = EnigmaCurrentData.WHEELPOS[i] - (EnigmaCurrentData.ROTORPOS[i] - 1);

            if (p < 'A')
            {
               p += 26;
            }

            k -= (p - 'A');
         }

         if (k < 'A')
         {
            k += 26;
         }

         serial_monitor(k);
      }
   }

   for (byte j = 0; j < 26; j++)
   {
      if (EffSTECKER[j] == k)
      {
         k1 = 'A' + j;
      }
   }

   k = k1;

   serial_monitor(k);

   return k;
}  // encode_key()


// erase the background to crinkle black/gray
void erase_background(void)
{
   for (int y = 0; y < 320; y++)
   {
      tft.drawFastHLine(0, y, 240, ILI9341_BLACK);

      for (int x = random(7); x < 240; x+=random(7)+1)
      {
         int r = random(10);

         if (r < 2)
         {
            tft.drawPixel(x, y, MY_ILI9341_SHADOW_GRAY);
         }
         else
         {
            if (r < 4)
            {
               tft.drawPixel(x, y, MY_ILI9341_DARK_GRAY);
            }
            else
            {
               if (r < 7)
               {
                  tft.drawPixel(x, y, MY_ILI9341_MEDIUM_GRAY);
               }
            }
         }
      }
   }
}  // erase_background()


// increment a wheel position
void increment_wheel(int wheel)
{
   EnigmaCurrentData.WHEELPOS[wheel]++;

   if ((EnigmaCurrentData.WHEELPOS[wheel] > 'Z') || (EnigmaCurrentData.WHEELPOS[wheel] < 'A'))
   {
      EnigmaCurrentData.WHEELPOS[wheel] = 'A';
   }

   draw_rotor_letters();
}  // increment_wheel()


// see if it is time to rotate a wheel
bool is_carry(byte wheelType, byte wheelPos)
{
   const char *charptr = (const char *)WHEELSF;
   unsigned int wheeltype = (wheelType - 1) * 28;

   byte k1 = pgm_read_byte(charptr + wheeltype);
   byte k2 = pgm_read_byte(charptr + wheeltype + 1);

   if ((wheelPos == k1) || (wheelPos == k2))
   {
      return(true);
   }
   else
   {
      return(false);
   }
}  // is_carry()


// loop forever
void loop()
{
   process_serial();

   process_buttons();
}  // loop()


// move the wheels with each keypress, but before determining the resulting letter translation
void move_wheels(void)
{
   byte i = 4;
   bool carry = true;

   do
   {
      i--;

      if (carry)
      {
         EnigmaCurrentData.WHEELPOS[i]++;

         if (i > 1)
         {
            carry = is_carry(EnigmaCurrentData.WHEELTYPE[4 - i], EnigmaCurrentData.WHEELPOS[i]);
         }
         else
         {
            carry = false;
         }
      }
      else
      {
         // double stepping on second wheel
         if (i == 2)
         {
            byte w2 = EnigmaCurrentData.WHEELPOS[2] + 1;

            if (w2 > 'Z')
            {
               w2 = 'A';
            }

            if (is_carry(EnigmaCurrentData.WHEELTYPE[2], w2))
            {
               EnigmaCurrentData.WHEELPOS[2]++;

               carry = true;
            }
         }
      }

      if (EnigmaCurrentData.WHEELPOS[i] > 'Z')
      {
         EnigmaCurrentData.WHEELPOS[i] = 'A';

         carry = is_carry(EnigmaCurrentData.WHEELTYPE[4 - i], EnigmaCurrentData.WHEELPOS[i]) || carry;

         if (i == 1)
         {
            carry = false;
         }
      }
   } while (i > 0);
}  // move_wheels()


// print machine type
void print_machine_type()
{
   switch (EnigmaConfigData.machine_type)
   {
      case MACHINE_TYPE_M3:
      {
         tft.print("M3 (1939)");
      }
      break;

      case MACHINE_TYPE_M4:
      {
         tft.print("M4 (1942)");
      }
      break;

      case MACHINE_TYPE_ENIGMA_D:
      {
         tft.print("Enigma D");
      }
      break;

      case MACHINE_TYPE_ROCKET_K:
      {
         tft.print("Rocket K");
      }
      break;

      case MACHINE_TYPE_SWISS_K:
      {
         tft.print(" Swiss K");
      }
   }
}  // print_machine_type


// detect button presses
void process_buttons()
{
   boolean key_pressed = false;
   boolean button_pressed = false;
   boolean wait_for_release = false;

   char encoded_key = 0x00;
   char pressed_key = 0x00;

   // See if there's any touch data for us
   if (ts.bufferEmpty())
   {
      return;
   }

   // a point object holds x y and z coordinates.
   TS_Point p = ts.getPoint();

   // Scale from ~0->4000 to tft.width using the calibration #'s
   p.x = map(p.x, TS_MINX, TS_MAXX, 0, tft.width());
   p.y = map(p.y, TS_MINY, TS_MAXY, 0, tft.height());

#ifdef DEBUG_TS
   // show where the screen was touched (for debugging purposes)
   tft.fillRect(0, 0, 240, 10, ILI9341_BLACK);
   tft.setTextColor(ILI9341_WHITE);
   tft.setTextSize(1);
   tft.setCursor(80, 0);
   tft.println("X: ");
   tft.setCursor(95, 0);
   tft.println(p.x);
   tft.setCursor(130, 0);
   tft.println("Y: ");
   tft.setCursor(145, 0);
   tft.println(p.y);
#endif

   switch (enigma_state)
   {
      case SPLASH_STATE:
      {
         if ((p.x >= 0) && (p.x <= 240) && (p.y >= 0) && (p.y <= 320))
         {
            button_pressed = true;
            wait_for_release = true;
         }
      }
      break;

      case OPERATE_STATE:
      {
         // click on the enigma logo or the machine type to enter config
         if ((p.x >= 160) && (p.x <= 240) && (p.y >= 25) && (p.y <= 80))
         {
            button_pressed = true;
            wait_for_release = true;
         }

         // clear the tape
         if ((p.x >= 0) && (p.x <= 240) && (p.y >= 100) && (p.y <= 115))
         {
            button_pressed = true;
            wait_for_release = true;
         }

         // click below the rotors - increment the letter
         if ((p.y >= 70) && (p.y < 95))
         {
            // click below rotor 1
            if ((p.x >= 0) && (p.x < 40))
            {
               if (EnigmaCurrentData.machine_type != MACHINE_TYPE_M3)
               {
                  button_pressed = true;
                  wait_for_release = true;
               }
            }

            // click below rotor 2
            if ((p.x >= 40) && (p.x < 80))
            {
               button_pressed = true;
               wait_for_release = true;
            }

            // click below rotor 3
            if ((p.x >= 80) && (p.x < 120))
            {
               button_pressed = true;
               wait_for_release = true;
            }

            // click below rotor 4
            if ((p.x >= 120) && (p.x < 160))
            {
               button_pressed = true;
               wait_for_release = true;
            }
         }

         // click above the rotors - decrement the letter
         if ((p.y >= 0) && (p.y < 25))
         {
            // click above rotor 1
            if ((p.x >= 0) && (p.x < 40))
            {
               if (EnigmaCurrentData.machine_type != MACHINE_TYPE_M3)
               {
                  button_pressed = true;
                  wait_for_release = true;
               }
            }

            // click above rotor 2
            if ((p.x >= 40) && (p.x < 80))
            {
               button_pressed = true;
               wait_for_release = true;
            }

            // click above rotor 3
            if ((p.x >= 80) && (p.x < 120))
            {
               button_pressed = true;
               wait_for_release = true;
            }

            // click above rotor 4
            if ((p.x >= 120) && (p.x < 160))
            {
               button_pressed = true;
               wait_for_release = true;
            }
         }

         // first row key is pressed
         if ((p.y >= 213) && (p.y <= 237))
         {
            // Q is pressed
            if ((p.x >= 7) && (p.x <= 31))
            {
               pressed_key = 'Q';

               encoded_key = process_key_inputs(pressed_key);

               key_pressed = true;
               button_pressed = true;
               wait_for_release = true;
            }

            // W is pressed
            if ((p.x >= 32) && (p.x <= 56))
            {
               pressed_key = 'W';

               encoded_key = process_key_inputs(pressed_key);

               key_pressed = true;
               button_pressed = true;
               wait_for_release = true;
            }

            // E is pressed
            if ((p.x >= 57) && (p.x <= 81))
            {
               pressed_key = 'E';

               encoded_key = process_key_inputs(pressed_key);

               key_pressed = true;
               button_pressed = true;
               wait_for_release = true;
            }

            // R is pressed
            if ((p.x >= 82) && (p.x <= 106))
            {
               pressed_key = 'R';

               encoded_key = process_key_inputs(pressed_key);

               key_pressed = true;
               button_pressed = true;
               wait_for_release = true;
            }

            // T is pressed
            if ((p.x >= 107) && (p.x <= 131))
            {
               pressed_key = 'T';

               encoded_key = process_key_inputs(pressed_key);

               key_pressed = true;
               button_pressed = true;
               wait_for_release = true;
            }

            // Z is pressed
            if ((p.x >= 132) && (p.x <= 156))
            {
               pressed_key = 'Z';

               encoded_key = process_key_inputs(pressed_key);

               key_pressed = true;
               button_pressed = true;
               wait_for_release = true;
            }

            // U is pressed
            if ((p.x >= 157) && (p.x <= 181))
            {
               pressed_key = 'U';

               encoded_key = process_key_inputs(pressed_key);

               key_pressed = true;
               button_pressed = true;
               wait_for_release = true;
            }

            // I is pressed
            if ((p.x >= 182) && (p.x <= 206))
            {
               pressed_key = 'I';

               encoded_key = process_key_inputs(pressed_key);

               key_pressed = true;
               button_pressed = true;
               wait_for_release = true;
            }

            // O is pressed
            if ((p.x >= 207) && (p.x <= 232))
            {
               pressed_key = 'O';

               encoded_key = process_key_inputs(pressed_key);

               key_pressed = true;
               button_pressed = true;
               wait_for_release = true;
            }
         }

         // second row key is pressed
         if ((p.y >= 243) && (p.y <= 267))
         {
            // A is pressed
            if ((p.x >= 19) && (p.x <= 43))
            {
               pressed_key = 'A';

               encoded_key = process_key_inputs(pressed_key);

               key_pressed = true;
               button_pressed = true;
               wait_for_release = true;
            }

            // S is pressed
            if ((p.x >= 44) && (p.x <= 68))
            {
               pressed_key = 'S';

               encoded_key = process_key_inputs(pressed_key);

               key_pressed = true;
               button_pressed = true;
               wait_for_release = true;
            }

            // D is pressed
            if ((p.x >= 69) && (p.x <= 93))
            {
               pressed_key = 'D';

               encoded_key = process_key_inputs(pressed_key);

               key_pressed = true;
               button_pressed = true;
               wait_for_release = true;
            }

            // F is pressed
            if ((p.x >= 94) && (p.x <= 118))
            {
               pressed_key = 'F';

               encoded_key = process_key_inputs(pressed_key);

               key_pressed = true;
               button_pressed = true;
               wait_for_release = true;
            }

            // G is pressed
            if ((p.x >= 119) && (p.x <= 143))
            {
               pressed_key = 'G';

               encoded_key = process_key_inputs(pressed_key);

               key_pressed = true;
               button_pressed = true;
               wait_for_release = true;
            }

            // H is pressed
            if ((p.x >= 144) && (p.x <= 168))
            {
               pressed_key = 'H';

               encoded_key = process_key_inputs(pressed_key);

               key_pressed = true;
               button_pressed = true;
               wait_for_release = true;
            }

            // J is pressed
            if ((p.x >= 169) && (p.x <= 193))
            {
               pressed_key = 'J';

               encoded_key = process_key_inputs(pressed_key);

               key_pressed = true;
               button_pressed = true;
               wait_for_release = true;
            }

            // K is pressed
            if ((p.x >= 194) && (p.x <= 218))
            {
               pressed_key = 'K';

               encoded_key = process_key_inputs(pressed_key);

               key_pressed = true;
               button_pressed = true;
               wait_for_release = true;
            }
         }

         // third row key is pressed
         if ((p.y >= 269) && (p.y <= 293))
         {
            // P is pressed
            if ((p.x >= 7) && (p.x <= 31))
            {
               pressed_key = 'P';

               encoded_key = process_key_inputs(pressed_key);

               key_pressed = true;
               button_pressed = true;
               wait_for_release = true;
            }

            // Y is pressed
            if ((p.x >= 32) && (p.x <= 56))
            {
               pressed_key = 'Y';

               encoded_key = process_key_inputs(pressed_key);

               key_pressed = true;
               button_pressed = true;
               wait_for_release = true;
            }

            // X is pressed
            if ((p.x >= 57) && (p.x <= 81))
            {
               pressed_key = 'X';

               encoded_key = process_key_inputs(pressed_key);

               key_pressed = true;
               button_pressed = true;
               wait_for_release = true;
            }

            // C is pressed
            if ((p.x >= 82) && (p.x <= 106))
            {
               pressed_key = 'C';

               encoded_key = process_key_inputs(pressed_key);

               key_pressed = true;
               button_pressed = true;
               wait_for_release = true;
            }

            // V is pressed
            if ((p.x >= 107) && (p.x <= 131))
            {
               pressed_key = 'V';

               encoded_key = process_key_inputs(pressed_key);

               key_pressed = true;
               button_pressed = true;
               wait_for_release = true;
            }

            // B is pressed
            if ((p.x >= 132) && (p.x <= 156))
            {
               pressed_key = 'B';

               encoded_key = process_key_inputs(pressed_key);

               key_pressed = true;
               button_pressed = true;
               wait_for_release = true;
            }

            // N is pressed
            if ((p.x >= 157) && (p.x <= 181))
            {
               pressed_key = 'N';

               encoded_key = process_key_inputs(pressed_key);

               key_pressed = true;
               button_pressed = true;
               wait_for_release = true;
            }

            // M is pressed
            if ((p.x >= 182) && (p.x <= 206))
            {
               pressed_key = 'M';

               encoded_key = process_key_inputs(pressed_key);

               key_pressed = true;
               button_pressed = true;
               wait_for_release = true;
            }

            // L is pressed
            if ((p.x >= 207) && (p.x <= 232))
            {
               pressed_key = 'L';

               encoded_key = process_key_inputs(pressed_key);

               key_pressed = true;
               button_pressed = true;
               wait_for_release = true;
            }
         }
      }
      break;

      case CONFIG_STATE:
      {
         // detect LOAD button
         if ((p.x >= 190) && (p.x <= 240) && (p.y >= 45) && (p.y <= 75))
         {
            button_pressed = true;
            wait_for_release = true;
         }

         // detect DISCARD button
         if ((p.x >= 30) && (p.x <= 90) && (p.y >= 290) && (p.y < 320))
         {
            button_pressed = true;
            wait_for_release = true;
         }

         // detect ACTIVATE button
         if ((p.x >= 150) && (p.x <= 210) && (p.y >= 290) && (p.y < 320))
         {
            button_pressed = true;
            wait_for_release = true;
         }

         // detect machine type button
         if ((p.x >= 0) && (p.x <= 240) && (p.y >= 75) && (p.y < 95))
         {
            button_pressed = true;
            wait_for_release = true;
         }

         // detect reflector type button
         if ((p.x >= 0) && (p.x <= 240) && (p.y >= 95) && (p.y < 115))
         {
            button_pressed = true;
            wait_for_release = true;
         }

         // detect tape group size button
         if ((p.x >= 0) && (p.x <= 240) && (p.y >= 115) && (p.y < 135))
         {
            button_pressed = true;
            wait_for_release = true;
         }

         // detect wheel4 button
         if ((p.x >= 110) && (p.x <= 135) && (p.y >= 150) && (p.y < 160))
         {
            if (EnigmaConfigData.machine_type == MACHINE_TYPE_M4)
            {
               button_pressed = true;
               wait_for_release = true;
            }
         }

         // detect wheel3 button
         if ((p.x >= 140) && (p.x <= 165) && (p.y >= 150) && (p.y < 160))
         {
            button_pressed = true;
            wait_for_release = true;
         }

         // detect wheel2 button
         if ((p.x >= 170) && (p.x <= 195) && (p.y >= 150) && (p.y < 160))
         {
            button_pressed = true;
            wait_for_release = true;
         }

         // detect wheel1 button
         if ((p.x >= 200) && (p.x <= 225) && (p.y >= 150) && (p.y < 160))
         {
            button_pressed = true;
            wait_for_release = true;
         }

         // detect ring4 button
         if ((p.x >= 110) && (p.x <= 135) && (p.y >= 170) && (p.y < 180))
         {
            if (EnigmaConfigData.machine_type != MACHINE_TYPE_M3)
            {
               button_pressed = true;
               wait_for_release = true;
            }
         }

         // detect ring3 button
         if ((p.x >= 140) && (p.x <= 165) && (p.y >= 170) && (p.y < 180))
         {
            button_pressed = true;
            wait_for_release = true;
         }

         // detect ring2 button
         if ((p.x >= 170) && (p.x <= 195) && (p.y >= 170) && (p.y < 180))
         {
            button_pressed = true;
            wait_for_release = true;
         }

         // detect ring1 button
         if ((p.x >= 200) && (p.x <= 225) && (p.y >= 170) && (p.y < 180))
         {
            button_pressed = true;
            wait_for_release = true;
         }

         // detect setup type button
         if ((p.x >= 0) && (p.x <= 180) && (p.y >= 40) && (p.y <= 70))
         {
            button_pressed = true;
            wait_for_release = true;
         }

         // detect rows of plugs
         if ((((p.y >= 195) && (p.y <= 235)) || ((p.y >= 240) && (p.y < 280))) && ((EnigmaConfigData.machine_type == MACHINE_TYPE_M3) || (EnigmaConfigData.machine_type == MACHINE_TYPE_M4)))
         {
            if ((p.x >= 5) && (p.x <= 19))
            {
               button_pressed = true;
               wait_for_release = true;
            }

            if ((p.x >= 23) && (p.x <= 37))
            {
               button_pressed = true;
               wait_for_release = true;
            }

            if ((p.x >= 41) && (p.x <= 55))
            {
               button_pressed = true;
               wait_for_release = true;
            }

            if ((p.x >= 59) && (p.x <= 73))
            {
               button_pressed = true;
               wait_for_release = true;
            }

            if ((p.x >= 77) && (p.x <= 91))
            {
               button_pressed = true;
               wait_for_release = true;
            }

            if ((p.x >= 95) && (p.x <= 109))
            {
               button_pressed = true;
               wait_for_release = true;
            }

            if ((p.x >= 113) && (p.x <= 127))
            {
               button_pressed = true;
               wait_for_release = true;
            }

            if ((p.x >= 131) && (p.x <= 145))
            {
               button_pressed = true;
               wait_for_release = true;
            }

            if ((p.x >= 149) && (p.x <= 163))
            {
               button_pressed = true;
               wait_for_release = true;
            }

            if ((p.x >= 167) && (p.x <= 181))
            {
               button_pressed = true;
               wait_for_release = true;
            }

            if ((p.x >= 185) && (p.x <= 199))
            {
               button_pressed = true;
               wait_for_release = true;
            }

            if ((p.x >= 203) && (p.x <= 217))
            {
               button_pressed = true;
               wait_for_release = true;
            }

            if ((p.x >= 221) && (p.x <= 235))
            {
               button_pressed = true;
               wait_for_release = true;
            }
         }
      }
      break;
   }

   if (button_pressed)
   {
      boolean debounce = true;

      while ((wait_for_release) && (debounce))
      {
         while (ts.touched())
         {
            delay(50);
         }

         // if currently not being touched, then we're done
         if (! ts.touched())
         {
            debounce = false;
         }
      }

      if (key_pressed)
      {
         draw_keys(pressed_key, NOT_PRESSED);
         draw_lights(encoded_key, NOT_PRESSED);
      } else {
         process_button_inputs(p);
      }

      while (! ts.bufferEmpty())
      {
         TS_Point p_discard = ts.getPoint();

         // this is here to keep the compiler from complaining that p_discard is set but not used
         if (p_discard.x == 0)
         {
            p_discard.x = 0;
         }
      }
   }
} // process_buttons()


// act on button presses
void process_button_inputs(TS_Point p)
{
   switch (enigma_state)
   {
      case SPLASH_STATE:
      {
         enigma_state = OPERATE_STATE;
   
         // initialize the machine as an M3 (1939) machine by default
         EnigmaCurrentData.machine_type = MACHINE_TYPE_M3;
   
         // Rotors (right-to-left)
         EnigmaCurrentData.WHEELTYPE[0] = USE_ROTOR_ETW;    // ENTRY CONTACTS
         EnigmaCurrentData.WHEELTYPE[1] = USE_ROTOR_1;      // RIGHT ROTOR  (NOTE THE REVERSED ORDER VS THE OTHER ARRAYS !!)
         EnigmaCurrentData.WHEELTYPE[2] = USE_ROTOR_2;      // MIDDLE ROTOR (NOTE THE REVERSED ORDER VS THE OTHER ARRAYS !!)
         EnigmaCurrentData.WHEELTYPE[3] = USE_ROTOR_3;      // LEFT ROTOR   (NOTE THE REVERSED ORDER VS THE OTHER ARRAYS !!)
         EnigmaCurrentData.WHEELTYPE[4] = USE_ROTOR_NONE;   // ADDITIONAL WHEEL (M4 only)
         EnigmaCurrentData.WHEELTYPE[5] = USE_ROTOR_UKWB;   // REFLECTOR
   
         // Initial Rotor Positions
         EnigmaCurrentData.WHEELPOS[0] = 'A';    // LEFTMOST LETTER, ONLY ON M4
         EnigmaCurrentData.WHEELPOS[1] = 'A';    // LEFTMOST LETTER ON M3
         EnigmaCurrentData.WHEELPOS[2] = 'A';    // MIDDLE LETTER
         EnigmaCurrentData.WHEELPOS[3] = 'A';    // RIGHTMOST LETTER
   
         // Ring Settings
         EnigmaCurrentData.ROTORPOS[0] =  1;      // LEFTMOST ROTOR SETTING, ONLY ON M4
         EnigmaCurrentData.ROTORPOS[1] =  1;      // LEFTMOST ROTOR SETTING ON M3
         EnigmaCurrentData.ROTORPOS[2] =  1;      // MIDDLE ROTOR SETTING
         EnigmaCurrentData.ROTORPOS[3] =  1;      // RIGHTMOST ROTOR SETTING
   
         // Initialize stecker with no plugs
         remove_all_plugs();
   
         calculate_stecker();
   
         show_rotors_over_serial();
   
         draw_display();         
      }
      break;

      case OPERATE_STATE:
      {
         // click on the enigma logo or the machine type to enter config
         if ((p.x >= 160) && (p.x <= 240) && (p.y >= 25) && (p.y <= 80))
         {
            enigma_state = CONFIG_STATE;

            EnigmaConfigData.machine_type = EnigmaCurrentData.machine_type;

            for (int i = 0; i < 27; i++)
            {
               EnigmaConfigData.STECKER[i] = EnigmaCurrentData.STECKER[i];
            }

            for (int i = 0; i < 6; i++)
            {
               EnigmaConfigData.WHEELTYPE[i] = EnigmaCurrentData.WHEELTYPE[i];
            }

            for (int i = 0; i < 4; i++)
            {
               EnigmaConfigData.WHEELPOS[i] = EnigmaCurrentData.WHEELPOS[i];
               EnigmaConfigData.ROTORPOS[i] = EnigmaCurrentData.ROTORPOS[i];
            }

            draw_display();
         }

         // clear the tape
         if ((p.x >= 0) && (p.x <= 240) && (p.y >= 100) && (p.y <= 115))
         {
            clear_tape();
         }

         // click below the rotors - increment the letter
         if ((p.y >= 70) && (p.y < 95))
         {
            // click below rotor 1
            if ((p.x >= 0) && (p.x < 40))
            {
               if (EnigmaCurrentData.machine_type != MACHINE_TYPE_M3)
               {
                  increment_wheel(0);
               }
            }

            // click below rotor 2
            if ((p.x >= 40) && (p.x < 80))
            {
               increment_wheel(1);
            }

            // click below rotor 3
            if ((p.x >= 80) && (p.x < 120))
            {
               increment_wheel(2);
            }

            // click below rotor 4
            if ((p.x >= 120) && (p.x < 160))
            {
               increment_wheel(3);
            }
         }

         // click above the rotors - decrement the letter
         if ((p.y >= 0) && (p.y < 25))
         {
            // click above rotor 1
            if ((p.x >= 0) && (p.x < 40))
            {
               if (EnigmaCurrentData.machine_type != MACHINE_TYPE_M3)
               {
                  decrement_wheel(0);
               }
            }

            // click above rotor 2
            if ((p.x >= 40) && (p.x < 80))
            {
               decrement_wheel(1);
            }

            // click above rotor 3
            if ((p.x >= 80) && (p.x < 120))
            {
               decrement_wheel(2);
            }

            // click above rotor 4
            if ((p.x >= 120) && (p.x < 160))
            {
               decrement_wheel(3);
            }
         }
      }
      break;

      case CONFIG_STATE:
      {
         // process LOAD button
         if ((p.x >= 190) && (p.x <= 240) && (p.y >= 40) && (p.y <= 70))
         {
            start_plug = 0;  // just in case a plug activity was in progress

            // initialize defaults (unless overriden)            
            tape_group_size = 5;
            EnigmaConfigData.WHEELTYPE[4] = USE_ROTOR_NONE;   // ADDITIONAL WHEEL (M4 only)

            // Initialize stecker with no plugs
            remove_all_plugs();

            switch (config_setup)
            {
               case CONFIG_M3_1939:
               {
                  EnigmaConfigData.machine_type = MACHINE_TYPE_M3;

                  EnigmaConfigData.WHEELTYPE[0] = USE_ROTOR_ETW;
                  EnigmaConfigData.WHEELTYPE[1] = USE_ROTOR_1;
                  EnigmaConfigData.WHEELTYPE[2] = USE_ROTOR_2;
                  EnigmaConfigData.WHEELTYPE[3] = USE_ROTOR_3;
                  EnigmaConfigData.WHEELTYPE[5] = USE_ROTOR_UKWB;

                  // Initial Rotor Positions
                  EnigmaConfigData.WHEELPOS[0] = 'A';
                  EnigmaConfigData.WHEELPOS[1] = 'A';
                  EnigmaConfigData.WHEELPOS[2] = 'A';
                  EnigmaConfigData.WHEELPOS[3] = 'A';

                  // Ring Settings
                  EnigmaConfigData.ROTORPOS[0] =  1;
                  EnigmaConfigData.ROTORPOS[1] =  1;
                  EnigmaConfigData.ROTORPOS[2] =  1;
                  EnigmaConfigData.ROTORPOS[3] =  1;
               }
               break;

               case CONFIG_EXAMPLE_1930:
               {
                  EnigmaConfigData.machine_type = MACHINE_TYPE_M3;

                  EnigmaConfigData.WHEELTYPE[0] = USE_ROTOR_ETW;
                  EnigmaConfigData.WHEELTYPE[1] = USE_ROTOR_3;
                  EnigmaConfigData.WHEELTYPE[2] = USE_ROTOR_1;
                  EnigmaConfigData.WHEELTYPE[3] = USE_ROTOR_2;
                  EnigmaConfigData.WHEELTYPE[5] = USE_ROTOR_UKWA;

                  // Initial Rotor Positions
                  EnigmaConfigData.WHEELPOS[0] = 'A';
                  EnigmaConfigData.WHEELPOS[1] = 'A';
                  EnigmaConfigData.WHEELPOS[2] = 'B';
                  EnigmaConfigData.WHEELPOS[3] = 'L';

                  // Ring Settings
                  EnigmaConfigData.ROTORPOS[0] =  1;
                  EnigmaConfigData.ROTORPOS[1] = 24;
                  EnigmaConfigData.ROTORPOS[2] = 13;
                  EnigmaConfigData.ROTORPOS[3] = 22;

                  add_config_plug('A', 'M');
                  add_config_plug('F', 'I');
                  add_config_plug('N', 'V');
                  add_config_plug('P', 'S');
                  add_config_plug('T', 'U');
                  add_config_plug('W', 'Z');
               }
               break;

               case CONFIG_ENIGMA_D:
               {
                  EnigmaConfigData.machine_type = MACHINE_TYPE_ENIGMA_D;

                  EnigmaConfigData.WHEELTYPE[0] = USE_ROTOR_ETWD;   // ENTRY CONTACTS
                  EnigmaConfigData.WHEELTYPE[1] = USE_ROTOR_D1;     // RIGHT ROTOR  (NOTE THE REVERSED ORDER VS THE OTHER ARRAYS !!)
                  EnigmaConfigData.WHEELTYPE[2] = USE_ROTOR_D2;     // MIDDLE ROTOR (NOTE THE REVERSED ORDER VS THE OTHER ARRAYS !!)
                  EnigmaConfigData.WHEELTYPE[3] = USE_ROTOR_D3;     // LEFT ROTOR   (NOTE THE REVERSED ORDER VS THE OTHER ARRAYS !!)
                  EnigmaConfigData.WHEELTYPE[5] = USE_ROTOR_UKWD;   // REFLECTOR

                  // Initial Rotor Positions
                  EnigmaConfigData.WHEELPOS[0] = 'A';    // LEFTMOST LETTER ON M4
                  EnigmaConfigData.WHEELPOS[1] = 'A';    // LEFTMOST LETTER ON M3
                  EnigmaConfigData.WHEELPOS[2] = 'A';    // MIDDLE LETTER
                  EnigmaConfigData.WHEELPOS[3] = 'A';    // RIGHTMOST LETTER

                  // Ring Settings
                  EnigmaConfigData.ROTORPOS[0] =  1;      // LEFTMOST ROTOR SETTING ON M4
                  EnigmaConfigData.ROTORPOS[1] =  1;      // LEFTMOST ROTOR SETTING ON M3
                  EnigmaConfigData.ROTORPOS[2] =  1;      // MIDDLE ROTOR SETTING
                  EnigmaConfigData.ROTORPOS[3] =  1;      // RIGHTMOST ROTOR SETTING
               }
               break;

               case CONFIG_ROCKET_K_RAILWAY:
               {
                  EnigmaConfigData.machine_type = MACHINE_TYPE_ROCKET_K;

                  EnigmaConfigData.WHEELTYPE[0] = USE_ROTOR_ETWR;   // ENTRY CONTACTS
                  EnigmaConfigData.WHEELTYPE[1] = USE_ROTOR_R1;     // RIGHT ROTOR  (NOTE THE REVERSED ORDER VS THE OTHER ARRAYS !!)
                  EnigmaConfigData.WHEELTYPE[2] = USE_ROTOR_R2;     // MIDDLE ROTOR (NOTE THE REVERSED ORDER VS THE OTHER ARRAYS !!)
                  EnigmaConfigData.WHEELTYPE[3] = USE_ROTOR_R3;     // LEFT ROTOR   (NOTE THE REVERSED ORDER VS THE OTHER ARRAYS !!)
                  EnigmaConfigData.WHEELTYPE[5] = USE_ROTOR_UKWR;   // REFLECTOR

                  // Initial Rotor Positions
                  EnigmaConfigData.WHEELPOS[0] = 'A';    // LEFTMOST LETTER ON M4
                  EnigmaConfigData.WHEELPOS[1] = 'A';    // LEFTMOST LETTER ON M3
                  EnigmaConfigData.WHEELPOS[2] = 'A';    // MIDDLE LETTER
                  EnigmaConfigData.WHEELPOS[3] = 'A';    // RIGHTMOST LETTER

                  // Ring Settings
                  EnigmaConfigData.ROTORPOS[0] =  1;      // LEFTMOST ROTOR SETTING ON M4
                  EnigmaConfigData.ROTORPOS[1] =  1;      // LEFTMOST ROTOR SETTING ON M3
                  EnigmaConfigData.ROTORPOS[2] =  1;      // MIDDLE ROTOR SETTING
                  EnigmaConfigData.ROTORPOS[3] =  1;      // RIGHTMOST ROTOR SETTING
               }
               break;

               case CONFIG_SWISS_K:
               {
                  EnigmaConfigData.machine_type = MACHINE_TYPE_SWISS_K;

                  EnigmaConfigData.WHEELTYPE[0] = USE_ROTOR_ETWS;   // ENTRY CONTACTS
                  EnigmaConfigData.WHEELTYPE[1] = USE_ROTOR_S1;     // RIGHT ROTOR  (NOTE THE REVERSED ORDER VS THE OTHER ARRAYS !!)
                  EnigmaConfigData.WHEELTYPE[2] = USE_ROTOR_S2;     // MIDDLE ROTOR (NOTE THE REVERSED ORDER VS THE OTHER ARRAYS !!)
                  EnigmaConfigData.WHEELTYPE[3] = USE_ROTOR_S3;     // LEFT ROTOR   (NOTE THE REVERSED ORDER VS THE OTHER ARRAYS !!)
                  EnigmaConfigData.WHEELTYPE[5] = USE_ROTOR_UKWS;   // REFLECTOR

                  // Initial Rotor Positions
                  EnigmaConfigData.WHEELPOS[0] = 'A';    // LEFTMOST LETTER ON M4
                  EnigmaConfigData.WHEELPOS[1] = 'A';    // LEFTMOST LETTER ON M3
                  EnigmaConfigData.WHEELPOS[2] = 'A';    // MIDDLE LETTER
                  EnigmaConfigData.WHEELPOS[3] = 'A';    // RIGHTMOST LETTER

                  // Ring Settings
                  EnigmaConfigData.ROTORPOS[0] =  1;      // LEFTMOST ROTOR SETTING ON M4
                  EnigmaConfigData.ROTORPOS[1] =  1;      // LEFTMOST ROTOR SETTING ON M3
                  EnigmaConfigData.ROTORPOS[2] =  1;      // MIDDLE ROTOR SETTING
                  EnigmaConfigData.ROTORPOS[3] =  1;      // RIGHTMOST ROTOR SETTING
               }
               break;

               case CONFIG_TURING_1940:
               {
                  EnigmaConfigData.machine_type = MACHINE_TYPE_ROCKET_K;

                  EnigmaConfigData.WHEELTYPE[0] = USE_ROTOR_ETWR;    // ENTRY CONTACTS
                  EnigmaConfigData.WHEELTYPE[1] = USE_ROTOR_R2;      // RIGHT ROTOR  (NOTE THE REVERSED ORDER VS THE OTHER ARRAYS !!)
                  EnigmaConfigData.WHEELTYPE[2] = USE_ROTOR_R1;      // MIDDLE ROTOR (NOTE THE REVERSED ORDER VS THE OTHER ARRAYS !!)
                  EnigmaConfigData.WHEELTYPE[3] = USE_ROTOR_R3;      // LEFT ROTOR   (NOTE THE REVERSED ORDER VS THE OTHER ARRAYS !!)
                  EnigmaConfigData.WHEELTYPE[5] = USE_ROTOR_UKWR;    // REFLECTOR

                  // Initial Rotor Positions
                  EnigmaConfigData.WHEELPOS[0] = 'J';    // LEFTMOST LETTER ON M4
                  EnigmaConfigData.WHEELPOS[1] = 'E';    // LEFTMOST LETTER ON M3
                  EnigmaConfigData.WHEELPOS[2] = 'Z';    // MIDDLE LETTER
                  EnigmaConfigData.WHEELPOS[3] = 'A';    // RIGHTMOST LETTER

                  // Ring Settings
                  EnigmaConfigData.ROTORPOS[0] = 26;      // LEFTMOST ROTOR SETTING ON M4
                  EnigmaConfigData.ROTORPOS[1] = 17;      // LEFTMOST ROTOR SETTING ON M3
                  EnigmaConfigData.ROTORPOS[2] = 16;      // MIDDLE ROTOR SETTING
                  EnigmaConfigData.ROTORPOS[3] = 13;      // RIGHTMOST ROTOR SETTING
               }
               break;

               case CONFIG_BARBAROSA_1941:
               {
                  EnigmaConfigData.machine_type = MACHINE_TYPE_M3;

                  EnigmaConfigData.WHEELTYPE[0] = USE_ROTOR_ETW;
                  EnigmaConfigData.WHEELTYPE[1] = USE_ROTOR_5;
                  EnigmaConfigData.WHEELTYPE[2] = USE_ROTOR_4;
                  EnigmaConfigData.WHEELTYPE[3] = USE_ROTOR_2;
                  EnigmaConfigData.WHEELTYPE[5] = USE_ROTOR_UKWB;

                  // Initial Rotor Positions
                  EnigmaConfigData.WHEELPOS[0] = 'A';
                  EnigmaConfigData.WHEELPOS[1] = 'B';
                  EnigmaConfigData.WHEELPOS[2] = 'L';
                  EnigmaConfigData.WHEELPOS[3] = 'A';

                  // Ring Settings
                  EnigmaConfigData.ROTORPOS[0] = 1;
                  EnigmaConfigData.ROTORPOS[1] = 2;
                  EnigmaConfigData.ROTORPOS[2] = 21;
                  EnigmaConfigData.ROTORPOS[3] = 12;

                  add_config_plug('A', 'V');
                  add_config_plug('B', 'S');
                  add_config_plug('C', 'G');
                  add_config_plug('D', 'L');
                  add_config_plug('F', 'U');
                  add_config_plug('H', 'Z');
                  add_config_plug('I', 'N');
                  add_config_plug('K', 'M');
                  add_config_plug('O', 'W');
                  add_config_plug('R', 'X');
               }
               break;

               case CONFIG_M4_1942:
               {
                  EnigmaConfigData.machine_type = MACHINE_TYPE_M4;

                  EnigmaConfigData.WHEELTYPE[0] = USE_ROTOR_ETW;    // ENTRY CONTACTS
                  EnigmaConfigData.WHEELTYPE[1] = USE_ROTOR_1;      // RIGHT ROTOR  (NOTE THE REVERSED ORDER VS THE OTHER ARRAYS !!)
                  EnigmaConfigData.WHEELTYPE[2] = USE_ROTOR_2;      // MIDDLE ROTOR (NOTE THE REVERSED ORDER VS THE OTHER ARRAYS !!)
                  EnigmaConfigData.WHEELTYPE[3] = USE_ROTOR_3;      // LEFT ROTOR   (NOTE THE REVERSED ORDER VS THE OTHER ARRAYS !!)
                  EnigmaConfigData.WHEELTYPE[4] = USE_ROTOR_B;      // ADDITIONAL WHEEL (M4 only)
                  EnigmaConfigData.WHEELTYPE[5] = USE_ROTOR_UKWBD;  // REFLECTOR

                  // Initial Rotor Positions
                  EnigmaConfigData.WHEELPOS[0] = 'A';    // LEFTMOST LETTER ON M4
                  EnigmaConfigData.WHEELPOS[1] = 'A';    // LEFTMOST LETTER ON M3
                  EnigmaConfigData.WHEELPOS[2] = 'A';    // MIDDLE LETTER
                  EnigmaConfigData.WHEELPOS[3] = 'A';    // RIGHTMOST LETTER

                  // Ring Settings
                  EnigmaConfigData.ROTORPOS[0] =  1;      // LEFTMOST ROTOR SETTING ON M4
                  EnigmaConfigData.ROTORPOS[1] =  1;      // LEFTMOST ROTOR SETTING ON M3
                  EnigmaConfigData.ROTORPOS[2] =  1;      // MIDDLE ROTOR SETTING
                  EnigmaConfigData.ROTORPOS[3] =  1;      // RIGHTMOST ROTOR SETTING

                  tape_group_size = 4;
               }
               break;

               case CONFIG_U264_1942:
               {
                  EnigmaConfigData.machine_type = MACHINE_TYPE_M4;

                  EnigmaConfigData.WHEELTYPE[0] = USE_ROTOR_ETW;    // ENTRY CONTACTS
                  EnigmaConfigData.WHEELTYPE[1] = USE_ROTOR_1;      // RIGHT ROTOR  (NOTE THE REVERSED ORDER VS THE OTHER ARRAYS !!)
                  EnigmaConfigData.WHEELTYPE[2] = USE_ROTOR_4;      // MIDDLE ROTOR (NOTE THE REVERSED ORDER VS THE OTHER ARRAYS !!)
                  EnigmaConfigData.WHEELTYPE[3] = USE_ROTOR_2;      // LEFT ROTOR   (NOTE THE REVERSED ORDER VS THE OTHER ARRAYS !!)
                  EnigmaConfigData.WHEELTYPE[4] = USE_ROTOR_B;      // ADDITIONAL WHEEL (M4 only)
                  EnigmaConfigData.WHEELTYPE[5] = USE_ROTOR_UKWBD;  // REFLECTOR

                  // Initial Rotor Positions
                  EnigmaConfigData.WHEELPOS[0] = 'V';    // LEFTMOST LETTER ON M4
                  EnigmaConfigData.WHEELPOS[1] = 'J';    // LEFTMOST LETTER ON M3
                  EnigmaConfigData.WHEELPOS[2] = 'N';    // MIDDLE LETTER
                  EnigmaConfigData.WHEELPOS[3] = 'A';    // RIGHTMOST LETTER

                  // Ring Settings
                  EnigmaConfigData.ROTORPOS[0] =  1;      // LEFTMOST ROTOR SETTING ON M4
                  EnigmaConfigData.ROTORPOS[1] =  1;      // LEFTMOST ROTOR SETTING ON M3
                  EnigmaConfigData.ROTORPOS[2] =  1;      // MIDDLE ROTOR SETTING
                  EnigmaConfigData.ROTORPOS[3] =  22;      // RIGHTMOST ROTOR SETTING

                  add_config_plug('A', 'T');
                  add_config_plug('B', 'L');
                  add_config_plug('D', 'F');
                  add_config_plug('G', 'J');
                  add_config_plug('H', 'M');
                  add_config_plug('N', 'W');
                  add_config_plug('O', 'P');
                  add_config_plug('Q', 'Y');
                  add_config_plug('R', 'Z');
                  add_config_plug('V', 'X');

                  tape_group_size = 4;
               }
               break;

               case CONFIG_SCHARNHORST_1943:
               {
                  EnigmaConfigData.machine_type = MACHINE_TYPE_M3;

                  EnigmaConfigData.WHEELTYPE[0] = USE_ROTOR_ETW;    // ENTRY CONTACTS
                  EnigmaConfigData.WHEELTYPE[1] = USE_ROTOR_8;      // RIGHT ROTOR  (NOTE THE REVERSED ORDER VS THE OTHER ARRAYS !!)
                  EnigmaConfigData.WHEELTYPE[2] = USE_ROTOR_6;      // MIDDLE ROTOR (NOTE THE REVERSED ORDER VS THE OTHER ARRAYS !!)
                  EnigmaConfigData.WHEELTYPE[3] = USE_ROTOR_3;      // LEFT ROTOR   (NOTE THE REVERSED ORDER VS THE OTHER ARRAYS !!)
                  EnigmaConfigData.WHEELTYPE[5] = USE_ROTOR_UKWB;   // REFLECTOR

                  // Initial Rotor Positions
                  EnigmaConfigData.WHEELPOS[0] = 'A';    // LEFTMOST LETTER ON M4
                  EnigmaConfigData.WHEELPOS[1] = 'U';    // LEFTMOST LETTER ON M3
                  EnigmaConfigData.WHEELPOS[2] = 'Z';    // MIDDLE LETTER
                  EnigmaConfigData.WHEELPOS[3] = 'V';    // RIGHTMOST LETTER

                  // Ring Settings
                  EnigmaConfigData.ROTORPOS[0] =  1;      // LEFTMOST ROTOR SETTING ON M4
                  EnigmaConfigData.ROTORPOS[1] =  1;      // LEFTMOST ROTOR SETTING ON M3
                  EnigmaConfigData.ROTORPOS[2] =  8;      // MIDDLE ROTOR SETTING
                  EnigmaConfigData.ROTORPOS[3] = 13;      // RIGHTMOST ROTOR SETTING

                  add_config_plug('A', 'N');
                  add_config_plug('E', 'Z');
                  add_config_plug('H', 'K');
                  add_config_plug('I', 'J');
                  add_config_plug('L', 'R');
                  add_config_plug('M', 'Q');
                  add_config_plug('O', 'T');
                  add_config_plug('P', 'V');
                  add_config_plug('S', 'W');
                  add_config_plug('U', 'X');
               }
               break;
            }

            // redraw the config display for each config change
            draw_config_display();                  
         }

         // process DISCARD button
         if ((p.x >= 30) && (p.x <= 90) && (p.y >= 290) && (p.y < 320))
         {
            enigma_state = OPERATE_STATE;

            show_rotors_over_serial();

            draw_display();
         }

         // process ACTIVATE button
         if ((p.x >= 150) && (p.x <= 210) && (p.y >= 290) && (p.y < 320))
         {
            for (int i = 0; i < 6; i++)
            {
               EnigmaCurrentData.WHEELTYPE[i] = EnigmaConfigData.WHEELTYPE[i];
            }

            for (int i = 0; i < 4; i++)
            {
               EnigmaCurrentData.WHEELPOS[i] = EnigmaConfigData.WHEELPOS[i];
               EnigmaCurrentData.ROTORPOS[i] = EnigmaConfigData.ROTORPOS[i];
            }

            start_plug = 0;  // just in case a plug activity was in progress

            enigma_state = OPERATE_STATE;

            if (EnigmaConfigData.machine_type != EnigmaCurrentData.machine_type)
            {
               EnigmaCurrentData.machine_type = EnigmaConfigData.machine_type;
            }

            for (int i = 0; i < 27; i++)
            {
               EnigmaCurrentData.STECKER[i] = EnigmaConfigData.STECKER[i];
            }

            calculate_stecker();

            show_rotors_over_serial();

            draw_display();
         }

         // process machine type button
         if ((p.x >= 0) && (p.x <= 240) && (p.y >= 75) && (p.y < 95))
         {
            if (EnigmaConfigData.machine_type != MACHINE_TYPE_SWISS_K)
            {
               EnigmaConfigData.machine_type = (MACHINE_TYPE)(((int)EnigmaConfigData.machine_type) + 1);
            }
            else
            {
               EnigmaConfigData.machine_type = MACHINE_TYPE_M3;
            }

            // common to all (unless overriden)
            EnigmaConfigData.WHEELPOS[0] = 'A';
            EnigmaConfigData.ROTORPOS[0] = 1;
            EnigmaConfigData.WHEELTYPE[4] = USE_ROTOR_NONE;

            switch (EnigmaConfigData.machine_type)
            {
               case MACHINE_TYPE_M3:
               {
                  if ((EnigmaConfigData.WHEELTYPE[5] != USE_ROTOR_UKWA) &&
                      (EnigmaConfigData.WHEELTYPE[5] != USE_ROTOR_UKWB) &&
                      (EnigmaConfigData.WHEELTYPE[5] != USE_ROTOR_UKWC))
                  {
                     EnigmaConfigData.WHEELTYPE[5] = USE_ROTOR_UKWA;
                  }

                  if ((EnigmaConfigData.WHEELTYPE[3] != USE_ROTOR_1) &&
                      (EnigmaConfigData.WHEELTYPE[3] != USE_ROTOR_2) &&
                      (EnigmaConfigData.WHEELTYPE[3] != USE_ROTOR_3))
                  {
                     EnigmaConfigData.WHEELTYPE[3] = USE_ROTOR_1;
                  }

                  if ((EnigmaConfigData.WHEELTYPE[2] != USE_ROTOR_1) &&
                      (EnigmaConfigData.WHEELTYPE[2] != USE_ROTOR_2) &&
                      (EnigmaConfigData.WHEELTYPE[2] != USE_ROTOR_3))
                  {
                     EnigmaConfigData.WHEELTYPE[2] = USE_ROTOR_2;
                  }

                  if ((EnigmaConfigData.WHEELTYPE[1] != USE_ROTOR_1) &&
                      (EnigmaConfigData.WHEELTYPE[1] != USE_ROTOR_2) &&
                      (EnigmaConfigData.WHEELTYPE[1] != USE_ROTOR_3))
                  {
                     EnigmaConfigData.WHEELTYPE[1] = USE_ROTOR_3;
                  }

                  EnigmaConfigData.WHEELTYPE[0] = USE_ROTOR_ETW;
               }
               break;

               case MACHINE_TYPE_M4:
               {
                  if ((EnigmaConfigData.WHEELTYPE[5] != USE_ROTOR_UKWBD) &&
                      (EnigmaConfigData.WHEELTYPE[5] != USE_ROTOR_UKWCD))
                  {
                     EnigmaConfigData.WHEELTYPE[5] = USE_ROTOR_UKWBD;
                  }

                  if ((EnigmaConfigData.WHEELTYPE[4] != USE_ROTOR_B) &&
                      (EnigmaConfigData.WHEELTYPE[4] != USE_ROTOR_G))
                  {
                     EnigmaConfigData.WHEELTYPE[4] = USE_ROTOR_B;
                  }

                  if ((EnigmaConfigData.WHEELTYPE[3] != USE_ROTOR_1) &&
                      (EnigmaConfigData.WHEELTYPE[3] != USE_ROTOR_2) &&
                      (EnigmaConfigData.WHEELTYPE[3] != USE_ROTOR_3))
                  {
                     EnigmaConfigData.WHEELTYPE[3] = USE_ROTOR_1;
                  }

                  if ((EnigmaConfigData.WHEELTYPE[2] != USE_ROTOR_1) &&
                      (EnigmaConfigData.WHEELTYPE[2] != USE_ROTOR_2) &&
                      (EnigmaConfigData.WHEELTYPE[2] != USE_ROTOR_3))
                  {
                     EnigmaConfigData.WHEELTYPE[2] = USE_ROTOR_2;
                  }

                  if ((EnigmaConfigData.WHEELTYPE[1] != USE_ROTOR_1) &&
                      (EnigmaConfigData.WHEELTYPE[1] != USE_ROTOR_2) &&
                      (EnigmaConfigData.WHEELTYPE[1] != USE_ROTOR_3))
                  {
                     EnigmaConfigData.WHEELTYPE[1] = USE_ROTOR_3;
                  }

                  EnigmaConfigData.WHEELTYPE[0] = USE_ROTOR_ETW;
               }
               break;

               case MACHINE_TYPE_ENIGMA_D:
               {
                  if (EnigmaConfigData.WHEELTYPE[5] != USE_ROTOR_UKWD)
                  {
                     EnigmaConfigData.WHEELTYPE[5] = USE_ROTOR_UKWD;
                  }

                  if ((EnigmaConfigData.WHEELTYPE[3] != USE_ROTOR_D1) &&
                      (EnigmaConfigData.WHEELTYPE[3] != USE_ROTOR_D2) &&
                      (EnigmaConfigData.WHEELTYPE[3] != USE_ROTOR_D3))
                  {
                     EnigmaConfigData.WHEELTYPE[3] = USE_ROTOR_D1;
                  }

                  if ((EnigmaConfigData.WHEELTYPE[2] != USE_ROTOR_D1) &&
                      (EnigmaConfigData.WHEELTYPE[2] != USE_ROTOR_D2) &&
                      (EnigmaConfigData.WHEELTYPE[2] != USE_ROTOR_D3))
                  {
                     EnigmaConfigData.WHEELTYPE[2] = USE_ROTOR_D2;
                  }

                  if ((EnigmaConfigData.WHEELTYPE[1] != USE_ROTOR_D1) &&
                      (EnigmaConfigData.WHEELTYPE[1] != USE_ROTOR_D2) &&
                      (EnigmaConfigData.WHEELTYPE[1] != USE_ROTOR_D3))
                  {
                     EnigmaConfigData.WHEELTYPE[1] = USE_ROTOR_D3;
                  }

                  EnigmaConfigData.WHEELTYPE[0] = USE_ROTOR_ETWD;
               }
               break;

               case MACHINE_TYPE_ROCKET_K:
               {
                  if (EnigmaConfigData.WHEELTYPE[5] != USE_ROTOR_UKWR)
                  {
                     EnigmaConfigData.WHEELTYPE[5] = USE_ROTOR_UKWR;
                  }

                  if ((EnigmaConfigData.WHEELTYPE[3] != USE_ROTOR_R1) &&
                      (EnigmaConfigData.WHEELTYPE[3] != USE_ROTOR_R2) &&
                      (EnigmaConfigData.WHEELTYPE[3] != USE_ROTOR_R3))
                  {
                     EnigmaConfigData.WHEELTYPE[3] = USE_ROTOR_R1;
                  }

                  if ((EnigmaConfigData.WHEELTYPE[2] != USE_ROTOR_R1) &&
                      (EnigmaConfigData.WHEELTYPE[2] != USE_ROTOR_R2) &&
                      (EnigmaConfigData.WHEELTYPE[2] != USE_ROTOR_R3))
                  {
                     EnigmaConfigData.WHEELTYPE[2] = USE_ROTOR_R2;
                  }

                  if ((EnigmaConfigData.WHEELTYPE[1] != USE_ROTOR_R1) &&
                      (EnigmaConfigData.WHEELTYPE[1] != USE_ROTOR_R2) &&
                      (EnigmaConfigData.WHEELTYPE[1] != USE_ROTOR_R3))
                  {
                     EnigmaConfigData.WHEELTYPE[1] = USE_ROTOR_R3;
                  }

                  EnigmaConfigData.WHEELTYPE[0] = USE_ROTOR_ETWR;
               }
               break;

               case MACHINE_TYPE_SWISS_K:
               {
                  if (EnigmaConfigData.WHEELTYPE[5] != USE_ROTOR_UKWS)
                  {
                     EnigmaConfigData.WHEELTYPE[5] = USE_ROTOR_UKWS;
                  }

                  if ((EnigmaConfigData.WHEELTYPE[3] != USE_ROTOR_S1) &&
                      (EnigmaConfigData.WHEELTYPE[3] != USE_ROTOR_S2) &&
                      (EnigmaConfigData.WHEELTYPE[3] != USE_ROTOR_S3))
                  {
                     EnigmaConfigData.WHEELTYPE[3] = USE_ROTOR_S1;
                  }

                  if ((EnigmaConfigData.WHEELTYPE[2] != USE_ROTOR_S1) &&
                      (EnigmaConfigData.WHEELTYPE[2] != USE_ROTOR_S2) &&
                      (EnigmaConfigData.WHEELTYPE[2] != USE_ROTOR_S3))
                  {
                     EnigmaConfigData.WHEELTYPE[2] = USE_ROTOR_S2;
                  }

                  if ((EnigmaConfigData.WHEELTYPE[1] != USE_ROTOR_S1) &&
                      (EnigmaConfigData.WHEELTYPE[1] != USE_ROTOR_S2) &&
                      (EnigmaConfigData.WHEELTYPE[1] != USE_ROTOR_S3))
                  {
                     EnigmaConfigData.WHEELTYPE[1] = USE_ROTOR_S3;
                  }

                  EnigmaConfigData.WHEELTYPE[0] = USE_ROTOR_ETWS;
               }
               break;
            }

            draw_config_display();
         }

         // process reflector type button
         if ((p.x >= 0) && (p.x <= 240) && (p.y >= 95) && (p.y < 115))
         {
            if (EnigmaConfigData.machine_type == MACHINE_TYPE_M3)
            {
               if (EnigmaConfigData.WHEELTYPE[5] == USE_ROTOR_UKWA)
               {
                  EnigmaConfigData.WHEELTYPE[5] = USE_ROTOR_UKWB;
               }
               else
               {
                  if (EnigmaConfigData.WHEELTYPE[5] == USE_ROTOR_UKWB)
                  {
                     EnigmaConfigData.WHEELTYPE[5] = USE_ROTOR_UKWC;
                  }
                  else
                  {
                     EnigmaConfigData.WHEELTYPE[5] = USE_ROTOR_UKWA;
                  }
               }
            }
            else
            {
               if (EnigmaConfigData.machine_type == MACHINE_TYPE_M4)
               {
                  if (EnigmaConfigData.WHEELTYPE[5] == USE_ROTOR_UKWBD)
                  {
                     EnigmaConfigData.WHEELTYPE[5] = USE_ROTOR_UKWCD;
                  }
                  else
                  {
                     EnigmaConfigData.WHEELTYPE[5] = USE_ROTOR_UKWBD;
                  }
               }
               else
               {
                  EnigmaConfigData.WHEELTYPE[5] = USE_ROTOR_UKWD;
               }
            }

            draw_config_reflector();            
         }

         // process tape group size button
         if ((p.x >= 0) && (p.x <= 240) && (p.y >= 115) && (p.y < 135))
         {
            switch (tape_group_size)
            {
               case 0:
               {
                  tape_group_size = 3;
               }
               break;

               case 3:
               case 4:
               case 5:
               {
                  tape_group_size += 1;
               }
               break;

               default:
               {
                  tape_group_size = 0;
               }
               break;
            }

            draw_config_tape_group_size();
         }

         // process wheel4 button
         if ((p.x >= 110) && (p.x <= 135) && (p.y >= 150) && (p.y < 160))
         {
            if (EnigmaConfigData.machine_type == MACHINE_TYPE_M4)
            {
               if (EnigmaConfigData.WHEELTYPE[4] == USE_ROTOR_B)
               {
                  EnigmaConfigData.WHEELTYPE[4] = USE_ROTOR_G;
               }
               else
               {
                  EnigmaConfigData.WHEELTYPE[4] = USE_ROTOR_B;
               }

               draw_config_wheels();
            }
         }

         // process wheel3 button
         if ((p.x >= 140) && (p.x <= 165) && (p.y >= 150) && (p.y < 160))
         {
            switch (EnigmaConfigData.machine_type)
            {
               case MACHINE_TYPE_ENIGMA_D:
               {
                  if (EnigmaConfigData.WHEELTYPE[3] < USE_ROTOR_D3)
                  {
                     EnigmaConfigData.WHEELTYPE[3]++;
                  }
                  else
                  {
                     EnigmaConfigData.WHEELTYPE[3] = USE_ROTOR_D1;
                  }
               }
               break;

               case MACHINE_TYPE_ROCKET_K:
               {
                  if (EnigmaConfigData.WHEELTYPE[3] < USE_ROTOR_R3)
                  {
                     EnigmaConfigData.WHEELTYPE[3]++;
                  }
                  else
                  {
                     EnigmaConfigData.WHEELTYPE[3] = USE_ROTOR_R1;
                  }
               }
               break;

               case MACHINE_TYPE_SWISS_K:
               {
                  if (EnigmaConfigData.WHEELTYPE[3] < USE_ROTOR_S3)
                  {
                     EnigmaConfigData.WHEELTYPE[3]++;
                  }
                  else
                  {
                     EnigmaConfigData.WHEELTYPE[3] = USE_ROTOR_S1;
                  }
               }
               break;

               default:
               {
                  if (EnigmaConfigData.WHEELTYPE[3] < USE_ROTOR_8)
                  {
                     EnigmaConfigData.WHEELTYPE[3]++;
                  }
                  else
                  {
                     EnigmaConfigData.WHEELTYPE[3] = USE_ROTOR_1;
                  }
               }
               break;
            }

            draw_config_wheels();
         }

         // process wheel2 button
         if ((p.x >= 170) && (p.x <= 195) && (p.y >= 150) && (p.y < 160))
         {
            switch (EnigmaConfigData.machine_type)
            {
               case MACHINE_TYPE_ENIGMA_D:
               {
                  if (EnigmaConfigData.WHEELTYPE[2] < USE_ROTOR_D3)
                  {
                     EnigmaConfigData.WHEELTYPE[2]++;
                  }
                  else
                  {
                     EnigmaConfigData.WHEELTYPE[2] = USE_ROTOR_D1;
                  }
               }
               break;

               case MACHINE_TYPE_ROCKET_K:
               {
                  if (EnigmaConfigData.WHEELTYPE[2] < USE_ROTOR_R3)
                  {
                     EnigmaConfigData.WHEELTYPE[2]++;
                  }
                  else
                  {
                     EnigmaConfigData.WHEELTYPE[2] = USE_ROTOR_R1;
                  }
               }
               break;

               case MACHINE_TYPE_SWISS_K:
               {
                  if (EnigmaConfigData.WHEELTYPE[2] < USE_ROTOR_S3)
                  {
                     EnigmaConfigData.WHEELTYPE[2]++;
                  }
                  else
                  {
                     EnigmaConfigData.WHEELTYPE[2] = USE_ROTOR_S1;
                  }
               }
               break;

               default:
               {
                  if (EnigmaConfigData.WHEELTYPE[2] < USE_ROTOR_8)
                  {
                     EnigmaConfigData.WHEELTYPE[2]++;
                  }
                  else
                  {
                     EnigmaConfigData.WHEELTYPE[2] = USE_ROTOR_1;
                  }
               }
               break;
            }

            draw_config_wheels();
         }

         // process wheel button
         if ((p.x >= 200) && (p.x <= 225) && (p.y >= 150) && (p.y < 160))
         {
            switch (EnigmaConfigData.machine_type)
            {
               case MACHINE_TYPE_ENIGMA_D:
               {
                  if (EnigmaConfigData.WHEELTYPE[1] < USE_ROTOR_D3)
                  {
                     EnigmaConfigData.WHEELTYPE[1]++;
                  }
                  else
                  {
                     EnigmaConfigData.WHEELTYPE[1] = USE_ROTOR_D1;
                  }
               }
               break;

               case MACHINE_TYPE_ROCKET_K:
               {
                  if (EnigmaConfigData.WHEELTYPE[1] < USE_ROTOR_R3)
                  {
                     EnigmaConfigData.WHEELTYPE[1]++;
                  }
                  else
                  {
                     EnigmaConfigData.WHEELTYPE[1] = USE_ROTOR_R1;
                  }
               }
               break;

               case MACHINE_TYPE_SWISS_K:
               {
                  if (EnigmaConfigData.WHEELTYPE[1] < USE_ROTOR_S3)
                  {
                     EnigmaConfigData.WHEELTYPE[1]++;
                  }
                  else
                  {
                     EnigmaConfigData.WHEELTYPE[1] = USE_ROTOR_S1;
                  }
               }
               break;

               default:
               {
                  if (EnigmaConfigData.WHEELTYPE[1] < USE_ROTOR_8)
                  {
                     EnigmaConfigData.WHEELTYPE[1]++;
                  }
                  else
                  {
                     EnigmaConfigData.WHEELTYPE[1] = USE_ROTOR_1;
                  }
               }
               break;
            }

            draw_config_wheels();
         }

         // process ring4 button
         if ((p.x >= 110) && (p.x <= 135) && (p.y >= 170) && (p.y < 180))
         {
            if (EnigmaConfigData.machine_type != MACHINE_TYPE_M3)
            {
               if (EnigmaConfigData.ROTORPOS[0] < 26)
               {
                  EnigmaConfigData.ROTORPOS[0]++;
               }
               else
               {
                  EnigmaConfigData.ROTORPOS[0] = 1;
               }

               draw_config_rings();
            }
         }

         // process ring3 button
         if ((p.x >= 140) && (p.x <= 165) && (p.y >= 170) && (p.y < 180))
         {
            if (EnigmaConfigData.ROTORPOS[1] < 26)
            {
               EnigmaConfigData.ROTORPOS[1]++;
            }
            else
            {
               EnigmaConfigData.ROTORPOS[1] = 1;
            }

            draw_config_rings();
         }

         // process ring2 button
         if ((p.x >= 170) && (p.x <= 195) && (p.y >= 170) && (p.y < 180))
         {
            if (EnigmaConfigData.ROTORPOS[2] < 26)
            {
               EnigmaConfigData.ROTORPOS[2]++;
            }
            else
            {
               EnigmaConfigData.ROTORPOS[2] = 1;
            }

            draw_config_rings();
         }

         // process ring1 button
         if ((p.x >= 200) && (p.x <= 225) && (p.y >= 170) && (p.y < 180))
         {
            if (EnigmaConfigData.ROTORPOS[3] < 26)
            {
               EnigmaConfigData.ROTORPOS[3]++;
            }
            else
            {
               EnigmaConfigData.ROTORPOS[3] = 1;
            }

            draw_config_rings();
         }

         // process setup type button
         if ((p.x >= 0) && (p.x <= 180) && (p.y >= 40) && (p.y <= 70))
         {
            config_setup++;

            if (config_setup > CONFIG_SCHARNHORST_1943)
            {
               config_setup = CONFIG_M3_1939;
            }

            draw_config_setup();
         }

         // detect top row of plugs
         if ((p.y >= 195) && (p.y <= 235) && ((EnigmaConfigData.machine_type == MACHINE_TYPE_M3) || (EnigmaConfigData.machine_type == MACHINE_TYPE_M4)))
         {
            // process plug A button
            if ((p.x >= 5) && (p.x <= 19))
            {
               process_plug_inputs('A');
            }

            if ((p.x >= 23) && (p.x <= 37))
            {
               process_plug_inputs('B');
            }

            if ((p.x >= 41) && (p.x <= 55))
            {
               process_plug_inputs('C');
            }

            if ((p.x >= 59) && (p.x <= 73))
            {
               process_plug_inputs('D');
            }

            if ((p.x >= 77) && (p.x <= 91))
            {
               process_plug_inputs('E');
            }

            if ((p.x >= 95) && (p.x <= 109))
            {
               process_plug_inputs('F');
            }

            if ((p.x >= 113) && (p.x <= 127))
            {
               process_plug_inputs('G');
            }

            if ((p.x >= 131) && (p.x <= 145))
            {
               process_plug_inputs('H');
            }

            if ((p.x >= 149) && (p.x <= 163))
            {
               process_plug_inputs('I');
            }

            if ((p.x >= 167) && (p.x <= 181))
            {
               process_plug_inputs('J');
            }

            if ((p.x >= 185) && (p.x <= 199))
            {
               process_plug_inputs('K');
            }

            if ((p.x >= 203) && (p.x <= 217))
            {
               process_plug_inputs('L');
            }

            if ((p.x >= 221) && (p.x <= 235))
            {
               process_plug_inputs('M');
            }
         }

         // detect second row of plugs
         if ((p.y >= 240) && (p.y <= 280) && ((EnigmaConfigData.machine_type == MACHINE_TYPE_M3) || (EnigmaConfigData.machine_type == MACHINE_TYPE_M4)))
         {
            if ((p.x >= 5) && (p.x <= 19))
            {
               process_plug_inputs('N');
            }

            if ((p.x >= 23) && (p.x <= 37))
            {
               process_plug_inputs('O');
            }

            if ((p.x >= 41) && (p.x <= 55))
            {
               process_plug_inputs('P');
            }

            if ((p.x >= 59) && (p.x <= 73))
            {
               process_plug_inputs('Q');
            }

            if ((p.x >= 77) && (p.x <= 91))
            {
               process_plug_inputs('R');
            }

            if ((p.x >= 95) && (p.x <= 109))
            {
               process_plug_inputs('S');
            }

            if ((p.x >= 113) && (p.x <= 127))
            {
               process_plug_inputs('T');
            }

            if ((p.x >= 131) && (p.x <= 145))
            {
               process_plug_inputs('U');
            }

            if ((p.x >= 149) && (p.x <= 163))
            {
               process_plug_inputs('V');
            }

            if ((p.x >= 167) && (p.x <= 181))
            {
               process_plug_inputs('W');
            }

            if ((p.x >= 185) && (p.x <= 199))
            {
               process_plug_inputs('X');
            }

            if ((p.x >= 203) && (p.x <= 217))
            {
               process_plug_inputs('Y');
            }

            if ((p.x >= 221) && (p.x <= 235))
            {
               process_plug_inputs('Z');
            }
         }
      }
      break;
   }
}  // process_button_inputs()


// act on keypresses
char process_key_inputs(char pressed_key)
{
   char encoded_key;

   draw_keys(pressed_key, IS_PRESSED);

   move_wheels();

   draw_rotor_letters();

   encoded_key = encode_key(pressed_key);

   add_char_to_tape(encoded_key);

   draw_lights(encoded_key, IS_PRESSED);

   return(encoded_key);
}  // process_key_inputs()


// act on plug button inputs
void process_plug_inputs(char plug_char)
{
   if (EnigmaConfigData.STECKER[plug_char - 65] != plug_char)
   {
      add_config_plug(EnigmaConfigData.STECKER[plug_char - 65], EnigmaConfigData.STECKER[plug_char - 65]);
      add_config_plug(plug_char, plug_char);

      if (start_plug != 0)
      {
         add_config_plug(plug_char, start_plug);
         start_plug = 0;
      }
   }
   else
   {
      if (start_plug == 0)
      {
         start_plug = plug_char;

         draw_config_plugboard();
      }
      else
      {
         if (start_plug == plug_char)
         {
            start_plug = 0;

            draw_config_plugboard();
         }
         else
         {
            add_config_plug(plug_char, start_plug);
            start_plug = 0;
         }
      }
   }
}  // process_plug_inputs


// process serial inputs & outputs
void process_serial(void)
{
   if (enigma_state == OPERATE_STATE)
   {
      if (Serial.available())
      {
         KeyPressed = Serial.read();

         KeyPressed = KeyPressed & (255 - 32);

         if ((KeyPressed > 'A' - 1) && (KeyPressed < 'Z' + 1))
         {
            SerialRead = true;

            move_wheels();

            draw_rotor_letters();

            draw_keys(KeyPressed, IS_PRESSED);

            EncodedKey = encode_key(KeyPressed);

            add_char_to_tape(EncodedKey);

            draw_lights(EncodedKey, IS_PRESSED);

            delay(500);

            draw_keys(KeyPressed, NOT_PRESSED);

            draw_lights(EncodedKey, NOT_PRESSED);
         }
      }

      if ((SerialRead) && (!Serial.available()))
      {
         SerialRead = false;
      }
   }
}  // process_serial()


// take out all plug wires at once
void remove_all_plugs(void)
{
   strcpy(EnigmaCurrentData.STECKER, "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
   strcpy(EnigmaConfigData.STECKER, "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
}  // remove_all_plugs()


// send the current machine internal state out to serial
void serial_monitor(char k)
{
   static byte SerialMonitorStepIndex;

   if (k == 0)
   {
      SerialMonitorStepIndex = 0;
   }
   else
   {
      SerialMonitorStepIndex++;

      // unless this is an Enigma M4 (4-wheel machine), skip R4
      if ((EnigmaCurrentData.machine_type != MACHINE_TYPE_M4) && ((SerialMonitorStepIndex == 6) || (SerialMonitorStepIndex == 8)))
      {
         SerialMonitorStepIndex++;
      }

      Serial.print(k);

      switch (SerialMonitorStepIndex)
      {
         case 1:
         case 13:
         {
            Serial.print(F(" > Stecker > "));
         }
         break;

         case 2:
         case 12:
         {
            Serial.print(F(" > ETW > "));
         }
         break;

         case 3:
         case 11:
         {
            Serial.print(F(" > R1 > "));
         }
         break;

         case 4:
         case 10:
         {
            Serial.print(F(" > R2 > "));
         }
         break;

         case 5:
         case 9:
         {
            Serial.print(F(" > R3 > "));
         }
         break;

         case 6:
         case 8:
         {
            Serial.print(F(" > R4 > "));
         }
         break;

         case 7:
         {
            Serial.print(F(" > UKW > "));
         }
         break;

         default:
         {
            Serial.println("");
         }
      }
   }
}  // serial_monitor()


// initialize everything
void setup(void)
{
   Serial.begin(9600);

   delay(500);
   tft.begin();
   tft.setRotation(0);
   delay(100);
   ts.begin();
   ts.setRotation(2);

   while (!Serial && (millis() <= 1000));

   WHEELSF = F(STR(ETW) STR(ROTOR1) STR(ROTOR2) STR(ROTOR3) STR(ROTOR4) STR(ROTOR5) STR(ROTOR6) STR(ROTOR7) STR(ROTOR8)
               STR(UKWA) STR(UKWB) STR(UKWC) STR(ROTORB) STR(ROTORG) STR(UKWBD) STR(UKWCD)
               STR(ETWD) STR(ROTORD1) STR(ROTORD2) STR(ROTORD3) STR(UKWD)
               STR(ETWR) STR(ROTORR1) STR(ROTORR2) STR(ROTORR3) STR(UKWR)
               STR(ETWS) STR(ROTORS1) STR(ROTORS2) STR(ROTORS3) STR(UKWS));

   LOGOX1 = F("\x00\x00\x03\xFF\xFC\x00\x00\x00"
              "\x00\x00\xFF\xFF\xFF\xF0\x00\x00"
              "\x00\x07\xFC\x00\x03\xFE\x00\x00"
              "\x00\x3F\x00\x00\x00\x0F\xC0\x00"
              "\x00\xF8\x01\x8F\x1F\x01\xF0\x00"
              "\x03\xC0\xF9\x9F\x9F\xF0\x3C\x00"
              "\x07\x03\xF9\x99\x99\xF8\x0E\x00"
              "\x0C\x33\x19\x98\x19\x99\x83\x00"
              "\x19\xF3\x19\x98\x19\x99\xE1\x80"
              "\x39\x83\x19\x98\x19\x99\xF9\xC0"
              "\x79\x83\x19\x98\x19\x99\x99\xE0"
              "\xDD\xE3\x19\x9B\x99\x99\x9B\xB0"
              "\x8D\xE3\x19\x9B\x99\x99\xFB\x10"
              "\xDD\x83\x19\x99\x99\x99\xFB\xB0"
              "\x79\x83\x19\x99\x99\x99\x99\xE0"
              "\x39\xC3\x19\x99\x99\x99\x99\xC0"
              "\x19\xF3\x19\x99\x99\x99\x91\x80"
              "\x0C\x33\x19\x99\x99\x99\x83\x00"
              "\x07\x03\x19\x99\x99\x99\x0E\x00"
              "\x03\xC0\x19\x9F\x99\x90\x3C\x00"
              "\x00\xF8\x19\x8F\x19\x01\xF0\x00"
              "\x00\x3F\x00\x00\x00\x0F\xC0\x00"
              "\x00\x07\xFC\x00\x03\xFE\x00\x00"
              "\x00\x00\xFF\xFF\xFF\xF0\x00\x00"
              "\x00\x00\x03\xFF\xFC\x00\x00\x00");

   LOGOX2 = F("\x00\x00\x00\x00\x03\xFF\xFF\xFF\xFF\xFC\x00\x00\x00\x00\x00"
              "\x00\x00\x00\x00\x3F\xFF\xFF\xFF\xFF\xFF\xC0\x00\x00\x00\x00"
              "\x00\x00\x00\x07\xFF\xFF\xFF\xFF\xFF\xFF\xFE\x00\x00\x00\x00"
              "\x00\x00\x00\x3F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xC0\x00\x00\x00"
              "\x00\x00\x01\xFF\xFF\xF0\x00\x00\x00\xFF\xFF\xF8\x00\x00\x00"
              "\x00\x00\x0F\xFF\xFE\x00\x00\x00\x00\x07\xFF\xFF\x00\x00\x00"
              "\x00\x00\x3F\xFF\x80\x00\x00\x00\x00\x00\x1F\xFF\xC0\x00\x00"
              "\x00\x00\xFF\xFC\x00\x00\x00\x00\x00\x00\x03\xFF\xF0\x00\x00"
              "\x00\x03\xFF\xE0\x00\x0F\x03\xFC\x0F\xFF\xC0\x7F\xFC\x00\x00"
              "\x00\x0F\xFF\x00\x00\x0F\x07\xFE\x0F\xFF\xF0\x0F\xFF\x00\x00"
              "\x00\x3F\xF8\x03\xFF\x0F\x0F\xFF\x0F\xFF\xFC\x01\xFF\xC0\x00"
              "\x00\x7F\xC0\x0F\xFF\x0F\x0F\xFF\x0F\xFF\xFE\x00\x3F\xE0\x00"
              "\x00\xFF\x00\x3F\xFF\x0F\x0F\x0F\x0F\x0F\x1F\x00\x0F\xF0\x00"
              "\x01\xFC\x00\x3F\xFF\x0F\x0F\x0F\x0F\x0F\x0F\x00\x03\xF8\x00"
              "\x03\xF0\x3C\x3C\x0F\x0F\x0F\x00\x0F\x0F\x0F\x0F\x00\xFC\x00"
              "\x07\xC1\xFC\x3C\x0F\x0F\x0F\x00\x0F\x0F\x0F\x0F\xC0\x3E\x00"
              "\x0F\x87\xFC\x3C\x0F\x0F\x0F\x00\x0F\x0F\x0F\x0F\xF0\x1F\x00"
              "\x1F\x0F\xE0\x3C\x0F\x0F\x0F\x00\x0F\x0F\x0F\x0F\xFC\x0F\x80"
              "\x3F\x0F\x00\x3C\x0F\x0F\x0F\x00\x0F\x0F\x0F\x0F\xFF\x0F\xC0"
              "\x3F\x0F\x00\x3C\x0F\x0F\x0F\x00\x0F\x0F\x0F\x0F\xFF\x0F\xC0"
              "\x7F\x0F\x00\x3C\x0F\x0F\x0F\x00\x0F\x0F\x0F\x0F\x0F\x0F\xE0"
              "\x7F\x0F\x00\x3C\x0F\x0F\x0F\x00\x0F\x0F\x0F\x0F\x0F\x0F\xE0"
              "\xFF\x8F\xF0\x3C\x0F\x0F\x0F\x3F\x0F\x0F\x0F\x0F\x0F\x1F\xF0"
              "\xF3\xCF\xF0\x3C\x0F\x0F\x0F\x3F\x0F\x0F\x0F\x0F\x0F\x3C\xF0"
              "\xE1\xCF\xF0\x3C\x0F\x0F\x0F\x3F\x0F\x0F\x0F\x0F\xFF\x38\x70"
              "\xE1\xCF\xF0\x3C\x0F\x0F\x0F\x3F\x0F\x0F\x0F\x0F\xFF\x38\x70"
              "\xF3\xCF\x00\x3C\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\xFF\x3C\xF0"
              "\xFF\x8F\x00\x3C\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\xFF\x1F\xF0"
              "\x7F\x0F\x00\x3C\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\xE0"
              "\x7F\x0F\x00\x3C\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\xE0"
              "\x3F\x0F\xC0\x3C\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\xC0"
              "\x3F\x0F\xC0\x3C\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\xC0"
              "\x1F\x0F\xE0\x3C\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0E\x0F\x80"
              "\x0F\x87\xFC\x3C\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0C\x1F\x00"
              "\x07\xC1\xFC\x3C\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x00\x3E\x00"
              "\x03\xF0\x3C\x3C\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x00\xFC\x00"
              "\x01\xFC\x00\x3C\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0E\x03\xF8\x00"
              "\x00\xFF\x00\x3C\x0F\x0F\x0F\x0F\x0F\x0F\x0F\x0C\x0F\xF0\x00"
              "\x00\x7F\xC0\x00\x0F\x0F\x0F\xFF\x0F\x0F\x0E\x00\x3F\xE0\x00"
              "\x00\x3F\xF8\x00\x0F\x0F\x0F\xFF\x0F\x0F\x0C\x01\xFF\xC0\x00"
              "\x00\x0F\xFF\x00\x0F\x0F\x07\xFE\x0F\x0E\x00\x0F\xFF\x00\x00"
              "\x00\x03\xFF\xE0\x0F\x0F\x03\xFC\x0F\x0C\x00\x7F\xFC\x00\x00"
              "\x00\x00\xFF\xFC\x00\x00\x00\x00\x00\x00\x03\xFF\xF0\x00\x00"
              "\x00\x00\x3F\xFF\x80\x00\x00\x00\x00\x00\x1F\xFF\xC0\x00\x00"
              "\x00\x00\x0F\xFF\xFE\x00\x00\x00\x00\x07\xFF\xFF\x00\x00\x00"
              "\x00\x00\x01\xFF\xFF\xF0\x00\x00\x00\xFF\xFF\xF8\x00\x00\x00"
              "\x00\x00\x00\x3F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xC0\x00\x00\x00"
              "\x00\x00\x00\x07\xFF\xFF\xFF\xFF\xFF\xFF\xFE\x00\x00\x00\x00"
              "\x00\x00\x00\x00\x3F\xFF\xFF\xFF\xFF\xFF\xC0\x00\x00\x00\x00"
              "\x00\x00\x00\x00\x03\xFF\xFF\xFF\xFF\xFC\x00\x00\x00\x00\x00");

   enigma_state = SPLASH_STATE;

   draw_display();
}  // setup()


// show initialization over serial
void show_init_over_serial(void)
{
   Serial.println();
   Serial.println(TITLE);
   Serial.println(VERSION0);
   Serial.println(VERSION1);
   Serial.println();
   Serial.println(TAPSTART);
   Serial.println();
   Serial.println();
}  // show_init_over_serial()


// show the current machine settings over serial
void show_machine_over_serial(void)
{
   Serial.println("");
   Serial.println("");
   switch(EnigmaCurrentData.machine_type)
   {
      case MACHINE_TYPE_M3:
      {
         Serial.println(F("[ Enigma M3 (1939) ]"));
      }
      break;

      case MACHINE_TYPE_M4:
      {
         Serial.println(F("[ Enigma M4 (1942) ]"));
      }
      break;

      case MACHINE_TYPE_ENIGMA_D:
      {
         Serial.println(F("[ Business Enigma D ]"));
      }
      break;

      case MACHINE_TYPE_ROCKET_K:
      {
         Serial.println(F("[ Rocket K Railway ]"));
      }
      break;

      case MACHINE_TYPE_SWISS_K:
      {
         Serial.println(F("[ Swiss K ]"));
      }
      break;
   }

   Serial.println("");
   Serial.print(F("Stecker                 : "));
   Serial.println(EffSTECKER);
}  // show_machine_over_serial()


// send the current value of the rotors out serial
void show_rotors_over_serial(void)
{
   const char *charptr = (const char *)WHEELSF;
   char k;
   unsigned int wheeltype;

   for (byte i = 0; i < 6; i++)
   {
      if (EnigmaCurrentData.WHEELTYPE[i] != 0)
      {
         switch (i)
         {
            case 0:
            {
               Serial.print(F("ETW (input)             : "));
            }
            break;

            case 1:
            {
               Serial.print(F("R1  (rightmost)         : "));
            }
            break;

            case 2:
            {
               Serial.print(F("R2  (2nd from right)    : "));
            }
            break;

            case 3:
            {
               Serial.print(F("R3  (leftmost of three) : "));
            }
            break;

            case 4:
            {
               Serial.print(F("R4  (leftmost of four)  : "));
            }
            break;

            case 5:
            {
               Serial.print(F("UKW (reflector)         : "));
            }
            break;
         }

         wheeltype = ((EnigmaCurrentData.WHEELTYPE[i] - 1) * 28) + 2;

         for (byte i = 0; i < 26; i++)
         {
            k = pgm_read_byte(charptr + wheeltype + i);
            Serial.print(k);
         }
         Serial.println("");
     }
   }
}  // show_rotors_over_serial()


//
// Sample Enigma messages for testing/verification ( from http://franklinheath.co.uk )
//
//
// =======================================
//              Example 1930
// =======================================
// Machine   : M3
// Reflector : A
// Wheels    : II I III
// Rings     : 24 13 22
// Plugs     : AM FI NV PS TU WZ
//
// Key       : ABL
//
// Encrypted:
// gcdse ahugw tqgrk vlfgx ucalx vymig
// mmnmf dxtgn vhvrm mevou yfzsl rhdrr
// xfjwc fhuhm unzef rdisi kbgpm yvxuz
//
// Decrypted:
// feind liqei nfant eriek olonn ebeob
// aqtet xanfa ngsue dausg angba erwal
// dexen dedre ikmos twaer tsneu stadt
//
// German:
// Feindliche Infanterie Kolonne beobachtet.  Anfang Sudausgang Barwalde. Ende 3km ostwarts Neustadt.
//
// English:
// Enemy infantry column was observed. Beginning [at] southern exit [of] Baerwald. Ending 3km east of Neustadt.
//
// =======================================
//              Turing 1940
// =======================================
// Machine   : Rocket K Railway
// Reflector : N/A
// Wheels    :    III I II
// Rings     : 26 17 16 13
// Plugs     : N/A
//
// Key       : JEZA
//
// Encrypted:
// qszvi dvmpn exacm rwwxu iyoty ngvvx
// dz
//
// Decrypted:
// deuts qetru ppens indje tztin engla
// nd
//
// German:
// Deutsche Truppen sind jetzt in England.
//
// English:
// German troops are now in England.
//
//
// =======================================
//             Barbarosa 1941
// =======================================
// Machine   : M3
// Reflector : B
// Wheels    : II IV V
// Rings     : 02 21 12
// Plugs     : AV BS CG DL FU HZ IN KM OW RX
//
// Key       : BLA
//
// Encrypted:
// edpud nrgys zrcxn uytpo mrmbo fktbz
// rezkm lxlve fguey siozv eqmik ubpmm
// ylklt tdeis mdica gykua ctcdo mohwx
// muuia ubsts lrnbz szwnr fxwfy ssxjz
// vijhi dishp rklka yupad txqsp inqma
// tlpif svkda sctac dpbop vhjk
//
// Decrypted:
// aufkl xabte ilung xvonx kurti nowax
// kurti nowax nordw estlx sebez xsebe
// zxuaf flieg erstr aszer iqtun gxdub
// rowki xdubr owkix opots chkax opots
// chkax umxei nsaqt drein ullxu hrang
// etret enxan griff xinfx rgtx
//
// German:
// Aufklarung abteilung von Kurtinowa Kurtinowa nordwestlich Sebez Sebez [auf]
// Fliegerstase in Richtung Dubrowki Dubrowki, Opotschka Opotschka. Um 18:30 Uhr
// angetreten angriff. Infanterie Regiment
//
// English:
// Reconnaisance division from Kurtinowa north-west of Sebech on the flight
// corridor towards Dubrowki, Opochka. Attack begun at 18:30 hours. Infantry
// Regiment
//
// Key       : LSD
//
// Encrypted:
// sfbwd njuse gqobh krtar eezmw kpprb
// xohdr oeqgb bgtqv pgvkb vvgbi mhusz
// ydajq iroax sssnr ehygg rpise zbovm
// qiemm zcysg qdgre rvbil ekxyq irgir
// qnrdn vrxcy ytnjr
//
// Decrypted:
// dreig ehtla ngsam abers iqerv orwae
// rtsxe inssi ebenn ullse qsxuh rxroe
// mxein sxinf rgtxd reixa uffli egers
// trasz emita nfang xeins seqsx kmxkm
// xostw xkame necxk 
//
// German:
// 3 geht langsam aber sicher vorwarts. 17:06 Uhr rom eins Infanterie Regiment 3
// auf Fliegerstase mit Anfang 16km km ostwarts Kamanec K.
//
// English:
// 3 goes slowly but surely forwards.  17:06 hours [Roman Numeral I] Infantry
// Regiment 3 on the flight corridor starting 16km east of Kamanec.
//
//
// =======================================
//               U-264 1942
// =======================================
// Machine   : M4
// Reflector : Thin B
// Wheels    : beta II IV I
// Rings     : 01 01 01 22
// Plugs     : AT BL DF GJ HM NW OP QY RZ VX
//
// Key       : VJNA
//
// Encrypted:
// nczw vusx pnym inhz xmqx sfwx wlkj ahsh
// nmco ccak uqpm kcsm hkse inju sblk iosx
// ckub hmll xcsj usrr dvko hulx wccb gvli
// yxeo ahxr hkkf vdre wezl xoba fgyu jquk
// grtv ukam eurb veks uhhv oyha bcjw makl
// fklm yfvn rizr vvrt kofd anjm olbg ffle
// oprg tflv rhow opbe kvwm uqfm pwpa rmfh
// agkx iibg
//
// Decrypted:
// vonv onjl ooks jhff ttte inse insd reiz
// woyy qnns neun inha ltxx beia ngri ffun
// terw asse rged ruec ktyw abos xlet zter
// gegn erst andn ulac htdr einu luhr marq
// uant onjo tane unac htse yhsd reiy zwoz
// wonu lgra dyac htsm ysto ssen achx ekns
// vier mbfa ellt ynnn nnno oovi erys icht
// eins null
//
// German:
// Von Von 'Looks' F T 1132/19 Inhalt: Bei Angriff unter Wasser gedruckt,
// Wasserbomben. Letzter Gegnerstandort 08:30 Uhr Marine Quadrat AJ9863, 220 Grad,
// 8sm, stosse nach. 14mb fallt, NNO 4, Sicht 10.
//
// English:
// From Looks, radio-telegram 1132/19 contents: Forced to submerge under attack,
// depth charges. Last enemy location 08:30 hours, sea square AJ9863, following 220
// degrees, 8 knots. [Pressure] 14 millibars falling, [wind] north-north-east 4,
// visibility 10.
//
//
// =======================================
//            Scharnhorst 1943
// =======================================
// Machine   : M3
// Reflector : B
// Wheels    : III VI VIII
// Rings     : 01 08 13
// Plugs     : AN EZ HK IJ LR MQ OT PV SW UX
//
// Key       : UZV
//
// Encrypted:
// ykae nzap msch zbfo cuvm rmdp ycof hadz
// izme fxth flol pzlf ggbo tgox gret dwtj
// iqhl mxvj wkzu astr
//
// Decrypted:
// steue rejta nafjo rdjan stand ortqu
// aaacc cvier neunn eunzw ofahr tzwon
// ulsmx xscha rnhor sthco
//
// German:
// Steuere Tanafjord an. Standort Quadrat AC4992, fahrt 20sm. Scharnhorst. [hco -
// padding?]
//
// English:
// Heading for Tanafjord. Position is square AC4992, speed 20 knots. Scharnhorst.
//
// End of sample Enigma messages for testing/verification
//

I hope that this can be of some help . . . please let me know if you need any clarifications on anything presented here.

Mark J Culross
KD5RXT
 
Thanks for that. That helps. I will try to implement something like that. Was a bit worried that I had to create dozens of new variables to keep track of, which would be a housekeeping nightmare. This is easier. Think I cobbled something like this together, but using an elapsed time. Yours is better, as it seems to act on finger release.
 
What I typically do is upon a touch, I enter a while touched loop. Below are excerpts and my own button lib. If you are using adafruit buttons, this will not work, but at least you can follow the logic in ProcessButtonPress().


Code:
 if (Touch.dataAvailable()) {

    ProcessTouch(); // get the x y touch location

   if (ProcessButtonPress(Button1)) {
         // do something
    }
}


void ProcessTouch() {

  // depending on the touch library you may need to change methods here
  Touch.read();

  BtnX = Touch.getX();
  BtnY = Touch.getY();

  // consistency between displays is a mess...
  // this is some debug code to help show
  // where you pressed and the resulting map

  //Serial.print("real coordinates: ");
  //Serial.print(BtnX);
  //Serial.print(",");
  //Serial.println (BtnY);
  //Display.drawPixel(BtnX, BtnY, C_RED);

  //different values depending on where touch happened

  // x  = map(x, real left, real right, 0, 480);
  // y  = map(y, real bottom, real top, 320, 0);

  // tft with yellow headers
  //BtnX  = map(BtnX, 240, 0, 320, 0);
  //BtnY  = map(BtnY, 379, 0, 240, 0);

  // tft with black headers
  BtnX  = map(BtnX, 0, 240, 320, 0);
  BtnY  = map(BtnY, 0, 380, 240, 0);

  Serial.print(", Mapped coordinates: ");
  Serial.print(BtnX);
  Serial.print(",");
  Serial.println(BtnY);
  Display.drawPixel(BtnX, BtnY, C_GREEN);

}





bool ProcessButtonPress(Button TheButton) {

  if (TheButton.press(BtnX, BtnY)) {
    TheButton.draw(B_PRESSED);
    while (Touch.dataAvailable()) {
      if (TheButton.press(BtnX, BtnY)) {
        TheButton.draw(B_PRESSED);
      }
      else {
        TheButton.draw(B_RELEASED);
        return false;
      }
      ProcessTouch();
    }

    TheButton.draw(B_RELEASED);
    return true;
  }
  return false;
}
 
What I typically do is upon a touch, I enter a while touched loop. Below are excerpts and my own button lib. If you are using adafruit buttons, this will not work, but at least you can follow the logic in ProcessButtonPress().


Code:
 if (Touch.dataAvailable()) {

    ProcessTouch(); // get the x y touch location

   if (ProcessButtonPress(Button1)) {
         // do something
    }
}


void ProcessTouch() {

  // depending on the touch library you may need to change methods here
  Touch.read();

  BtnX = Touch.getX();
  BtnY = Touch.getY();

  // consistency between displays is a mess...
  // this is some debug code to help show
  // where you pressed and the resulting map

  //Serial.print("real coordinates: ");
  //Serial.print(BtnX);
  //Serial.print(",");
  //Serial.println (BtnY);
  //Display.drawPixel(BtnX, BtnY, C_RED);

  //different values depending on where touch happened

  // x  = map(x, real left, real right, 0, 480);
  // y  = map(y, real bottom, real top, 320, 0);

  // tft with yellow headers
  //BtnX  = map(BtnX, 240, 0, 320, 0);
  //BtnY  = map(BtnY, 379, 0, 240, 0);

  // tft with black headers
  BtnX  = map(BtnX, 0, 240, 320, 0);
  BtnY  = map(BtnY, 0, 380, 240, 0);

  Serial.print(", Mapped coordinates: ");
  Serial.print(BtnX);
  Serial.print(",");
  Serial.println(BtnY);
  Display.drawPixel(BtnX, BtnY, C_GREEN);

}





bool ProcessButtonPress(Button TheButton) {

  if (TheButton.press(BtnX, BtnY)) {
    TheButton.draw(B_PRESSED);
    while (Touch.dataAvailable()) {
      if (TheButton.press(BtnX, BtnY)) {
        TheButton.draw(B_PRESSED);
      }
      else {
        TheButton.draw(B_RELEASED);
        return false;
      }
      ProcessTouch();
    }

    TheButton.draw(B_RELEASED);
    return true;
  }
  return false;
}

Thanks, I will study that this weekend. Not using anyone's button library at this point. Mostly because I'm not a very good at software, so it's often hard for me to apply the libraries and still get the functionality that I desire.

My primary concerns are not repainting the display too often, for aesthetic concerns, preventing from drilling through menus, and being able to stop the controller in a timely fashion. Drilling through menus is when there are multiple buttons (at the same xy location) on several screens. If you leave your finger there too long, you may find you have selected a bunch of things, you hadn't intended. If one is controlling a machine, (one that is perfectly capable of killing you without any remorse,) that is not so good. I'm not paranoid, but around machines, you have to be on guard - all the time. Fortunately, in my set up mode, the motor controls are off. For an additional layer of safety, I want to make sure the transitions require a deliberate press and release to activate, or at least I think I do. Guess I won't know until I try it.
 
Yeah I hear ya, that’s actually why I wrote a new button library. You can have two buttons on top of one another and you just hide one and it will not be recognized. I also have enable disable states in a bunch of other things. One key thing that I do that while your fingers pressed the button will not Flickr it just looks like a normal button being pressed
 
Back
Top