Working on a project that uses OSC to control lighting software. Teensy 3.5 is the brains, and everything is working except the displays. One is a LCD-20x4Y (HD44780 driver), the other is a NHD-0440WH-ATMI-JT# 40x4. Both light up with 2 rows of boxes. I can control contrast and backlight with pots built in. I've traced the wiring a number of times with eyes and multimeter, seems like I have that right.

Tried undoing all wiring except VSS, VDD, VO and led power, no change. Should I be getting 5V on the DB pins with nothing attached?

Code is attached. I had to cut out some sections to keep to the size limit, nothing related to the display I hope. Not sure I can get pictures worth anything with how tight the build is and the distance between screen and board.

Thanks for any ideas.

Code:
#include <OSCBoards.h>
#include <OSCBundle.h>
#include <OSCData.h>
#include <OSCMatch.h>
#include <OSCTiming.h>
#ifdef BOARD_HAS_USB_SERIAL
#include <SLIPEncodedUSBSerial.h>
SLIPEncodedUSBSerial SLIPSerial(thisBoardsSerialUSB);
#else
#include <SLIPEncodedSerial.h>
SLIPEncodedSerial SLIPSerial(Serial);
#endif
#include <LiquidCrystal.h>
#include <string.h>

//LCD is the single display for P/T ad S1 and S2; LCD 2 is the left half of the 40 X 4, and LCD 3 is the right half of the 40 X 4
#define LCD_CHARS   20
#define LCD_LINES   4
#define LCD2_CHARS   40
#define LCD2_LINES   2
#define LCD3_CHARS   40
#define LCD3_LINES   2

#define LED           29  //Pin for LED backlights
byte dim = 255;//Starting level for LED brightness
byte LEDLevel[7] = {0, 31, 63, 95, 159, 255};

#define SOFTKEY_1     23
#define SOFTKEY_2     22
#define SOFTKEY_3     21
#define SOFTKEY_4     20
#define SOFTKEY_5     19
#define SOFTKEY_6     18
#define MORE_SOFTKEYS 17
#define SHIFT_BTN     16
#define INTENSITY_BTN 15
#define FOCUS_BTN     14
#define COLOR_BTN     13
#define IMAGE_BTN     38
#define FORM_BTN      37
#define SHUTTER_BTN   39
#define ENC1_BTN     2
#define ENC2_BTN     5
#define ENC3_BTN     8
#define ENC4_BTN     11
#define ENC5_BTN     25
#define ENC6_BTN     28
#define EDGE_DOWN   1
#define EDGE_UP     0
#define FORWARD     0
#define REVERSE_     1//REVERSE replaced with REVERSE_ as REVERSE is a recognized keyword
#define ENC1_DIR     REVERSE_
#define ENC2_DIR    REVERSE_
#define ENC3_DIR    REVERSE_
#define ENC4_DIR    REVERSE_
#define ENC5_DIR    REVERSE_
#define ENC6_DIR    REVERSE_
#define SIG_DIGITS  3
#define OSC_BUF_MAX_SIZE 512

const String HANDSHAKE_QUERY = "ETCOSC?";
const String HANDSHAKE_REPLY = "OK";
const String EOS_KEY = "/eos/key/";
const String EOS_CMD = "/eos/cmd";
const String EOS_SOFTKEY_1 = "SOFTKEY_1";
const String EOS_SOFTKEY_2 = "SOFTKEY_2";
const String EOS_SOFTKEY_3 = "SOFTKEY_3";
const String EOS_SOFTKEY_4 = "SOFTKEY_4";
const String EOS_SOFTKEY_5 = "SOFTKEY_5";
const String EOS_SOFTKEY_6 = "SOFTKEY_6";
const String EOS_MORE_SOFTKEYS = "MORE_SOFTKEYS";
const String EOS_SHIFT = "SHIFT";
const String EOS_PAN_Cmd = "Pan";
const String EOS_TILT_Cmd = "Tilt";
const String EOS_HUE_Cmd = "Hue";
const String EOS_SAT_Cmd = "Saturation";
const String EOS_INTENSITY = "Intensity";
const String EOS_FOCUS = "Focus";
const String EOS_COLOR = "Color";
const String EOS_IMAGE = "Image";
const String EOS_FORM = "Form";
const String EOS_SHUTTER = "Shutter";
const String EOS_INTENSITY_CAT = "encoder_category_intensity";
const String EOS_FOCUS_CAT = "encoder_category_focus";
const String EOS_COLOR_CAT = "encoder_category_color";
const String EOS_SHUTTER_CAT = "encoder_category_shutter";
const String EOS_IMAGE_CAT = "encoder_category_image";
const String EOS_FORM_CAT = "encoder_category_form";
const String CategoryKeyMessages[7] = {"Easter Eggs R Us!", EOS_INTENSITY_CAT, EOS_FOCUS_CAT, EOS_COLOR_CAT, EOS_IMAGE_CAT, EOS_FORM_CAT, EOS_SHUTTER_CAT};
const String EOS_WHEEL = "/eos";
//These are the messages for routing OSC commands
const String EOS_CHAN_OUT = "/eos/out/active/chan";
const String EOS_PT_ADDRESS = "/eos/out/pantilt";
const String EOS_HS_VALUES = "/eos/out/color/hs";
const String EOS_SK_OUT = "/eos/out/softkey";
const String EOS_WHEEL_OUT = "/eos/out/active/wheel";
//The below are the active wheel numbers of the physical Encoders bank - they currently start at Active Wheels 1,2,3,4
int E3 = 1;
int E4 = 2;
int E5 = 3;
int E6 = 4;

//This identifies the current category of parameter that is populating the physical encoders
//1 Intensity, 2 Focus, 3 Color, 4 Image, 5 Form, 6 Shutter
//All Arrays are 7 so that the index is the same as the number being used for convenience
int CurrentCategory = 1;
int FirstParameter[7] = {0, 0, 0, 0, 0, 0, 0}; //Initializes the first parameter in each category as 1
int LastParameter[7] = {0, 0, 0, 0, 0, 0, 0}; //Initializes the last parameter as 0 (meaning there are no parameters until an active channel is chosen)
int CurrentParameterPage[7] = {0, 0, 0, 0, 0, 0, 0}; //Array to track what the current page is for each category; stores the wheel number for E3.
//- i.e. if you are moving a shutter and go to edge, when you come back to shutter, you're on the same page
bool NoParameters[7] = {true, true, true, true, true, true, true};//These are booleans that identify when a channel does not have certain parameters - and thus provide fun messages:)

