Hello!
I'm a real beginner at code, although I'm pretty confident at the hardware side.
I'm working on a menu for an audio processor that is driven by rotary encoder with a push button. However, I can't make the rotary encoder and push button behave as expected. What I wish to achieve is behaviour like this:
Main menu page - The rotary encoder adjusts volume and prints the decibel (dB) value on the serial terminal. It also prints audio input and tone value settings. I did this with a void updateDisplay so don't need to repeat it.
If the button is pressed, the menu progresses to the next page. I did this with a switch case function.
Menu 2 - Selects audio input. If the button is pressed but the audio input selection has not changed it progresses to the next menu page.
If the button is pressed and the audio input selection has been changed by the rotary encoder, it sets that input to current and returns to the main page.
Menu 3 - If the button is pressed but the tone control value has not changed it goes back to the main page as this is the last menu.
If the button is pressed and the tone control value has been changed by the rotary encoder, it sets that tone value and returns to the main page.
I will use those set values for volume, audio input and tone value to write the relevant memory addresses to the audio processor over I2C, but that code is currently in a separate project.
The behaviour I am getting is that I see the serial print repeated 3 times when the rotary encoder is moved one step. Additionally the button logic to either progress to the next menu if nothing has been changed, or set the new setting to current if something has been changed, doesn't seem to work and I can't see the logic in what it is doing.
Appreciate some help!
// Rotary encoder variables
const byte inputCLK = 3; // Arduino pin the rotary encoder CLK pin is connected to.
const byte inputDT = 2; // Arduino pin the rotary encoder DT pin is connected to.
int currentStateCLK; // Current state of rotary encoder pin CLK. Used to identify rotation.
int previousStateCLK; // Current state of rotary encoder pin CLK. Used to identify rotation.
boolean encCCW; // Rotary encoder flag to denote counter-clockwise rotatation by one pulse.
boolean encCW; // Rotary encoder flag to denote clockwise rotatation by one pulse.
// Push button variables
const byte buttonPin = 4; // Arduino pin the push button is connected to.
byte previousButtonState = HIGH; // Assume switch open because of pull-up resistor.
byte currentButtonState; // Will be populated by reading button pin satae.
const unsigned long debounceTime = 5; // Milliseconds of debounce.
unsigned long buttonPressTime; // Time since button last changed state. Used to differentialte button bounce vs. intentional press.
boolean buttonPressed = 0; // A flag variable to identify button presses. Returned from button debounce code.
// Menu mode change variables
int mode = 1; // Mode variable used to run switch case. 1 = volume, 2 = audio input, 3 = tone control.
int currentInputNumber = 1; // Audio input the system is currently set to.
int newInputNumber = 1; // Audio input user is in the process of choosing by rotary control.
int currentToneValue = 5; // Current tone control setting.
int newToneValue = 5; // Tone control setting the user is in the process of choosing by rotary control.
// Gain variables
float volume; // Volume level in dB.
float linGain; // Volume level as a linear gain value between 0 (no level) and 1 (full level).
uint32_t vol32; // Linear gain as a 32 bit word to populate hex array.
byte volArray[4]; // 4 byte array to hold volume hex values ready to send over I2C to the audio processor.
void setup() {
volume = -45; // Set default volume level to -45dB.
// Set encoder and push button pins as inputs.
pinMode (inputCLK,INPUT);
pinMode (inputDT,INPUT);
pinMode (buttonPin, INPUT_PULLUP);
encCCW = 0; // Initial state for rotation flag not rotated.
encCW = 0; // Initial state for rotation flag not rotated.
previousStateCLK = digitalRead(inputCLK); // Read initial state of rotary encoder CLK pin. Assign to previousStateCLK so we can check for state changes.
// Setup Serial Monitor
Serial.begin (9600);
}
void loop() {
// Rotary encoder code.
currentStateCLK = digitalRead(inputCLK); // Read the current state of inputCLK pin.
if (currentStateCLK != previousStateCLK) { // If the previous and the current state of inputCLK are different, then the encoder and been rotated.
if (currentStateCLK != digitalRead(inputDT)) { // If the inputCLK state is different than the inputDT state then the rotation is counterclockwise.
encCCW = 1; // Set rotation flag counter-clockwise.
}
else { // If inputCLK and inputDT are the same then encoder is not rotating CCW.
encCW = 1; // Set rotation flag clockwise.
}
}
// Button press code with debounce.
currentButtonState = digitalRead (buttonPin);
if (currentButtonState != previousButtonState){
if (millis () - buttonPressTime >= debounceTime){ // debounce
buttonPressTime = millis (); // when we closed the switch
previousButtonState = currentButtonState; // remember for next time
if (currentButtonState == LOW){ // When debounce criteria are met and button is still pressed.
buttonPressed = 1; // Set button flag to 1.
}
else { // When debounce criteria are not met because boutton is not pressed or it is bouncing.
buttonPressed = 0; // Set button flag to 0.
}
}
}
// Limit Mode (menu page) and loop back to main menu.
if (mode >= 4){
mode = 1;
}
// Convert dB volume level to hex array for sending by I2C to the audio processor.
linGain = pow(10, volume/20); // Convert dB level to linear gain value.
vol32 = linGain * 16777216; // Convert linear gain value stored as a float variable to a 32 bit integer.
volArray[0] = (vol32 >> 24) & 0xFF; // Populate first byte of array with most significant 8 bits of the 32 bit volume word, by shifting word right by 24 bits. Adding 0xFF denotes it as a hex value.
volArray[1] = (vol32 >> 16) & 0xFF; // Populate second byte of array with next most significant 8 bits of the 32 bit volume word, by shifting word right by 16 bits. Adding 0xFF denotes it as a hex value.
volArray[2] = (vol32 >> 8) & 0xFF; // Populate third byte of array with next most significant 8 bits of the 32 bit volume word, by shifting word right by 8 bits. Adding 0xFF denotes it as a hex value.
volArray[3] = vol32 & 0xFF; // Populate fourth byte of array with least significant 8 bits of the 32 bit volume word, naturally the first 8 bits of the word. Adding 0xFF denotes it as a hex value.
switch (mode) { // Each menu page is a case operated by the 'mode' variable.
case 1: // Main menu page, volume control.
if (encCCW == 1) { // If the rotary encoder has been rotated counter-clockwise.
volume --; // Decrement volume level by one.
updateDisplay();
}
if (encCW == 1) { // If the rotary encoder has been rotated clockwise.
volume ++; // Increment volume level by one.
updateDisplay();
}
if (buttonPressed == 1) {
mode = mode + 1 ; // If the button is pressed while on the main volume page (case 1), the menu progresses to the next page (case 2).
updateDisplay();
}
// End of case 1.
case 2:
if (encCCW == 1) { // If the rotary encoder has been rotated counter-clockwise.
newInputNumber -= 1; // subtract 1 from input number.
updateDisplay();
}
if (encCW == 1) { // If the rotary encoder has been rotated clockwise.
newInputNumber += 1; // Add 1 to the input number.
updateDisplay();
}
if (newInputNumber >= 6){ // If input number is more than 5 then loop back to 1.
newInputNumber = 1;
}
if (newInputNumber <= 0){ // If input number is less than 1 then loop back to 5.
newInputNumber = 5;
}
if (buttonPressed == 1) {
if(newInputNumber == currentInputNumber){ // If the button is pressed without changing input selection.
mode = mode + 1 ; // Increment mode by 1 to progress to the next menu page.
updateDisplay();
}
else{ // A new audio input is being selected.
currentInputNumber = newInputNumber; // Set the current audio input to the new selection.
mode = 1; // Go back to the main menu page (case 1)
updateDisplay();
}
}
// End of case 2.
case 3:
if (encCCW == 1) { // If the rotary encoder has been rotated counter-clockwise.
newToneValue -= 1; // subtract 1 from tone value.
updateDisplay();
}
if (encCW == 1) { // If the rotary encoder has been rotated clockwise.
newToneValue += 1; // Add 1 to the tone value.
updateDisplay();
}
if (newToneValue >= 10){ // If tone value is more than 9.
newToneValue = 9; // Limit tone value to 9.
}
if (newToneValue <= 0){ // If tone value is less than 1.
newToneValue = 1; // Limit tone value to 1.
}
if (buttonPressed == 1) {
if(newToneValue == currentToneValue){ // If the button is pressed without changing input selection.
mode = 1 ; // Go back to the main menu page, because this is the last menu.
updateDisplay();
}
else{ // A new tone value has been selected.
currentToneValue = newToneValue; // Set the current tone value to the new tone value.
mode = 1; // Go back to the main menu page (case 1).
updateDisplay();
}
}
// End of case 3.
} // End of switch.
// Rotary encoder reset for next loop around.
previousStateCLK = currentStateCLK; // Update previousStateCLK with the current state of inputCLK.
encCCW = 0; // Reset encoder counter-clockwise flag.
encCW = 0; // Reset encoder clockwise flag.
} // end of loop.
void updateDisplay(){ // Display the menu mode, volume level and current audio input.
Serial.print("Mode:");
Serial.println(mode);
Serial.print("Volume: ");
Serial.print(volume);
Serial.println("dB");
Serial.print("Current Input Number:");
Serial.println(currentInputNumber);
Serial.print("New Input Number:");
Serial.println(newInputNumber);
Serial.print("Current Tone Control Value:");
Serial.println(currentToneValue);
Serial.print("New Tone Control Value:");
Serial.println(newToneValue);
Serial.println();
}