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.
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();
}