bool HueSatPresent[2] = {false, false};//Detects if a fixture has hue and/or sat
int HueSatActive = 79;//Where the first of the active wheels for Hue/Sat parameters are Initialized in the highest possible spot
int HueSatActiveWheels[2] = {79, 79};//Where each of the wheels are (needed for rewriting fixtures)
int HueSatShift = 0;//How much we have to shift color parameters based off of Hue and Sat

char ErrorMessageLine1[7][41] = {"This fixture has literally no parameters", "This fixture has no intensity", "This fixture has no focus parameters;",
                                 "This fixture has no color parameters;", "This fixture has no image parameters;", "This fixture has no form parameters;", "This fixture has no shutter parameters;"
                                };
char ErrorMessageLine2[7][40] = {"       ...maybe patch something?", "parameters; maybe it's a hazer?", "you should probably go get a ladder.",
                                 "maybe add a cut of L202?", "I hear templates are out of style.", "you probably need a different light.", "have you tried a square template?"
                                };
int CategoryButton[7] = {0, INTENSITY_BTN, FOCUS_BTN, COLOR_BTN, IMAGE_BTN, FORM_BTN, SHUTTER_BTN};
byte CategoryButtonState[7] = {HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH};
const String CategoryKeyCommand[7] = {"(C) JB & JD", EOS_INTENSITY, EOS_FOCUS, EOS_COLOR, EOS_IMAGE, EOS_FORM, EOS_SHUTTER};

bool EosParametersChanged = false;//Boolean that lets us know if Eos has changed the parameters
unsigned long ParameterUpdateTime = 0;//lets us know when a parameter was last updated - used to not run the parameter counter on every active wheel command
bool NewParameterSort = false;//for when a new channel is chosen and it runs the parameter check. It keeps it from advancing to the next set of parameters
bool HSISwap = false;//for when we decided to have pan/tilt swap out to Hue Saturation - a bit of an easter egg that will be in the software
bool shiftMode = false;//for holding shift and hitting an encoder - it allows you to add the mode function
bool SelectedChannel = true;//This is for recognizing when no channel is selected and when choosing newfixtures 120807
bool NewChannel = false;//This is a new one for when the channel selection changes
bool NewParams = true;//This checks to see if the parameter names are being cleared out.
char ActiveChannel [500];

byte ButtonState[14] = {HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH,
                        HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH
                       };
int Buttons[14] = {SOFTKEY_1, SOFTKEY_2, SOFTKEY_3, SOFTKEY_4, SOFTKEY_5, SOFTKEY_6, MORE_SOFTKEYS,
                   SHIFT_BTN, ENC1_BTN, ENC2_BTN, ENC3_BTN, ENC4_BTN, ENC5_BTN, ENC6_BTN
                  };
const String KeyMessages[12] = {EOS_SOFTKEY_1, EOS_SOFTKEY_2, EOS_SOFTKEY_3, EOS_SOFTKEY_4, EOS_SOFTKEY_5, EOS_SOFTKEY_6, EOS_MORE_SOFTKEYS, EOS_SHIFT, EOS_PAN_Cmd, EOS_TILT_Cmd, EOS_HUE_Cmd, EOS_SAT_Cmd};

int SKnum; //number of softkey
char Softkeys [13] [15];//Array that holds the names of all of the softkeys - 13 places as 0 is empty...

const int keyDelay = 1;//With a Teensy, the buttons have a bounce that is less than 1 millisecond - this conpemsnates for that.
const int ParamDelay = 25;//This is the wait between parsing the last "Active Wheel command" and flipping the Boolean to run the parameter counter function
//Tried a speed of 10 - it was too short; 50 doesn't feel laggy at all

enum WHEEL_TYPE { ENC1, ENC2, ENC3, ENC4, ENC5, ENC6 };
enum WHEEL_MODE { COARSE, FINE };

struct Encoder
{
  uint8_t pinA;
  uint8_t pinB;
  uint8_t buttonPin;
  uint8_t buttonState;
  int pinAPrevious;
  int pinBPrevious;
  float pos;
  uint8_t direction;
};

struct Encoder enc1wheel;
struct Encoder enc2wheel;
struct Encoder huewheel;
struct Encoder satwheel;
struct Encoder enc3wheel;
struct Encoder enc4wheel;
struct Encoder enc5wheel;
struct Encoder enc6wheel;

//Current software only works for fixtures with up to 79 active wheels but is easily scalable...
char mainWheelNames [80] [12];
char mainWheelCommand [80] [30];
char mainWheelData [80] [5];
int mainWheelType [80];
int wheelnum;
char WheelData[5];

//define the pins for the lcd displays
LiquidCrystal LeftLCD(46, 31, 42, 45, 40, 43);
LiquidCrystal RightTopLCD(53, 52, 49, 50, 47, 48);
LiquidCrystal RightBottomLCD(53, 54, 49, 50, 47, 48);

//booleans for when displays need to be updated
bool updateE1andE2Display = false;
bool updateSk1and2Display = false;
bool updateEncoderDisplay = false;
bool updateSoftKeyDisplay = false;
bool updateE3NameDisplay = false;
bool updateE4NameDisplay = false;
bool updateE5NameDisplay = false;
bool updateE6NameDisplay = false;
bool updateE3DataDisplay = false;
bool updateE4DataDisplay = false;
bool updateE5DataDisplay = false;
bool updateE6DataDisplay = false;

int32_t EncoderMotion[7];

void parseOSCMessage(String& msg)//When a serial message is received it comes here. We then handshake if it's the handshake or send it to the apprpriate OSC function.
{
  if (msg.indexOf(HANDSHAKE_QUERY) != -1)
  {
    SLIPSerial.beginPacket();
    SLIPSerial.write((const uint8_t*)HANDSHAKE_REPLY.c_str(), (size_t)HANDSHAKE_REPLY.length());
    SLIPSerial.endPacket();
  }
  else
  {
    OSCMessage oscmsg;
    oscmsg.fill((uint8_t*)msg.c_str(), (int)msg.length());
    oscmsg.route(EOS_PT_ADDRESS.c_str(), PanTiltUpdate);
    oscmsg.route(EOS_HS_VALUES.c_str(), HueSatUpdate);
    oscmsg.route(EOS_SK_OUT.c_str(), SKUpdate);
    oscmsg.route(EOS_CHAN_OUT.c_str(), ChannelUpdate);
    oscmsg.route(EOS_WHEEL_OUT.c_str(), EncoderUpdate);
  }
}

//Functions below take in OSC messages and modify internal variables
void PanTiltUpdate(OSCMessage& msg, int addressOffset)
{
  enc1wheel.pos = msg.getOSCData(4)->getFloat();
  enc2wheel.pos = msg.getOSCData(5)->getFloat();
  updateE1andE2Display = true;
}

