Hello, I am working on a network module to control a high-power VFD motor system. Until now, everything in the prototyping stage has worked, although I had to step away from the project for a year and a half. Now that I am implementing the first version of this device, I have encountered the problem of not being able to find a suitable rotary encoder at a fair price. Therefore, I am considering replacing the encoder with a 6-pin rocker switch. Given that the code is around 5000 lines and my peers are no longer involved in the project, it seems easier to adapt the rocker switch to perform the same function as the rotary encoder, which is simply scrolling through the menu.
If anyone has suggestions on how to adapt the code, that would be greatly appreciated. Currently, I am using the CLK pin for the RockerUp pin, the Data pin for the RockerDown pin, and I added an extra push button to simulate the select function. I also had to add some debouncing. At the moment, the code only scrolls in one direction (Up), but pressing the rocker down does not scroll in the opposite direction. I believe this is related to the CW and CCW logic in the code, specifically in the Rotate() function, but I am not entirely sure.
I couldn’t attach the entire code due to its length, but I've included some functions that relate to the CW and CCW logic, which I think might be causing the issue.
// LCD Buttons
#define rockerUpPin 4
#define rockerDownPin 11
#define SelectButton A2
#define BackButton 12
#define MenuButton A5
#define PowerFailure A1
void Rotate();
int previousMenuPosition = 0;
int menuPosition = 0;
bool ESC = false;
bool updateMenu = false;
bool refreshLCD = false;
bool offsetActive = false;
float tempVal = 0;
// LCD Encoder
int previousCLKDown;
int currentCLKDown;
// Menu Buttons (Interrupts)
bool encoderCW;
bool encoderCCW;
bool selectPressed = false;
// Menu Buttons (no interrupts)
bool backPress;
bool previousBackPress;
unsigned long lastDebounceTimeUp = 0;
unsigned long lastDebounceTimeDown = 0;
const unsigned long debounceDelay = 50; // 50ms debounce dela
void setup(){
// Initialize LCD Menu Buttons
pinMode(PowerFailure, INPUT); // PowerFailure
pinMode(rockerUpPin, INPUT_PULLUP); // rockerUpPin
pinMode(rockerDownPin, INPUT_PULLUP); // rockerDownPin
pinMode(SelectButton, INPUT_PULLUP); // SelectButton
pinMode(BackButton, INPUT_PULLUP); // BackButton
pinMode(MenuButton, INPUT_PULLUP); // MenuButton
pinMode(30, INPUT); // SD card chip detect pin
// Prepare previous state values for the first run through
previousCLKUp = digitalRead(rockerUpPin); // Initial state of rockerUpPin
previousCLKDown = digitalRead(rockerDownPin); // Initial state of rockerDownPin
backPress = !digitalRead(BackButton); // Invert value for active low
// Set Interrupts for the rotary encoder and select button on the encoder
attachInterrupt(digitalPinToInterrupt(rockerUpPin), Rotate, CHANGE); // Interrupt for rockerUpPin
attachInterrupt(digitalPinToInterrupt(rockerDownPin), Rotate, CHANGE); // Interrupt for rockerDownPin
attachInterrupt(digitalPinToInterrupt(SelectButton), SelectPressed, FALLING); // Interrupt for SelectButton
attachInterrupt(digitalPinToInterrupt(PowerFailure), PowerLoss, FALLING); // Interrupt for PowerFailure
clearScreen(); // Clear the LCD screen
}
// Main menu *****************************************************************
void MainMenu() {
int menuLength = 6;
menuPosition = 1;
ESC = false;
clearScreen();
updateMenu = true;
while (!ESC) {
previousMenuPosition = menuPosition;
if (encoderCW) {
if (menuPosition >= menuLength) {
menuPosition = 1;
} else {
menuPosition++;
}
encoderCW = false;
updateMenu = true;
} else if (encoderCCW) {
if (menuPosition <= 1) {
menuPosition = menuLength;
} else {
menuPosition--;
}
encoderCCW = false;
updateMenu = true;
}
if ((previousMenuPosition == 4 && menuPosition == 3) || (previousMenuPosition == menuLength && menuPosition == 1) || (previousMenuPosition == 3 && menuPosition == 4) || (previousMenuPosition == 1 && menuPosition == menuLength)) {
refreshLCD = true; // Checking to see if the menu has scrolled
}
if (updateMenu) {
if (refreshLCD) {
clearScreen();
refreshLCD = false;
}
if (menuPosition < 4) {
setCursor(0x04);
Serial1.print("-Main Menu-");
setCursor(0x41);
Serial1.print("Toggle Offset:");
setCursor(0x40 + 17);
if (offsetActive) {
Serial1.print(" ON");
} else {
Serial1.print("OFF");
}
setCursor(0x15);
Serial1.print("Ethernet Info");
setCursor(0x55);
Serial1.print("Cable Settings");
} else if (menuPosition > 3) {
setCursor(0x01);
Serial1.print("Drum Settings");
setCursor(0x41);
Serial1.print("Set Units:");
setCursor(0x40 + 16);
if (metric) {
Serial1.print(" m");
} else if (imperial) {
Serial1.print("ft");
}
if (seconds) {
Serial1.print("/s");
} else if (minutes) {
Serial1.print("/m");
}
setCursor(0x15);
Serial1.print("NMEA Period: ");
LCDPrintSeconds(NMEAPeriod, 0x14);
}
DrawCursor();
updateMenu = false;
}
if (selectPressed) {
switch (menuPosition) {
case 1:
LCDToggleOffset(1);
if (sdCard) {
drumFile = SD.open("DRMCONST.TXT", (O_READ | O_WRITE));
drumFile.find("offset:");
drumFile.print(offset, 1);
drumFile.print(',');
drumFile.close();
}
break;
case 2:
LCDEthernetInfo(2);
break;
case 3:
LCDCableSettings(3);
break;
case 4:
LCDDrumSettings(4);
break;
case 5:
LCDSetUnits(5);
if (sdCard) {
drumFile = SD.open("DRMCONST.TXT", (O_READ | O_WRITE));
drumFile.find("units:");
drumFile.position();
if (metric) {
drumFile.print('M');
} else if (imperial) {
drumFile.print('I');
}
if (seconds) {
drumFile.print('s');
} else if (minutes) {
drumFile.print('m');
}
drumFile.print(",");
drumFile.close();
}
updateLimits = true;
break;
case 6:
NMEAPeriod = LCDSetNMEAPeriod(0x14);
if (sdCard) {
tempVal = NMEAPeriod / 1000.0;
drumFile = SD.open("DRMCONST.TXT", (O_READ | O_WRITE));
drumFile.find("NMEAPeriod:");
drumFile.position();
drumFile.print(tempVal, 1);
drumFile.print(",");
drumFile.close();
}
break;
}
selectPressed = false;
}
previousBackPress = backPress;
backPress = !digitalRead(BackButton); // Inverting reading because of active low button
if (backPress && (backPress != previousBackPress)) {
ESC = true;
delay(50);
}
}
}
// Cursor *****************************************************************
void DrawCursor() {
//Clear display's ">" parts
setCursor(0x0); //1st line, 1st block
Serial1.print(" "); //erase by printing a space
setCursor(0x40);
Serial1.print(" ");
setCursor(0x14);
Serial1.print(" ");
setCursor(0x54);
Serial1.print(" ");
//Place cursor to the new position
switch (menuPosition) //this checks the value of the menu
{
case 0:
case 4:
setCursor(0x0); //1st line, 1st block
Serial1.print(">");
break;
//-------------------------------
case 1:
case 5:
setCursor(0x40); //2nd line, 1st block
Serial1.print(">");
break;
//-------------------------------
case 2:
case 6:
setCursor(0x14); //3rd line, 1st block
Serial1.print(">");
break;
//-------------------------------
case 3:
case 7:
setCursor(0x54); //4th line, 1st block
Serial1.print(">");
break;
}
}
void LCDEthernetInfo(int menuReturn) {
selectPressed = false;
ESC = false;
clearScreen();
setCursor(0x02);
Serial1.print("-Ethernet Info-");
setCursor(0x40);
if ((Ethernet.localIP() == error0IP) || (Ethernet.localIP() == error1IP)) {
Serial1.print("No IP Connection");
} else {
Serial1.print(Ethernet.localIP());
}
setCursor(0x14);
if (!manualState) {
Serial1.print("Wireless Control");
} else {
Serial1.print("Local Mode");
}
setCursor(0x54);
if (DHCPError) {
Serial1.print("DHCP Error!");
}
while (!ESC) {
previousBackPress = backPress;
backPress = !digitalRead(BackButton);
if (backPress && (backPress != previousBackPress)) {
ESC = true;
delay(50);
}
}
encoderCW = false;
encoderCCW = false;
ESC = false;
updateMenu = true;
refreshLCD = true;
menuPosition = menuReturn;
}
void LCDCableSettings(int menuReturn) {
int menuLength = 6;
selectPressed = false;
menuPosition = 1;
clearScreen();
updateMenu = true;
ESC = false;
while (!ESC) {
previousMenuPosition = menuPosition;
if (encoderCW) {
if (menuPosition >= menuLength) {
menuPosition = 1;
} else {
menuPosition++;
}
encoderCW = false;
updateMenu = true;
} else if (encoderCCW) {
if (menuPosition <= 1) {
menuPosition = menuLength;
} else {
menuPosition--;
}
encoderCCW = false;
updateMenu = true;
}
if (updateMenu) {
if ((previousMenuPosition == 4 && menuPosition == 3) || (previousMenuPosition == menuLength && menuPosition == 1) || (previousMenuPosition == 3 && menuPosition == 4) || (previousMenuPosition == 1 && menuPosition == menuLength)) {
// If the user has scrolled to the next menu
clearScreen();
}
if (menuPosition < 4) {
setCursor(0x02);
Serial1.print("-Cable Settings-");
setCursor(0x41);
Serial1.print("Max Limit:");
ConvertMeters(maxCablePayedOut);
LCDPrintUnitFloat(tempVal, 0x40);
setCursor(0x15);
Serial1.print("Min Limit:");
ConvertMeters(minCablePayedOut);
LCDPrintUnitFloat(tempVal, 0x14);
setCursor(0x55);
Serial1.print("Length:");
ConvertMeters(cableLength);
LCDPrintUnitFloat(tempVal, 0x54);
} else if (menuPosition > 3) {
setCursor(0x01);
Serial1.print("Diameter:");
ConvertCentimeters(cableDiameter);
LCDPrintUnitFloatCentimeters(tempVal, 0x00);
setCursor(0x41);
Serial1.print("Scale:");
LCDPrintUnitlessFloat(scaleFactor, 0x40);
setCursor(0x15);
Serial1.print("Stretch:");
LCDPrintUnitlessFloat(stretchFactor, 0x14);
}
DrawCursor();
updateMenu = false;
}
if (selectPressed) {
switch (menuPosition) {
case 1:
maxCablePayedOut = LCDSetMaxLimit(0x40);
if (sdCard) {
drumFile = SD.open("DRMCONST.TXT", (O_READ | O_WRITE));
drumFile.find("maxCablePayedOut:");
drumFile.print(maxCablePayedOut, 1);
drumFile.print(',');
drumFile.close();
}
updateLimits = true;
break;
case 2:
minCablePayedOut = LCDSetMinLimit(0x14);
if (sdCard) {
drumFile = SD.open("DRMCONST.TXT", (O_READ | O_WRITE));
drumFile.find("minCablePayedOut:");
drumFile.print(minCablePayedOut, 1);
drumFile.print(',');
drumFile.close();
}
updateLimits = true;
break;
case 3:
cableLength = LCDSetCableLength(0x54);
if (sdCard) {
drumFile = SD.open("DRMCONST.TXT", (O_READ | O_WRITE));
drumFile.find("cableLength:");
drumFile.position();
drumFile.print(cableLength, 1);
drumFile.print(",");
drumFile.close();
}
break;
case 4:
cableDiameter = LCDSetCableDiameter(0x00);
if (sdCard) {
drumFile = SD.open("drmConst.TXT", (O_READ | O_WRITE));
drumFile.find("cableDiameter:");
drumFile.print(cableDiameter, 5);
drumFile.print(",");
drumFile.close();
}
break;
case 5:
scaleFactor = LCDSetScaleFactor(0x40);
if (sdCard) {
drumFile = SD.open("drmConst.TXT", (O_READ | O_WRITE));
drumFile.find("scaleFactor:");
drumFile.position();
drumFile.print(scaleFactor, 4);
drumFile.print(",");
drumFile.close();
}
break;
case 6:
stretchFactor = LCDSetStretchFactor(0x14);
if (sdCard) {
drumFile = SD.open("drmConst.TXT", (O_READ | O_WRITE));
drumFile.find("stretchFactor:");
drumFile.position();
drumFile.print(stretchFactor, 4);
drumFile.print(",");
drumFile.close();
}
break;
}
selectPressed = false;
}
previousBackPress = backPress;
backPress = !digitalRead(BackButton);
if (backPress && (backPress != previousBackPress)) {
ESC = true;
delay(50);
}
}
ESC = false;
updateMenu = true;
refreshLCD = true;
menuPosition = menuReturn;
}
void SelectPressed() {
noInterrupts();
selectPressed = true;
interrupts();
}
void Rotate() {
unsigned long currentTime = millis();
// Logic for rockerUpPin (Clockwise)
if ((currentTime - lastDebounceTimeUp) > debounceDelay) {
int currentCLKUp = digitalRead(rockerUpPin);
if (currentCLKUp == HIGH && previousCLKUp == LOW) {
encoderCCW = true; // Clockwise rotation detected
}
previousCLKUp = currentCLKUp; // Update previous state
lastDebounceTimeUp = currentTime; // Update debounce time for rockerUpPin
}
// Logic for rockerDownPin (Counterclockwise)
if ((currentTime - lastDebounceTimeDown) > debounceDelay) {
int currentCLKDown = digitalRead(rockerDownPin);
if (currentCLKDown == HIGH && previousCLKDown == LOW) {
encoderCW = true; // Counterclockwise rotation detected
}
previousCLKDown = currentCLKDown; // Update previous state
lastDebounceTimeDown = currentTime; // Update debounce time for rockerDownPin
}
}