void HueSatUpdate(OSCMessage& msg, int addressOffset)
{
  huewheel.pos = msg.getOSCData(0)->getFloat();
  satwheel.pos = msg.getOSCData(1)->getFloat();
  updateE1andE2Display = true;
}

void EncoderUpdate(OSCMessage& msg, int addressOffset)
{
  //This needs to be a little more organized, but it all works. JB 20 May 2020
  int length1 = msg.getDataLength(0);
  if (length1 == 0 ) return;

  char tmp[2];//This gets the wheel number
  msg.getAddress(tmp, addressOffset + 1);
  wheelnum = atoi(tmp);
  if (wheelnum > 79) return;//if the wheel number is greater than 79, we're done here

  char tmp1[length1];
  msg.OSCMessage::getString(0, tmp1, length1);

  sscanf(tmp1, "%*[^[][%s]", WheelData);//this gets the data inside the bracket
  WheelData[strlen(WheelData) - 1] = 0;// this removes the last character which is a bracket

  char WheelName[25];
  sscanf(tmp1, "%[^[]" , WheelName);  //Gets the encoder name only up to 25 characters

  int wheeltype = msg.getInt(1);// this gets the category

  strncpy(mainWheelNames[wheelnum], WheelName, 9);//For display purposes, we only use the first 9 characters

  if ((wheelnum == HueSatActiveWheels[0]) && (strcmp(WheelName, "Hue  ") != 0)) //Clears out hue modifiers when we lose hue
  {
    HueSatPresent[0] = false;
    HueSatShift = (HueSatPresent[0] + HueSatPresent[1]);
    HueSatActiveWheels[0] = 79;
  }
  if ((wheelnum == HueSatActiveWheels[1]) && (strcmp(WheelName, "Saturatn  ") != 0)) //Clears out saturation modifiers when we lose saturation
  {
    HueSatPresent[1] = false;
    HueSatShift = (HueSatPresent[0] + HueSatPresent[1]);
    HueSatActiveWheels[1] = 79;
  }
  if (EosParametersChanged)
  {
    if (strcmp(WheelName, "Hue  ") == 0)
    {
      HueSatPresent[0] = true;
      HueSatActiveWheels[0] = wheelnum;
      HueSatActive = wheelnum;
      HueSatShift = (HueSatPresent[0] + HueSatPresent[1]);
    }
    if (strcmp(WheelName, "Saturatn  ") == 0)
    {
      HueSatPresent[1] = true;
      HueSatActiveWheels[1] = wheelnum;
      if (wheelnum < HueSatActive) HueSatActive = wheelnum;
      HueSatShift = (HueSatPresent[0] + HueSatPresent[1]);
    }
  }

  //Eventually the below will be a separate function so it can be maintained more easily, but for now,it's here
  //Eos uses abbreviated names for some wheels (probably from Ion days). They work on the command line, but the real words look nicer.
  if (strcmp(WheelName, "Blu Adj  ") == 0) strncpy(mainWheelCommand[wheelnum], "Blue Adjust  ", 14);
  else if (strcmp(WheelName, "Gre Adj  ") == 0) strncpy(mainWheelCommand[wheelnum], "Green Adjust  ", 15);
  else if (strcmp(WheelName, "Saturatn  ") == 0) strncpy(mainWheelCommand[wheelnum], "Saturation  ", 12);
  else strncpy(mainWheelCommand[wheelnum], WheelName, 25);//This array stores the full parameter names for posting them to the command line

  strncpy(mainWheelData[wheelnum], WheelData, 5);
  mainWheelType[wheelnum] = wheeltype;

  //Eventually the below will be a separate function so it can be maintained more easily, but for now,it's here
  //Some names suck on the 9 character screen because they are all the same - this fixes some

  if (strcmp(WheelName, "Gobo Select  ") == 0) strncpy(mainWheelNames[wheelnum], "Gobo Sel", 9);
  else if (strcmp(WheelName, "Gobo Select 2  ") == 0) strncpy(mainWheelNames[wheelnum], "Gobo Sel2", 9);
  else if (strcmp(WheelName, "Gobo Select 3  ") == 0) strncpy(mainWheelNames[wheelnum], "Gobo Sel3", 9);
  else if (strcmp(WheelName, "Gobo Ind/Spd  ") == 0) strncpy(mainWheelNames[wheelnum], "Gb In/Sp", 9);
  else if (strcmp(WheelName, "Gobo Ind/Spd 2  ") == 0) strncpy(mainWheelNames[wheelnum], "Gb In/Sp2", 9);
  else if (strcmp(WheelName, "Gobo Ind/Spd 3  ") == 0) strncpy(mainWheelNames[wheelnum], "Gb In/Sp3", 9);

  //This updates the displays for the respective parameters when they get there
  if (wheelnum == E3) updateE3NameDisplay = true;
  if (wheelnum == E4) updateE4NameDisplay = true;
  if (wheelnum == E5) updateE5NameDisplay = true;
  if (wheelnum == E6) updateE6NameDisplay = true;

  ParameterUpdateTime = millis();
}

void ParameterSorter()
{
  for (int i = 1; i < 7; i++) //Finds the first and last instance of each category of parameters
  {
    FirstParameter[i] = FindFirst(mainWheelType, i);
    if (FirstParameter[i] == 0) LastParameter[i] = FirstParameter[i];
    else LastParameter[i] = FindLast(mainWheelType, i);
  }
  NewParameterSort = true;//Boolean to let the Fader Bank Functions know the parameters have been refreshed
  if (SelectedChannel == true) EncoderBank(CurrentCategory);//This checks the active bank and calls the appropriate function
  EosParametersChanged = false;
}

int FindFirst(int Array[80], int Value)//This finds the first instance of a given character - used for the encoder categories
{
  for (int i = 1; i < 80; i++)
  {
    if (Array[i] == Value) return i;
  }
  return 0;//Returns 0 if there are no parameters in that category
}

int FindLast(int Array[80], int Value)//similar, but it finds the last.
{
  for (int i = 79; i >= 1; i--)
  {
    if (Array[i] == Value) return i;
  }
  return 0;//Returns 0 if there are no parameters in that category
}

void ChannelUpdate(OSCMessage& msg, int addressOffset)
{
  int lengthChan = msg.getDataLength(0);
  if (lengthChan == 0)  return;

  char Chantemp[lengthChan];
  msg.OSCMessage::getString(0, Chantemp, lengthChan);

  sscanf(Chantemp, "%[^[]" , ActiveChannel);  //Gets the channel up to the bracket

  char FirstCharacter[1];
  strncpy(FirstCharacter, Chantemp, 1);
  if (FirstCharacter[0] != '\0')
  {
    ParameterUpdateTime = millis();
    SelectedChannel = true;//This creates a boolean that only is true when a channel is selected - avoids when Eos send a blank active channel
    EosParametersChanged = true;
  }
  else
  {
    SelectedChannel = false;
    HueSatShift = 0;
    HueSatPresent[0] = false;
    HueSatPresent[1] = false;
    HueSatActive = 79;
  }
}

void SKUpdate(OSCMessage& msg, int addressOffset)
{

  int lengthSK = msg.getDataLength(0);
  if (lengthSK == 0 ) return;

  char SKtemp[lengthSK];
  msg.OSCMessage::getString(0, SKtemp, lengthSK);

  char SKtemp2[2];
  msg.getAddress(SKtemp2, addressOffset + 1);
  SKnum = atoi(SKtemp2);

  strncpy(Softkeys[SKnum], SKtemp, 9);
  if (SKnum == 2) updateSk1and2Display = true;
  if (SKnum == 6) updateSoftKeyDisplay = true;
  if (SKnum == 6) return;
}

/*Display code begins here*/
void CheckDisplays()
{
  if (updateE1andE2Display) E1andE2Display();
  if (updateSk1and2Display) SK1and2Display();
  if (updateEncoderDisplay) EncoderDisplay();
  if (updateSoftKeyDisplay) SoftKeyDisplay();
  if (updateE3NameDisplay) E3NameDisplay();
  if (updateE4NameDisplay) E4NameDisplay();
  if (updateE5NameDisplay) E5NameDisplay();
  if (updateE6NameDisplay) E6NameDisplay();
  if (updateE3DataDisplay) E3DataDisplay();
  if (updateE4DataDisplay) E4DataDisplay();
  if (updateE5DataDisplay) E5DataDisplay();
  if (updateE6DataDisplay) E6DataDisplay();
}

void E1andE2Display()//For updating the 4X20 display - top two lines only
{
  LeftLCD.setCursor(0, 0);
  LeftLCD.print("                    ");//Writes 20 spaces to clear the first line
  LeftLCD.setCursor(0, 1);
  LeftLCD.print("                    ");//Writes 20 spaces to clear the second line
  LeftLCD.setCursor(0, 0);
  if (HSISwap == false)
  {
    LeftLCD.print("   PAN ");
    LeftLCD.setCursor(10, 0);
    LeftLCD.print("   TILT ");
    LeftLCD.setCursor(2, 1);
    LeftLCD.print(enc1wheel.pos, SIG_DIGITS);
    LeftLCD.setCursor(12, 1);
    LeftLCD.print(enc2wheel.pos, SIG_DIGITS);
  }
  else
  {
    LeftLCD.print("   HUE ");
    LeftLCD.setCursor(10, 0);
    LeftLCD.print("   SAT ");
    LeftLCD.setCursor(2, 1);
    LeftLCD.print(huewheel.pos, SIG_DIGITS);
    LeftLCD.setCursor(12, 1);
    LeftLCD.print(satwheel.pos, SIG_DIGITS);
  }
  updateE1andE2Display = false;
}
void SK1and2Display()//For updating the 4X20 display - fourth line only
{
  LeftLCD.setCursor(0, 3);
  LeftLCD.print("                    ");//Writes 20 spaces to clear the fourth line
  LeftLCD.setCursor(0, 3);
  LeftLCD.print(Softkeys [1]);
  LeftLCD.setCursor(9, 3);
  LeftLCD.print("|");
  LeftLCD.setCursor(10, 3);
  LeftLCD.print(Softkeys [2]);
  updateSk1and2Display = false;
}

void E3NameDisplay()//Updates the name and value for Encoder 1 only
{
  if  (!SelectedChannel) updateE3NameDisplay = false;
  else
  {
    RightTopLCD.setCursor(0, 0);
    RightTopLCD.print("         ");
    RightTopLCD.setCursor(0, 0);
    RightTopLCD.print(mainWheelNames[E3]);
    RightTopLCD.setCursor(0, 1);
    RightTopLCD.print("         ");
    RightTopLCD.setCursor(0, 1);
    RightTopLCD.print(mainWheelData[E3]);
    updateE3NameDisplay = false;
  }
}

void E4NameDisplay()//Updates the name and value for Encoder 2 only
{
  if  (!SelectedChannel) updateE4NameDisplay = false;
  else
  {
    RightTopLCD.setCursor(10, 0);
    RightTopLCD.print("         ");
    RightTopLCD.setCursor(10, 0);
    RightTopLCD.print(mainWheelNames[E4]);
    RightTopLCD.setCursor(10, 1);
    RightTopLCD.print("         ");
    RightTopLCD.setCursor(10, 1);
    RightTopLCD.print(mainWheelData[E4]);
    updateE4NameDisplay = false;
  }
}

void E5NameDisplay()//Updates the name and value for Encoder 3 only
{
  if  (!SelectedChannel) updateE5NameDisplay = false;
  else
  {
    RightTopLCD.setCursor(20, 0);
    RightTopLCD.print("         ");
    RightTopLCD.setCursor(20, 0);
    RightTopLCD.print(mainWheelNames[E5]);
    RightTopLCD.setCursor(20, 1);
    RightTopLCD.print("         ");
    RightTopLCD.setCursor(20, 1);
    RightTopLCD.print(mainWheelData[E5]);
    updateE5NameDisplay = false;
  }
}

void E6NameDisplay()//Updates the name and value for Encoder 4 only
{
  if  (!SelectedChannel) updateE6NameDisplay = false;
  else
  {
    RightTopLCD.setCursor(30, 0);
    RightTopLCD.print("          ");
    RightTopLCD.setCursor(30, 0);
    RightTopLCD.print(mainWheelNames[E6]);
    RightTopLCD.setCursor(30, 1);
    RightTopLCD.print("          ");
    RightTopLCD.setCursor(30, 1);
    RightTopLCD.print(mainWheelData[E6]);
    updateE6NameDisplay = false;
  }
}

void E3DataDisplay()//Updates the value for Encoder 1 only
{
  RightTopLCD.setCursor(0, 1);
  RightTopLCD.print("         ");
  RightTopLCD.setCursor(0, 1);
  RightTopLCD.print(mainWheelData[E3]);
  updateE3DataDisplay = false;
}

void E4DataDisplay()//Updates the value for Encoder 2 only
{
  RightTopLCD.setCursor(10, 1);
  RightTopLCD.print("         ");
  RightTopLCD.setCursor(10, 1);
  RightTopLCD.print(mainWheelData[E4]);
  updateE4DataDisplay = false;
}

void E5DataDisplay()//Updates the value for Encoder 3 only
{
  RightTopLCD.setCursor(20, 1);
  RightTopLCD.print("         ");
  RightTopLCD.setCursor(20, 1);
  RightTopLCD.print(mainWheelData[E5]);
  updateE5DataDisplay = false;
}

void E6DataDisplay()//Updates the value for Encoder 4 only
{
  RightTopLCD.setCursor(30, 1);
  RightTopLCD.print("          ");
  RightTopLCD.setCursor(30, 1);
  RightTopLCD.print(mainWheelData[E6]);
  updateE6DataDisplay = false;
}

void EncoderDisplay()
{
  if (SelectedChannel && (mainWheelNames[1][0] == ' '))
  {
    RightTopLCD.clear();
    RightTopLCD.setCursor(0, 0);
    RightTopLCD.print(ErrorMessageLine1[0]);
    RightTopLCD.setCursor(0, 1);
    RightTopLCD.print(ErrorMessageLine2[0]);
    updateEncoderDisplay = false;
  }
  else if  (!SelectedChannel) updateEncoderDisplay = false;
  else
  {
    RightTopLCD.clear();
    RightTopLCD.setCursor(0, 0);
    if (NoParameters[CurrentCategory])
    {
      RightTopLCD.print(ErrorMessageLine1[CurrentCategory]);
      RightTopLCD.setCursor(0, 1);
      RightTopLCD.print(ErrorMessageLine2[CurrentCategory]);
    }
    else
    {
      RightTopLCD.print(mainWheelNames[E3]);
      RightTopLCD.setCursor(9, 0);
      RightTopLCD.print("|");
      RightTopLCD.setCursor(10, 0);
      RightTopLCD.print(mainWheelNames[E4]);
      RightTopLCD.setCursor(19, 0);
      RightTopLCD.print("|");
      RightTopLCD.setCursor(20, 0);
      RightTopLCD.print(mainWheelNames[E5]);
      RightTopLCD.setCursor(29, 0);
      RightTopLCD.print("|");
      RightTopLCD.setCursor(30, 0);
      RightTopLCD.print(mainWheelNames[E6]);
      RightTopLCD.setCursor(0, 1);
      RightTopLCD.print(mainWheelData[E3]);
      RightTopLCD.setCursor(9, 1);
      RightTopLCD.print("|");
      RightTopLCD.setCursor(10, 1);
      RightTopLCD.print(mainWheelData[E4]);
      RightTopLCD.setCursor(19, 1);
      RightTopLCD.print("|");
      RightTopLCD.setCursor(20, 1);
      RightTopLCD.print(mainWheelData[E5]);
      RightTopLCD.setCursor(29, 1);
      RightTopLCD.print("|");
      RightTopLCD.setCursor(30, 1);
      RightTopLCD.print(mainWheelData[E6]);
    }
    updateEncoderDisplay = false;
  }
}

void SoftKeyDisplay()//Writes softkey labels
{
  RightBottomLCD.setCursor(0, 1);
  RightBottomLCD.print("                                        ");//This adds 40 spaces to clearn line 4 of the 4x40 display
  RightBottomLCD.setCursor(0, 1);
  RightBottomLCD.print(Softkeys [3]);
  RightBottomLCD.setCursor(9, 1);
  RightBottomLCD.print("|");
  RightBottomLCD.setCursor(10, 1);
  RightBottomLCD.print(Softkeys [4]);
  RightBottomLCD.setCursor(19, 1);
  RightBottomLCD.print("|");
  RightBottomLCD.setCursor(20, 1);
  RightBottomLCD.print(Softkeys [5]);
  RightBottomLCD.setCursor(29, 1);
  RightBottomLCD.print("|");
  RightBottomLCD.setCursor(30, 1);
  RightBottomLCD.print(Softkeys [6]);
  updateSoftKeyDisplay = false;
}

void initEncoder(struct Encoder* encoder, uint8_t pinA, uint8_t pinB, uint8_t buttonPin, uint8_t direction)
{
  encoder->pinA = pinA;
  encoder->pinB = pinB;
  encoder->buttonPin = buttonPin;
  encoder->pos = 0;
  encoder->direction = direction;

  pinMode(pinA, INPUT_PULLUP);
  pinMode(pinB, INPUT_PULLUP);
  pinMode(buttonPin, INPUT_PULLUP);

  encoder->pinAPrevious = digitalRead(pinA);
  encoder->pinBPrevious = digitalRead(pinB);
  encoder->buttonState = digitalRead(buttonPin);
}

int8_t updateEncoder(struct Encoder* encoder)
{
  int8_t encoderMotion = 0;
  int pinACurrent = digitalRead(encoder->pinA);
  int pinBCurrent = digitalRead(encoder->pinB);

  if (encoder->pinAPrevious != pinACurrent)
  {
    if (encoder->pinAPrevious == encoder->pinBPrevious) encoderMotion = -1;
    else encoderMotion = 1;
    if (encoder->direction == REVERSE_) encoderMotion = -encoderMotion;// If we are in reverse mode, flip the direction of the encoder motion
    delay (keyDelay);
  }
  encoder->pinAPrevious = pinACurrent;
  encoder->pinBPrevious = pinBCurrent;
  return encoderMotion;
}

void sendWheelMove(int Enc, float ticks)
{
  if ((Enc == 3 && E3 == 0) || (Enc == 4 && E4 == 0) || (Enc == 5 && E5 == 0) || (Enc == 6 && E6 == 0)) return; //Checks to make sure you're not sending a non-active wheel

  String wheelMsg(EOS_WHEEL);

  if (Enc == 1 || Enc == 2) wheelMsg.concat("/wheel");
  else wheelMsg.concat("/active/wheel");

  if (digitalRead(SHIFT_BTN) == LOW) wheelMsg.concat("/fine");
  else wheelMsg.concat("/coarse");

  if (Enc == 1 && HSISwap == false) wheelMsg.concat("/pan");
  else if (Enc == 1 && HSISwap == true) wheelMsg.concat("/hue");
  else if (Enc == 2 && HSISwap == false) wheelMsg.concat("/tilt");
  else if (Enc == 2 && HSISwap == true) wheelMsg.concat("/saturation");
  else wheelMsg.concat("/");

  if (Enc == 3) wheelMsg.concat(E3);
  else if (Enc == 4) wheelMsg.concat(E4);
  else if (Enc == 5) wheelMsg.concat(E5);
  else if (Enc == 6) wheelMsg.concat(E6);

  OSCMessage wheelUpdate(wheelMsg.c_str());
  wheelUpdate.add(ticks);
  SLIPSerial.beginPacket();
  wheelUpdate.send(SLIPSerial);
  SLIPSerial.endPacket();
}

void sendKeyPress(bool press, String key)
{
  key = EOS_KEY + key;
  OSCMessage keyMsg(key.c_str());
  if (press) keyMsg.add(EDGE_DOWN);
  else keyMsg.add(EDGE_UP);
  SLIPSerial.beginPacket();
  keyMsg.send(SLIPSerial);
  SLIPSerial.endPacket();
  delay (keyDelay);
}

void sendCmdLine(String cmd)
{
  cmd.replace(" ", "_");//Replaces spaces with underscores for Entry as a command
  cmd.replace("__", "  ");//Puts two spaces at the end, so Nomad knows it's a new key press
  if (shiftMode) //This is used to switch the mode on index/rotation and other wheels from select to wheel mode
  {
    if (cmd.indexOf("Shutter_Strobe") != -1) cmd.replace("Shutter_Strobe", "Strobe_Mode");//Replaces the command for Strobe_Mode as it handles this different...
    cmd.replace("Select", "Wheel_Mode");
    if (cmd.indexOf("_Mode") != -1) cmd = cmd + "Next";//If it made the replacement it adds the word next to it
  }
  OSCMessage cmdMsg(EOS_CMD.c_str());
  cmdMsg.add(cmd.c_str());
  SLIPSerial.beginPacket();
  cmdMsg.send(SLIPSerial);
  SLIPSerial.endPacket();
  delay (keyDelay);
}

void EncoderBank(int Category)
{
  if (LastParameter[Category] == 0)
  {
    NoParameters[Category] = true;
    E3 = E4 = E5 = E6 = 0;
  }
  else
  {
    NoParameters[Category] = false;
    if (CurrentCategory == Category && NewParameterSort == false)
    {
      E3 = E3 + 4;
      if (Category == 3 && (HueSatPresent[0] || HueSatPresent[1]))//Special case for color and HueSat beingPresent
      {
        if ((E3 - 4) == HueSatActive) E3 = FirstParameter[3];//Needed anymore?? This prevents us from getting stuck on Hue Sat when they are on their own page...
        if ((E3 >= HueSatActive) && ((E3 - HueSatActive) / 4) < 1) E3 = E3 + HueSatShift;
        if (E3 > LastParameter[3])
        {
          if (E3 > (LastParameter[3] + HueSatShift)) E3 = FirstParameter[3];
          else E3 = HueSatActive;
        }
      }
      else if (E3 > LastParameter[Category]) E3 = FirstParameter[Category];
      CurrentParameterPage[Category] = ((E3 - FirstParameter[Category]) / 4); //This helps to get it to go back to the page you left it on.
    }
    else
    {
      NewParameterSort = false;//This gets thrown when we run the sort function, so we clear the boolean
      E3 = FirstParameter[Category] + (4 * CurrentParameterPage[Category]);
      if ((Category == 3) && (HueSatPresent[0] || HueSatPresent[1]) && E3 >= HueSatActive)//It's color, Hue or Sat is present, and we're at or past their point
      {
        E3 = FirstParameter[3] + (4 * CurrentParameterPage[3]) + HueSatShift;
        if (E3 > LastParameter[3])
        {
          if ((E3 - LastParameter[3]) <= HueSatShift) E3 = HueSatActive;
          else E3 = FirstParameter[3];
        }
      }
      else if (Category == 3 && (HueSatPresent[0] || HueSatPresent[1]) && (CurrentParameterPage[3] == ((LastParameter[3] - FirstParameter[3] + 1) / 4))) E3 = HueSatActive; //Special Case for when we are on a HueSat only page)
      if (mainWheelType[E3] != Category)
      {
        E3 = FirstParameter[Category];
        CurrentParameterPage[Category] = 0;
      }
    }
    if (Category == 3 && (HueSatPresent[0] || HueSatPresent[1]))//Special case for color category and Hue Sat present
    {
      if (E3 == HueSatActive)
      {
        CurrentParameterPage[3] = ((LastParameter[3] - FirstParameter[3] + 1) / 4);
        if (HueSatShift == 2) E4 = HueSatActive + 1;
        else E4 = 0;
        E5 = E6 = 0;
      }
      else if ((HueSatShift == 2) && (E3 == HueSatActive + 1))//special case for when there is Hue Sat and Sat is the first encoder
      {
        E3 = E3 + 1;
        if (E3 > LastParameter[3]) E3 = HueSatActive;
        E4 = E3 + 1;
        E5 = E6 = 0;
      }
      else
      {
        E4 = E3 + 1;
        if (E4 > LastParameter[3])
        {
          E4 = HueSatActive;
          if (HueSatShift == 2) E5 = HueSatActive + 1;
          else E5 = 0;
          E6 = 0;
        }
        else
        {
          E5 = E4 + 1;
          if (E5 > LastParameter[3])
          {
            E5 = HueSatActive;
            if (HueSatShift == 2) E6 = HueSatActive + 1;
            else E6 = 0;
          }
          else
          {
            E6 = E5 + 1;
            if (E6 == HueSatActive) E6 = E6 + HueSatShift;
            if (E6 > LastParameter[3])
            {
              if (HueSatShift == 1) E6 = HueSatActive;
              else E6 = 0;
            }
          }
        }
      }
    }
    else if (E3 == LastParameter[Category]) E4 = E5 = E6 = 0;
    else
    {
      E4 = E3 + 1;
      if (E4 == LastParameter[Category]) E5 = E6 = 0;
      else
      {
        E5 = E4 + 1;
        if (E5 == LastParameter[Category]) E6 = 0;
        else E6 = E5 + 1;
      }
    }
  }
  CurrentCategory = Category;
  updateEncoderDisplay = true;
}

void checkCategoryButtons(int ParameterButton)
{
  if (digitalRead(CategoryButton[ParameterButton]) != CategoryButtonState[ParameterButton])
  {
    if (CategoryButtonState[ParameterButton] == LOW)
    {
      CategoryButtonState[ParameterButton] = HIGH;
      if (digitalRead(SHIFT_BTN) == LOW) sendKeyPress(false, CategoryKeyCommand[ParameterButton]);
      else sendKeyPress (false, CategoryKeyMessages[ParameterButton]);
    }
    else
    {
      CategoryButtonState[ParameterButton] = LOW;
      if (digitalRead(SHIFT_BTN) == LOW) sendKeyPress(true, CategoryKeyCommand[ParameterButton]);
      else
      {
        EncoderBank(ParameterButton);
        sendKeyPress (true, CategoryKeyMessages[ParameterButton]);
      }
    }
  }
}

void checkButtons(int b)
{
  if (digitalRead (Buttons[b]) != ButtonState[b])//There's been a change to a button
  {
    if (b < 7)//For SoftKeys and More Softkeys
    {
      if (shiftMode)
      {
        if (b < 6) dim = LEDLevel[b];
        else if (ButtonState[b])
        {
          HSISwap = !HSISwap;
          updateE1andE2Display = true;
        }
      }
      else sendKeyPress (ButtonState[b], KeyMessages[b]);
    }
    if (b == 7)
    {
      shiftMode = ButtonState[b];//Shift has changed, and the shiftmode is equal to the previous state (True for High, False for Low)
      sendKeyPress (ButtonState[b], KeyMessages[b]);//New add of sending the shift key, so that it can work with the KeyPad
    }
    if (b == 8 || b == 9) if (ButtonState[b]) sendCmdLine (KeyMessages[b + (2 * HSISwap)]); //For Enc1 and Enc2
    if (b == 10) if (ButtonState[b]) sendCmdLine (mainWheelCommand[E3]);
    if (b == 11) if (ButtonState[b]) sendCmdLine (mainWheelCommand[E4]);
    if (b == 12) if (ButtonState[b]) sendCmdLine (mainWheelCommand[E5]);
    if (b == 13) if (ButtonState[b]) sendCmdLine (mainWheelCommand[E6]);
    ButtonState[b] = !ButtonState[b];
  }
}

void InitDisplays()
{
  LeftLCD.setCursor(3, 2);
  LeftLCD.print("S1");
  LeftLCD.setCursor(9, 2);
  LeftLCD.print("|");
  LeftLCD.setCursor(13, 2);
  LeftLCD.print("S2");
  RightTopLCD.setCursor(9, 0);
  RightTopLCD.print("|");
  RightTopLCD.setCursor(19, 0);
  RightTopLCD.print("|");
  RightTopLCD.setCursor(29, 0);
  RightTopLCD.print("|");
  RightTopLCD.setCursor(9, 1);
  RightTopLCD.print("|");
  RightTopLCD.setCursor(19, 1);
  RightTopLCD.print("|");
  RightTopLCD.setCursor(29, 1);
  RightTopLCD.print("|");
  RightBottomLCD.setCursor(3, 0);
  RightBottomLCD.print("S3");
  RightBottomLCD.setCursor(9, 0);
  RightBottomLCD.print("|");
  RightBottomLCD.setCursor(13, 0);
  RightBottomLCD.print("S4");
  RightBottomLCD.setCursor(19, 0);
  RightBottomLCD.print("|");
  RightBottomLCD.setCursor(23, 0);
  RightBottomLCD.print("S5");
  RightBottomLCD.setCursor(29, 0);
  RightBottomLCD.print("|");
  RightBottomLCD.setCursor(33, 0);
  RightBottomLCD.print("S6");
}

void setup()
{
  SLIPSerial.begin(2000000);
  LeftLCD.begin(LCD_CHARS, LCD_LINES);
  LeftLCD.clear();
  RightTopLCD.begin(LCD2_CHARS, LCD2_LINES);
  RightTopLCD.clear();
  RightBottomLCD.begin(LCD3_CHARS, LCD3_LINES);
  RightBottomLCD.clear();

  SplashScreen();
  InitDisplays();
  E1andE2Display();
  SK1and2Display();
  SoftKeyDisplay();

  initEncoder(&enc1wheel, 0, 1, 2, ENC1_DIR);
  initEncoder(&enc2wheel, 3, 4, 5, ENC2_DIR);
  initEncoder(&enc3wheel, 6, 7, 8, ENC3_DIR);
  initEncoder(&enc4wheel, 9, 10, 11, ENC4_DIR);
  initEncoder(&enc5wheel, 12, 24, 25, ENC5_DIR);
  initEncoder(&enc6wheel, 26, 27, 28, ENC6_DIR);

  pinMode(INTENSITY_BTN, INPUT_PULLUP);
  pinMode(FOCUS_BTN, INPUT_PULLUP);
  pinMode(COLOR_BTN, INPUT_PULLUP);
  pinMode(IMAGE_BTN, INPUT_PULLUP);
  pinMode(FORM_BTN, INPUT_PULLUP);
  pinMode(SHUTTER_BTN, INPUT_PULLUP);
  pinMode(SHIFT_BTN, INPUT_PULLUP);
  pinMode(SOFTKEY_1, INPUT_PULLUP);
  pinMode(SOFTKEY_2, INPUT_PULLUP);
  pinMode(SOFTKEY_3, INPUT_PULLUP);
  pinMode(SOFTKEY_4, INPUT_PULLUP);
  pinMode(SOFTKEY_5, INPUT_PULLUP);
  pinMode(SOFTKEY_6, INPUT_PULLUP);
  pinMode(MORE_SOFTKEYS, INPUT_PULLUP);

  E1andE2Display();
  SK1and2Display();
  SoftKeyDisplay();
}

void SplashScreen()
{
  char Splash[4][41]  = {"   || |--- |---|  /---   OSC BASED NOMAD",
                         "   || |__  || || |__           INTERFACE",
                         "|| || |    || ||    | C2020 JOE BEUMER &",
                         "|___| |___ |___| ___/     JEREMY DOMINIK"
                        };
  RightTopLCD.clear();
  RightBottomLCD.clear();
  for (int s = 1; s < 41; s++)//Wipes the screen in from right and left
  {
    RightTopLCD.setCursor((40 - s), 0);
    for (int x = 0; x < s; x++) RightTopLCD.print(Splash[0][x]);
    RightTopLCD.setCursor(0, 1);
    for (int x = 0; x < s; x++) RightTopLCD.print(Splash[1][40 - s + x]);
    RightBottomLCD.setCursor((40 - s), 0);
    for (int x = 0; x < s; x++) RightBottomLCD.print(Splash[2][x]);
    RightBottomLCD.setCursor(0, 1);
    for (int x = 0; x < s; x++) RightBottomLCD.print(Splash[3][40 - s + x]);
    delay(50);
  }
  delay(1500);

  for (int s = 0; s < 28; s ++)//Wipes away the logo
  {
    if (s < 22)
    {
      RightTopLCD.setCursor(s, 0);
      RightTopLCD.print(" ");
    }
    if ((s > 1) && (s < 24))
    {
      RightTopLCD.setCursor((s - 2), 1);
      RightTopLCD.print(" ");
    }
    if ((s > 3) && (s < 26))
    {
      RightBottomLCD.setCursor((s - 4), 0);
      RightBottomLCD.print(" ");
    }
    if (s > 5)
    {
      RightBottomLCD.setCursor((s - 6), 1);
      RightBottomLCD.print(" ");
    }
    delay(30);
  }

  for (int s = 0; s < 46; s ++)//Slides the TM Statement to the Right Screen
  {
    if (s < 22)
    {
      RightTopLCD.setCursor(21 - s, 0);
      for (int x = 22; x < 40; x++) RightTopLCD.print(Splash[0][x]);
      for (int x = 0; x <= s; x++) RightTopLCD.print(" ");
      RightTopLCD.setCursor(21 - s, 1);
      for (int x = 22; x < 40; x++) RightTopLCD.print(Splash[1][x]);
      for (int x = 0; x <= s; x++) RightTopLCD.print(" ");
      RightBottomLCD.setCursor(21 - s, 0);
      for (int x = 22; x < 40; x++) RightBottomLCD.print(Splash[2][x]);
      for (int x = 0; x <= s; x++) RightBottomLCD.print(" ");
      RightBottomLCD.setCursor(21 - s, 1);
      for (int x = 22; x < 40; x++) RightBottomLCD.print(Splash[3][x]);
      for (int x = 0; x <= s; x++) RightBottomLCD.print(" ");
    }
    else if (s < 44)
    {
      if (s < 40)
      {
        RightTopLCD.setCursor(0, 0);
        for (int x = s + 1; x < 40; x++) RightTopLCD.print(Splash[0][x]);
        for (int x = 0; x <= s; x++) RightTopLCD.print(" ");
        RightTopLCD.setCursor(0, 1);
        for (int x = s + 1; x < 40; x++) RightTopLCD.print(Splash[1][x]);
        for (int x = 0; x <= s; x++) RightTopLCD.print(" ");
        RightBottomLCD.setCursor(0, 0);
        for (int x = s + 1; x < 40; x++) RightBottomLCD.print(Splash[2][x]);
        for (int x = 0; x <= s; x++) RightBottomLCD.print(" ");
        RightBottomLCD.setCursor(0, 1);
        for (int x = s + 1; x < 40; x++) RightBottomLCD.print(Splash[3][x]);
        for (int x = 0; x <= s; x++) RightBottomLCD.print(" ");
      }
      if (s > 25)
      {
        for (int y = 0; y < 4; y++) //Iterates all 4 lines on the right column
        {
          LeftLCD.setCursor(45 - s, y);
          for (int x = 0; x + 25 < s; x++) LeftLCD.print(Splash[y][x + 22]);
        }
      }
    }
    else//Finishes the last two steps of the slide on the right side
    {
      for (int y = 0; y < 4; y++) //Iterates all 4 lines on the right column
      {
        LeftLCD.setCursor(45 - s, y);
        for (int x = 0; x + 27 < s; x++) LeftLCD.print(Splash[y][x + 22]);
        LeftLCD.print("  ");
      }
    }
    delay(60);
  }
  for (int p = 0; p < 9; p++)
  {
    if (p < 3)
    {
      LeftLCD.setCursor(0, 0);
      for (int x = 0; x < (17 - p); x++) LeftLCD.print(Splash[0][x + 23 + p]);
      LeftLCD.print(" ");
    }
    LeftLCD.setCursor(0, 1);
    for (int x = 0; x < (17 - p); x++) LeftLCD.print(Splash[1][x + 23 + p]);
    LeftLCD.print(" ");
    if (p < 4)
    {
      LeftLCD.setCursor(0, 3);
      for (int x = 0; x < (17 - p); x++) LeftLCD.print(Splash[3][x + 23 + p]);
      LeftLCD.print(" ");
    }
    delay(60);
  }
  delay(690);//Now slide everything down - because we're in different parts of each array, this seems easiest...
  LeftLCD.setCursor(0, 0);
  LeftLCD.print("               ");
  LeftLCD.setCursor(0, 1);
  for (int x = 25; x < 40; x++) LeftLCD.print(Splash[0][x]);
  LeftLCD.print("     ");
  LeftLCD.setCursor(0, 2);
  for (int x = 31; x < 40; x++) LeftLCD.print(Splash[1][x]);
  LeftLCD.print("         ");
  LeftLCD.setCursor(0, 3);
  for (int x = 22; x < 40; x++) LeftLCD.print(Splash[2][x]);
  delay(250);
  LeftLCD.setCursor(0, 1);
  LeftLCD.print("               ");
  LeftLCD.setCursor(0, 2);
  for (int x = 25; x < 40; x++) LeftLCD.print(Splash[0][x]);
  LeftLCD.print("     ");
  LeftLCD.setCursor(0, 3);
  for (int x = 31; x < 40; x++) LeftLCD.print(Splash[1][x]);
  LeftLCD.print("         ");
  delay(250);
  LeftLCD.setCursor(0, 2);
  LeftLCD.print("               ");
  LeftLCD.setCursor(0, 3);
  for (int x = 25; x < 40; x++) LeftLCD.print(Splash[0][x]);
  LeftLCD.print("     ");
  delay(250);
  LeftLCD.setCursor(0, 3);
  LeftLCD.print("               ");
  delay(750);
}

void loop()
{
  analogWrite (LED, dim);
  static String curMsg;
  Encoder* EncoderWheels[7] = {0, &enc1wheel, &enc2wheel, &enc3wheel, &enc4wheel, &enc5wheel, &enc6wheel};
  for (int e = 1; e < 7; e++) EncoderMotion[e] = updateEncoder(EncoderWheels[e]);//Read the encoders
  for (int c = 1; c < 7; c++) checkCategoryButtons(c); //Check all 6 of the category buttons
  for (int b = 0; b < 14; b++) checkButtons(b); //Check the other 14 buttons
  for (int e = 1; e < 7; e++) if (EncoderMotion[e] != 0) sendWheelMove(e, EncoderMotion[e]);//If the Encoder has moved, update the value
  int size = SLIPSerial.available();  //Check for incoming OSC
  if (size > 0)
    while (size--)
      curMsg += (char)(SLIPSerial.read());
  if (SLIPSerial.endofPacket())
  {
    parseOSCMessage(curMsg);
    curMsg = String();
  }
  if (EosParametersChanged == true)
  {
    if ((millis() - ParameterUpdateTime) >= ParamDelay)//If there are new parameters, and it's been more than 10 milliseconds, it will run the parameter counter functions
    {
      ParameterSorter();
    }
  }
  CheckDisplays();
}