Function weird behavior

Hey everyone, need a little help.

Maybe it's fool question, but I've really tried figuring it out and couldn't.

I'm having different behaviors for the same code if I have it inside or outside an 'if' statement.

I'm trying to create two cases with a Button, and have a rotary encoder to work according to the cases. The code I'm using is:

while(digitalRead(BUTTON) == LOW){
   delay(300);   

  // Short click alternates modeDial between 1 and 2
  if(digitalRead(BUTTON) == HIGH){ 
    modeDial ++;
    if(modeDial == modeDialMax){
      modeDial = 1;
    }
  switch(modeDial) {  
  
  case 1:
// These are attributes from RotaryEncoder library

 if (newPos < ROTARYMIN) {
    encoder.setPosition(ROTARYMIN / ROTARYSTEPS);
    newPos = ROTARYMIN;

  } else if (newPos > ROTARYMAX) {
    encoder.setPosition(ROTARYMAX / ROTARYSTEPS);
    newPos = ROTARYMAX;
  } // if
  if (lastPos != newPos) {
         controlChange(0, 101, newPos);
         MidiUSB.flush();
         lcd.setCursor(0, 1);
         lcd.print("Contrast        ");
         delay(2);
         lastPos = newPos;
     }
break;

case 2: 
if (newPos < ROTARYMIN) {
    encoder.setPosition(ROTARYMIN / ROTARYSTEPS);
    newPos = ROTARYMIN;

  } else if (newPos > ROTARYMAX) {
    encoder.setPosition(ROTARYMAX / ROTARYSTEPS);
    newPos = ROTARYMAX;
  } // if
  if (lastPos != newPos) {
         controlChange(0, 100, newPos);
         MidiUSB.flush();
         lcd.setCursor(0, 1);
         lcd.print("Exposure        ");
         delay(2);
         lastPos = newPos;
     }

These commands should send MIDI CC's to MIDI2LR, a plugin tu use MIDI with Lightroom, but when they are inside the switch they don't work. If I just configure the Encoder with the same function but without the switch, they do work. Any ideas of what could be causing it?

I just need to have two commands - Exposure and Contrast or MIDI cc 100 and CC 101 (or any values) - available in the same Rotary Button. Then, the Else of that switch would be long clicks leading to another two values. I actually think there is probably another simpler way to make the button work like this - like with a library - but that was the only way i could get it more or lesse working.

Well, I don't know if I've explained my self, but I can surely try again!

Thanks!!

Please edit your post, select all code and click the </> button; next save your post. It will apply so-called code-tags to your code which will make it easier to read, prevents the forum software from mangling it (e.g. preserve indentations) and makes it easier to copy.

1 Like

Can you post the version that works (without the switches) for comparison?

It looks like your first few lines could be missing some closing brackets. It is hard to tell without seeing the rest of your sketch.

I also recommend confirming that your switching logic is working as intended by adding some serial monitoring.

Thanks Winter. The code is pretty much the same:

if (newPos < ROTARYMIN) {
    encoder.setPosition(ROTARYMIN / ROTARYSTEPS);
    newPos = ROTARYMIN;

  } else if (newPos > ROTARYMAX) {
    encoder.setPosition(ROTARYMAX / ROTARYSTEPS);
    newPos = ROTARYMAX;
  } // if
  if (lastPos != newPos) {
         controlChange(0, 101, newPos);
         MidiUSB.flush();
         lcd.setCursor(0, 1);
         lcd.print("Contrast        ");
         delay(2);
         lastPos = newPos;
     }

This is the one I have right now, and it works as I intend. I just wanted to have another config triggered by the Encoder button, which would actually just change the CC value and the lcd.print.

About your suggestion, I actually tried with 'Serial.print (DialMode)' inside respective cases, and they are actually printed... So the switch seems to be working out, but the result I was expecting in Lightroom is not achieved.

I don't know, maybe I have to find another approach to change these 'modes'.

Here goes the whole sketch, it's just a little long - and is probably full of stupid ideas, but it's my first time coding! It's still missing the Dials 2,3 and 4 in the case 2 / Lightroom. The issue is with the Dial 1, in the last chunk of code. The way the code is below, it works. I was trying to add the switch inside that case 2/Lightroom.

#include <Arduino.h>
#include <RotaryEncoder.h>
#include "Keyboard.h"
#include "MIDIUSB.h"

// Liquid Crystal Pins
#include <LiquidCrystal.h>
const int RS = A5, EN = A4, D4 = A3, D5 = A2, D6 = A1, D7 = A0;
LiquidCrystal lcd(RS, EN, D4, D5, D6, D7);

// Encoders parameters 
#define ROTARYSTEPS 1
#define ROTARYMIN 0
#define ROTARYMAX 127

// Dial 1
#define  OUTPUT_B 3 //CLK
#define  OUTPUT_A 2 //DT
#define  BUTTON 1
int lastPos = -1;
bool lastButtonState = 0;

  // Lr Dial1 Switch Modes
  int modeDial = 1;
  int modeDialMax = 3;

// Dial 2
#define OUTPUT_B1 6 //CLK
#define OUTPUT_A1 5 //DT
#define BUTTON1 4
int lastPos1 = -1;
bool lastButtonState1 = 0;

  // Lr Dial2 Switch Modes
  int modeDial1 = 1;
  int modeDial1Max = 3;

// Dial 3
#define OUTPUT_B2 9 //CLK
#define OUTPUT_A2 8 //DT
#define BUTTON2 7
int lastPos2 = -1;
bool lastButtonState2 = 0;

  // Lr Dial3 Switch Modes
  int modeDial2 = 1;
  int modeDial2Max = 3;

// Dial 4
#define OUTPUT_B3 12 //CLK
#define OUTPUT_A3 11 //DT
#define BUTTON3 10
int lastPos3 = -1;
bool lastButtonState3 = 0;  

  // Lr Dial4 Switch Modes
  int modeDial3 = 1;
  int modeDial3Max = 3;

// Setup a RotaryEncoder with 4 steps per latch for the 2 signal input pins:
 RotaryEncoder encoder(OUTPUT_A, OUTPUT_B, RotaryEncoder::LatchMode::FOUR3);
 RotaryEncoder encoder1(OUTPUT_A1, OUTPUT_B1, RotaryEncoder::LatchMode::FOUR3);
 RotaryEncoder encoder2(OUTPUT_A2, OUTPUT_B2, RotaryEncoder::LatchMode::FOUR3);
 RotaryEncoder encoder3(OUTPUT_A3, OUTPUT_B3, RotaryEncoder::LatchMode::FOUR3);


// Mode Button with 2 modes
#define MODE 13 // pin 13
 bool lastButtonState9 = 0;
 int mode = 1;
 int modeMax = 3;



// MIDI CONFIG
  void noteOn(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOn = {0x09, 0x90 | channel, pitch, velocity};
  MidiUSB.sendMIDI(noteOn);}

  void noteOff(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOff = {0x08, 0x80 | channel, pitch, velocity};
  MidiUSB.sendMIDI(noteOff);
}


// - - - - - - - - - - - - - - - - - SETUP - - - - - - - - - - - - - - - - - - - //

void setup() {

   Serial.begin(115200);
  // Enable Keyboard Library 
  Keyboard.begin();
  lcd.begin(16, 2);
  lcd.clear();
  lcd.setCursor(0, 0);

  // Rotary Encoders positions  
  encoder.setPosition(64 / ROTARYSTEPS);
  encoder1.setPosition(64 / ROTARYSTEPS);
  encoder2.setPosition(64 / ROTARYSTEPS);
  encoder3.setPosition(64 / ROTARYSTEPS);


  // Dial 1 pins
  pinMode(OUTPUT_A, INPUT);
  pinMode(OUTPUT_B, INPUT);
  pinMode(BUTTON, INPUT_PULLUP);

  //Dial 2 pins
  pinMode(OUTPUT_A1, INPUT);
  pinMode(OUTPUT_B1, INPUT);                                                                                                                                                                                           pinMode(OUTPUT_B1, INPUT);
  pinMode(BUTTON1, INPUT_PULLUP);

  //Dial 3 pins
  pinMode(OUTPUT_A2, INPUT);
  pinMode(OUTPUT_B2, INPUT);                                                                                                                                                                                           pinMode(OUTPUT_B1, INPUT);
  pinMode(BUTTON2, INPUT_PULLUP);

  //Dial 4 pins
  pinMode(OUTPUT_A3, INPUT);
  pinMode(OUTPUT_B3, INPUT);                                                                                                                                                                                           pinMode(OUTPUT_B1, INPUT);
  pinMode(BUTTON3, INPUT_PULLUP);
  
 // Mode trigger pin  
  pinMode(MODE, INPUT_PULLUP); 
 
}                                                                                                                                                                                                                                                                                                                                                                                                                                           
  
long lastClickTime = 0; 
long tempCount = 0;              


void controlChange(byte channel, byte control, byte value) {
  midiEventPacket_t event = {0x0B, 0xB0 | channel, control, value};
  MidiUSB.sendMIDI(event);
}

// - - - - - - - - - - - - - - - - - LOOP - - - - - - - - - - - - - - - - - - - //
    
void loop(){

  static int pos = 0; // Static position of Dial 1
  static int pos1 = 0; // Static position of Dial 2
  static int pos2 = 0; // Static position of Dial 3
  static int pos3 = 0; // Static position of Dial 4

  encoder.tick();
  encoder1.tick();
  encoder2.tick();
  encoder3.tick();
  
  // get the current physical position and calc the logical position
  int newPos = encoder.getPosition() * ROTARYSTEPS;
  int newPos1 = encoder1.getPosition() * ROTARYSTEPS;
  int newPos2 = encoder2.getPosition() * ROTARYSTEPS;
  int newPos3 = encoder3.getPosition() * ROTARYSTEPS;  
 
  
  
// MODE SWITCH - - - - - - - - - - - - - - //

 if (digitalRead(MODE) == LOW){
      if (lastButtonState9== HIGH) {
      mode ++;
      lcd.setCursor(0, 1);
      lcd.print("                ");
 
 if(mode == modeMax){
      mode = 1;
    }}
      delay(500);
      Keyboard.releaseAll();
      Serial.println(mode);
      lastClickTime = millis();
      lastButtonState9 = LOW;
    } 
   
 else {
    lastButtonState9 = HIGH;
    }     
// END OF MODE SWITCH - - - - - - - - - - - //

     
// CASES - TOOGLE BETWEEN PREMIERE AND LIGHTROOM //     
switch(mode) {
  
  case 1: // PREMIERE
      
  lcd.setCursor(0, 0);
  lcd.print("Premiere   ");
    
// Dial 1 - - - - - - - - - - - - - - - - - - - - - - - 
   
  if (pos != newPos) {
    if ((int)(encoder.getDirection()) == 1){
      Keyboard.press(KEY_RIGHT_ARROW);
      delay(8);}
      
  else{
      Keyboard.press(KEY_LEFT_ARROW);
      delay(8);
   }
    Keyboard.releaseAll();
    pos = newPos;
  } 
    
    
// Dial 1 trigger
   
    if (digitalRead(BUTTON) == LOW) {
    if (lastButtonState == HIGH) {
    Keyboard.print(" ");            
    delay(8);
    }
    lastButtonState = LOW;
    } 
    
    else {
    lastButtonState = HIGH;
    }                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        

// END DIAL 1 - - - - - - - - - - - - - - - - - - - - - - 

// Dial 2- - - - - - - - - - - - - - - - - - - - - - -
  
   if (pos1 != newPos1) {
    if ((int)(encoder1.getDirection()) == 1){
      Keyboard.press(KEY_UP_ARROW);
      delay(8);}
      
      else{
        Keyboard.press(KEY_DOWN_ARROW);
        delay(8);
    }
    Keyboard.releaseAll();
    pos1 = newPos1;
    }
    
    
// Dial 2 trigger
   
    if (digitalRead(BUTTON1) == LOW) {
    if (lastButtonState1 == HIGH) {
      Keyboard.press(KEY_LEFT_GUI); // This is Mac Command key 
      Keyboard.press('k');
      delay(100);
      Keyboard.releaseAll();
    }
    lastButtonState1 = LOW;
    } else {
    lastButtonState1 = HIGH;
    }                             

// END DIAL 2 - - - - - - - - - - - - - - - - - - - - - - - - 

// Dial 3- - - - - - - - - - - - - - - - - - - - - - -
  
   if (pos2 != newPos2) {
    if ((int)(encoder2.getDirection()) == 1){
      Keyboard.press(KEY_UP_ARROW);
      delay(8);}
      else{
        Keyboard.press(KEY_DOWN_ARROW);
        delay(8);
    }
    Keyboard.releaseAll();
    pos2 = newPos2;
    }
    
    
// Dial 2 trigger
   
    if (digitalRead(BUTTON2) == LOW) {
    if (lastButtonState2 == HIGH) {
      Keyboard.press('k');
      delay(100);
      Keyboard.releaseAll();
    }
    lastButtonState2 = LOW;
    } else {
    lastButtonState2 = HIGH;
    }                             

// END DIAL 3 - - - - - - - - - - - - - - - - - - - - - - - - 

// Dial 4- - - - - - - - - - - - - - - - - - - - - - -
  
   if (pos3 != newPos3) {
    if ((int)(encoder3.getDirection()) == 1){
      Keyboard.press(KEY_UP_ARROW);
      delay(8);}
      else{
        Keyboard.press(KEY_DOWN_ARROW);
        delay(8);
    }
    Keyboard.releaseAll();
    pos3 = newPos3;
    }
    
    
// Dial 4 trigger
   
    if (digitalRead(BUTTON3) == LOW) {
    if (lastButtonState3 == HIGH) {
      Keyboard.press('p');
      delay(100);
      Keyboard.releaseAll();
    }
    lastButtonState3 = LOW;
    } else {
    lastButtonState3 = HIGH;
    }                             

// END DIAL 4 - - - - - - - - - - - - - - - - - - - - - - - - 
break;
// = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - =  CASE 1 / PREMIERE - END //


case 2: // LIGHTROOM
     lcd.setCursor(0, 0);
     lcd.print("Lightroom  ");
     
// Dial 1 - - - - - - - - - - - - - - - - - - - - - - 

 

 if (newPos < ROTARYMIN) {
    encoder.setPosition(ROTARYMIN / ROTARYSTEPS);
    newPos = ROTARYMIN;

  } else if (newPos > ROTARYMAX) {
    encoder.setPosition(ROTARYMAX / ROTARYSTEPS);
    newPos = ROTARYMAX;
  } // if
  if (lastPos != newPos) {
         controlChange(0, 101, newPos);
         MidiUSB.flush();
         lcd.setCursor(0, 1);
         lcd.print("Contrast        ");
         delay(2);
         lastPos = newPos;
     }

Hi Erik,

what you have posted is still not a complete code
minimum there are missing closing curly brackets.
The position of closing brackets has influence on the functionality of the code
so even every closing bracket is important.

often it is much easier to add serial output and to watch the serial output than to go through the code line by line to analyse what the code is doing.

So it is always a good idea to post complete sketches.

There is an automatic function for posting a complete code from the very first to the very last line
in the Arduino-IDE just three steps:

  1. press Ctrl-T for autoformatting your code
  2. do a rightclick with the mouse and choose "copy for forum"
  3. paste clipboard into write-window of a posting

This construction leaves the while-loop as soon as you press the button
because then the condition of the while-loop is false

You are asking for a certain detail "why does this not work?" in 95% of all cases it is a good idea to describe the functionality you want to have in normal words.

You have a lot of code inside your case 1: case 2:
This makes it hard to analyse what is going on.
You should group your code into functions where one function does one thing.
Test this subfunctionality until it works as intended then move on to the next part that builds a sub-function(ality)

By coding this way a bug can only be in a small number of lines of code and is easier to find.
The longer you wait with testing the longer it will take to find bugs because you have to go through so many lines of codes that could all interact with each other.

best regards Stefan

which if statement?

  • long functions are difficult to read and write

  • it would be better if all the code under each case in loop() were separate functions

  • it would be better if the code for each encoder (e.g. pos1 != newPos1 and digitalRead(BUTTON1) == LOW) were in separate functions

  • if you do the above, you'd probably realize that several of these separate functions could be implemented as a common function with parameters for each encode

  • these separate functions can return a button press or a change in position

  • each mode could call the functions for the encoders relevant in that mode

  • using a struct to capture encoder parameters and state would significantly reduce the size of the code and make it easier to read and maintain

  • Algorithms + Data Structures = Programs is a good approach

Hey, sorry I wasn't clear!

I actually meant that it's different when inside a switch function (not sure it's called a function, still getting used to the syntax of all that!)

Yeah, I'm sure it would, I just don't know how to do it! The book you suggested would help on that as well?

Having the issue with the syntax here. What exactly would be a new function with that? Is that another loop that I can call in the main loop?

Yeah, I saw several examples of stuff way more complicated coded in so much simpler ways. It's actually beautiful to see those, like reading some poetry, i guess. I'll get there!

Thanks for all the tips and I'm sorry if my doubts are too dummy!

Best!!

Oh, I'm sorry, you're right. I actually have a lot of code for the following dials, from previous attempts, and they are commented right now, so I got the whole code until the comment and forgot to paste the final brackets and break. In the end of all that code I have:

break;
}
}

Not sure is this solves the question, but i think it does. As I said, written this way the code seems to work. Anyways, I just pressed Ctrl-T and can't thank you enough! I deleted all the comments and here is the code again, now beautifully formatted thanks to you!

// MIDI Controller with X knobs
// WARNING: works only with 32u4 and SAMD based boards (Leonardo, Micro etc.)
// Libraries
// https://www.arduino.cc/reference/en/language/functions/usb/keyboard/

#include <Arduino.h>
#include <RotaryEncoder.h>
#include "Keyboard.h"
#include "MIDIUSB.h"

// Liquid Crystal Pins
#include <LiquidCrystal.h>
const int RS = A5, EN = A4, D4 = A3, D5 = A2, D6 = A1, D7 = A0;
LiquidCrystal lcd(RS, EN, D4, D5, D6, D7);

// Dial 1
#define  OUTPUT_B 3 //CLK
#define  OUTPUT_A 2 //DT
#define  BUTTON 1
int lastPos = -1;
bool lastButtonState = 0;

// Lr Dial1 Switch Modes
int modeDial = 1;
int modeDialMax = 3;

// Dial 2
#define OUTPUT_B1 6 //CLK
#define OUTPUT_A1 5 //DT
#define BUTTON1 4
int lastPos1 = -1;
bool lastButtonState1 = 0;

// Lr Dial2 Switch Modes
int modeDial1 = 1;
int modeDial1Max = 3;

// Dial 3
#define OUTPUT_B2 9 //CLK
#define OUTPUT_A2 8 //DT
#define BUTTON2 7
int lastPos2 = -1;
bool lastButtonState2 = 0;

// Lr Dial3 Switch Modes
int modeDial2 = 1;
int modeDial2Max = 3;

// Dial 4
#define OUTPUT_B3 12 //CLK
#define OUTPUT_A3 11 //DT
#define BUTTON3 10
int lastPos3 = -1;
bool lastButtonState3 = 0;

// Lr Dial4 Switch Modes
int modeDial3 = 1;
int modeDial3Max = 3;

// Encoders parameters
#define ROTARYSTEPS 1
#define ROTARYMIN 0
#define ROTARYMAX 127

// Setup a RotaryEncoder with 4 steps per latch for the 2 signal input pins:
RotaryEncoder encoder(OUTPUT_A, OUTPUT_B, RotaryEncoder::LatchMode::FOUR3);
RotaryEncoder encoder1(OUTPUT_A1, OUTPUT_B1, RotaryEncoder::LatchMode::FOUR3);
RotaryEncoder encoder2(OUTPUT_A2, OUTPUT_B2, RotaryEncoder::LatchMode::FOUR3);
RotaryEncoder encoder3(OUTPUT_A3, OUTPUT_B3, RotaryEncoder::LatchMode::FOUR3);


// Mode Button with 2 modes
#define MODE 13 // pin 13
bool lastButtonState9 = 0;
int mode = 1;
int modeMax = 3;



// MIDI CONFIG
void noteOn(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOn = {0x09, 0x90 | channel, pitch, velocity};
  MidiUSB.sendMIDI(noteOn);
}

void noteOff(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOff = {0x08, 0x80 | channel, pitch, velocity};
  MidiUSB.sendMIDI(noteOff);
}


// - - - - - - - - - - - - - - - - - SETUP - - - - - - - - - - - - - - - - - - - //

void setup() {

  Serial.begin(115200);
  // Enable Keyboard Library
  Keyboard.begin();
  lcd.begin(16, 2);
  lcd.clear();
  lcd.setCursor(0, 0);

  // Rotary Encoders positions
  encoder.setPosition(64 / ROTARYSTEPS);
  encoder1.setPosition(64 / ROTARYSTEPS);
  encoder2.setPosition(64 / ROTARYSTEPS);
  encoder3.setPosition(64 / ROTARYSTEPS);


  // Dial 1 pins
  pinMode(OUTPUT_A, INPUT);
  pinMode(OUTPUT_B, INPUT);
  pinMode(BUTTON, INPUT_PULLUP);

  //Dial 2 pins
  pinMode(OUTPUT_A1, INPUT);
  pinMode(OUTPUT_B1, INPUT);                                                                                                                                                                                           pinMode(OUTPUT_B1, INPUT);
  pinMode(BUTTON1, INPUT_PULLUP);

  //Dial 3 pins
  pinMode(OUTPUT_A2, INPUT);
  pinMode(OUTPUT_B2, INPUT);                                                                                                                                                                                           pinMode(OUTPUT_B1, INPUT);
  pinMode(BUTTON2, INPUT_PULLUP);

  //Dial 4 pins
  pinMode(OUTPUT_A3, INPUT);
  pinMode(OUTPUT_B3, INPUT);                                                                                                                                                                                           pinMode(OUTPUT_B1, INPUT);
  pinMode(BUTTON3, INPUT_PULLUP);

  // Mode trigger pin
  pinMode(MODE, INPUT_PULLUP);

}

long lastClickTime = 0;
long tempCount = 0;


void controlChange(byte channel, byte control, byte value) {
  midiEventPacket_t event = {0x0B, 0xB0 | channel, control, value};
  MidiUSB.sendMIDI(event);
}

// - - - - - - - - - - - - - - - - - LOOP - - - - - - - - - - - - - - - - - - - //

void loop() {

  static int pos = 0; // Static position of Dial 1
  static int pos1 = 0; // Static position of Dial 2
  static int pos2 = 0; // Static position of Dial 3
  static int pos3 = 0; // Static position of Dial 4

  encoder.tick();
  encoder1.tick();
  encoder2.tick();
  encoder3.tick();

  // get the current physical position and calc the logical position
  int newPos = encoder.getPosition() * ROTARYSTEPS;
  int newPos1 = encoder1.getPosition() * ROTARYSTEPS;
  int newPos2 = encoder2.getPosition() * ROTARYSTEPS;
  int newPos3 = encoder3.getPosition() * ROTARYSTEPS;



  // MODE SWITCH - - - - - - - - - - - - - - //

  if (digitalRead(MODE) == LOW) {
    if (lastButtonState9 == HIGH) {
      mode ++;
      lcd.setCursor(0, 1);
      lcd.print("                ");

      if (mode == modeMax) {
        mode = 1;
      }
    }
    delay(500);
    Keyboard.releaseAll();
    Serial.println(mode);
    lastClickTime = millis();
    lastButtonState9 = LOW;
  }

  else {
    lastButtonState9 = HIGH;
  }
  // END OF MODE SWITCH - - - - - - - - - - - //


  // CASES - TOOGLE BETWEEN PREMIERE AND LIGHTROOM //
  switch (mode) {

    case 1: // PREMIERE

      lcd.setCursor(0, 0);
      lcd.print("Premiere   ");

      // Dial 1 - - - - - - - - - - - - - - - - - - - - - - -

      if (pos != newPos) {
        if ((int)(encoder.getDirection()) == 1) {
          Keyboard.press(KEY_RIGHT_ARROW);
          delay(8);
        }

        else {
          Keyboard.press(KEY_LEFT_ARROW);
          delay(8);
        }
        Keyboard.releaseAll();
        pos = newPos;
      }


      // Dial 1 trigger

      if (digitalRead(BUTTON) == LOW) {
        if (lastButtonState == HIGH) {
          Keyboard.print(" ");
          delay(8);
        }
        lastButtonState = LOW;
      }

      else {
        lastButtonState = HIGH;
      }

      // END DIAL 1 - - - - - - - - - - - - - - - - - - - - - -

      // Dial 2- - - - - - - - - - - - - - - - - - - - - - -

      if (pos1 != newPos1) {
        if ((int)(encoder1.getDirection()) == 1) {
          Keyboard.press(KEY_UP_ARROW);
          delay(8);
        }

        else {
          Keyboard.press(KEY_DOWN_ARROW);
          delay(8);
        }
        Keyboard.releaseAll();
        pos1 = newPos1;
      }


      // Dial 2 trigger

      if (digitalRead(BUTTON1) == LOW) {
        if (lastButtonState1 == HIGH) {
          Keyboard.press(KEY_LEFT_GUI); // This is Mac Command key
          Keyboard.press('k');
          delay(100);
          Keyboard.releaseAll();
        }
        lastButtonState1 = LOW;
      } else {
        lastButtonState1 = HIGH;
      }

      // END DIAL 2 - - - - - - - - - - - - - - - - - - - - - - - -

      // Dial 3- - - - - - - - - - - - - - - - - - - - - - -

      if (pos2 != newPos2) {
        if ((int)(encoder2.getDirection()) == 1) {
          Keyboard.press(KEY_UP_ARROW);
          delay(8);
        }
        else {
          Keyboard.press(KEY_DOWN_ARROW);
          delay(8);
        }
        Keyboard.releaseAll();
        pos2 = newPos2;
      }


      // Dial 2 trigger

      if (digitalRead(BUTTON2) == LOW) {
        if (lastButtonState2 == HIGH) {
          Keyboard.press('k');
          delay(100);
          Keyboard.releaseAll();
        }
        lastButtonState2 = LOW;
      } else {
        lastButtonState2 = HIGH;
      }

      // END DIAL 3 - - - - - - - - - - - - - - - - - - - - - - - -

      // Dial 4- - - - - - - - - - - - - - - - - - - - - - -

      if (pos3 != newPos3) {
        if ((int)(encoder3.getDirection()) == 1) {
          Keyboard.press(KEY_UP_ARROW);
          delay(8);
        }
        else {
          Keyboard.press(KEY_DOWN_ARROW);
          delay(8);
        }
        Keyboard.releaseAll();
        pos3 = newPos3;
      }


      // Dial 4 trigger

      if (digitalRead(BUTTON3) == LOW) {
        if (lastButtonState3 == HIGH) {
          Keyboard.press('p');
          delay(100);
          Keyboard.releaseAll();
        }
        lastButtonState3 = LOW;
      } else {
        lastButtonState3 = HIGH;
      }

      // END DIAL 4 - - - - - - - - - - - - - - - - - - - - - - - -
      break;
    // = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - =  CASE 1 / PREMIERE - END //


    case 2: // LIGHTROOM
      lcd.setCursor(0, 0);
      lcd.print("Lightroom  ");

      // Dial 1 - - - - - - - - - - - - - - - - - - - - - -



      if (newPos < ROTARYMIN) {
        encoder.setPosition(ROTARYMIN / ROTARYSTEPS);
        newPos = ROTARYMIN;

      } else if (newPos > ROTARYMAX) {
        encoder.setPosition(ROTARYMAX / ROTARYSTEPS);
        newPos = ROTARYMAX;
      } // if
      if (lastPos != newPos) {
        controlChange(0, 101, newPos);
        MidiUSB.flush();
        lcd.setCursor(0, 1);
        lcd.print("Contrast        ");
        delay(2);
        lastPos = newPos;
      }
      break;
  }

}




So the whole project is to have a tool for editing videos in Premiere and photos in Lightroom, hence the 2 cases in the beginning of the code. The case 1 is for work with Premiere and so far it is working fine - mainly because the buttons just perform a simple action like pressing some keys of the keyboard.

Case 2 is for using with Lightroom and where the issues begin!

My idea is to have the 4 Encoders changing two MIDI values each (toggling between those with the click of the button), which in Lightroom are translated in increasing or decreasing a slider, like Exposure, for example.

When i use the code like above, without the switch, Ligthroom recognizes the Encoders turn and do change the value of the parameter whole I turn it.

When I put the code inside a switch, however, Lightroom recognizes the click and applies the current value to the slider (the first time I click it's a MIDI CC 64, as I set the initial states earlier in the code). It does not recognize the encoder immediately, but when I click the button again, it will apply a new value changed by the numbers of steps I turned the Encoder. So it's like itzs not reading the turning immediately, although the code is storing the value somewhere. So, if I'm getting it right, the turning is not sending the midi info.

God, it's hard to explain haha hope you can understand it!

Of course I noticed, seeing examples around, that are ways of making the code simpler, but as I said, i'm just getting started in coding, so I'm sorry if I bring too dumb questions. Do you know where I can find something about the idea of grouping the code in functions? Is it creating other loops and calling them in the main loop?

Thanks a lot, Stefan! Let me know if there is anything you can't understand!

Best!!

as a first answer here is a part of an online-tutorial that explains how functions work
https://startingelectronics.org/software/arduino/learn-to-program-course/15-functions/

switch case is not a "function" in this sense it is one kind of a program-structure that does conditional execution. Only if the condition evaluates to true the code inside this case is executed.

With switch-case-structures it is important to add a "break;" at each end of all case-sections.
Otherwise all other cases are evaluated too - which in most cases is not wished.

And to really seeing what is going on in your code you should add serial-debug-output

I have never used MidiUSB and serial-output at the same time. So I don't know if this works. Give it a try

If it does not work which microcontroller-Board are you using?
You can code a second serial connection and read in the serial data by a virtual-comport-adapter.
best regards Stefan

1 Like

Thanks again!

I'm using an Arduino Micro.

I'll definitely take a look at the serial-debug-output and check if it can be used along with MidiUSB. It is a library to help with debugging, I assume?

And getting the tutorial right now! Thanks!!

no debugoutput is simply using a few commands that belong to the standard-set of functions

most simple example:

int myIntegerVar;

void setup() {
  Serial.begin(115200);
  Serial.println( F("Setup-Start") );
  myIntegerVar = 100;
}

void loop() {
  Serial.println( F("Hello World!") );
  myIntegerVar++;
  Serial.print( F("value of myIntegerVar is:") );
  Serial.println(myIntegerVar) );
  delay(1000);
}

best regards Stefan

Oh, I see, printing statements to check if the code is working!

Yeah, it does work with the MidiUSB. I'll make sure I start using it more often.

Thanks again!

here is a more advanced version of serial debug-output which makes use of a macro.
A macro is some kind of a "function" that does automated keyboard typing to write code
into your *.ino-file.

#define debugTxtVar(myFixedText, variableName) \
        Serial.print( F(#myFixedText " " #variableName"=") ); \
        Serial.println(variableName); 
// usage debugTxtVar("fixed Text", variableName)
// example printing name and content of a variable called "myCounter"
// debugTxtVar("Demo-Text",myCounter);
// serialoutput will be '"Demo-Text" myCounter=1'
// which means this macro does three things
// 1. print the fixed text 
// 2. print the NAME of the variable
// 3. print the content of the variable 
// all in one line

int myCounter;

void setup() {
  Serial.begin(115200);
  Serial.print( F("\n Setup-Start  \n") );
  myCounter =0;
}

void loop() {
  myCounter++;
  
  debugTxtVar("Demo-Text",myCounter);
  // whenever possible replace delay() 
  // by non-blocking timing based on millis()
  delay(1000); 
}
// serial output will look like this
/*
 Setup-Start  
"Demo-Text" myCounter=1
"Demo-Text" myCounter=2
"Demo-Text" myCounter=3
"Demo-Text" myCounter=4
...
*/

best regards Stefan

Ughhh that one is a little harder to me! I tested it, and although it's pretty straight forward, I don't get how putting something like it in my code would help. Guess I have to study the concept a little more.

Anyway, on my way to try it, I noticed that my Arduino app sometimes shuts down the Serial Monitor just after the upload - sometimes it's enough time to write a first sentence, like myCounter=1, in this case, and then it goes off, all greyed. So I have to restart the app in order for it working again. Anyways, no big deal.

So, just a little update:

Thanks for that! I'm already applying some of the concepts, not only about functions. I changed some stuff to make them easier, like getting some repetitive values and instead of writing them all, creating arrays, it at least leave stuff more organized.

Reading it I got what gcjr said back there:

So I'm trying to take stuff out o loop and just call the functions, but didn't succeeded so far. I'm basically creating a void DIAL1 { code that was previously in the loop} and, inside the loop, changing it for DIAL1. Does that sound right? Anyway, didn't work, but I'm still reading stuff to figure it out.

This also sounds a lot more clear to me, although I still gotta go on learning, but yeah, makes a lot of sense and it's now my goal.

The other thing I did was changing the way I was doing the switch, and it finally worked! So now I could turn the knob to change a parameter in Lightroom, click the button and start changing another parameter. Then I got to a whole new problem.

Let me try to explain.

Turning the knob in the case 1 will send different values of a particular control value to Ligthroom, from 0 to 127, starting in 64 (which is the mid point, as all of the Lightroom sliders I'm controlling have pos and neg values, starting on 0). The case 2 does the same, but in another control value. So far, so good! The problem is, if I turn the knob to a value 80 in case 1, when i switch to case 2, its value will be on 80, which makes me assume that the encoder itself is storing its literal position. If this is true, I'd probably have to change something in the library, but it's complicated for me. So maybe I'll have to try another different approach, unless any of you have a suggestion!

I don't know if that suggestion would work in this case, but I'd still have to read and study a bit to be able to apply it.

Anyways, that's already a lot! Thank you very much for your help, my friends. I send you my best!!

Sending the whole new code here, in case you wanna take a look!

// MIDI Controller with X knobs
// WARNING: works only with 32u4 and SAMD based boards (Leonardo, Micro etc.)
// Libraries
// https://www.arduino.cc/reference/en/language/functions/usb/keyboard/

#include <Arduino.h>
#include <RotaryEncoder.h>
#include "Keyboard.h"
#include "MIDIUSB.h"

// Liquid Crystal Pins
#include <LiquidCrystal.h>
const int RS = A5, EN = A4, D4 = A3, D5 = A2, D6 = A1, D7 = A0;
LiquidCrystal lcd(RS, EN, D4, D5, D6, D7);


int lastPos[4] = { -1, -1, -1, -1};
bool lastButtonState[5] = {0, 0, 0, 0, 0};
bool buttonState1 = 0;
int value = 0;
// Dial 1
#define  OUTPUT_B 3 //CLK
#define  OUTPUT_A 2 //DT
#define  BUTTON 1

// Lr Dial1 Switch Modes
int modeDial0 = 1;
int modeDial0Max = 3;

// Dial 2
#define OUTPUT_B1 6 //CLK
#define OUTPUT_A1 5 //DT
#define BUTTON1 4

// Lr Dial2 Switch Modes
int modeDial1 = 1;
int modeDial1Max = 3;

// Dial 3
#define OUTPUT_B2 9 //CLK
#define OUTPUT_A2 8 //DT
#define BUTTON2 7

// Lr Dial3 Switch Modes
int modeDial2 = 1;
int modeDial2Max = 3;

// Dial 4
#define OUTPUT_B3 12 //CLK
#define OUTPUT_A3 11 //DT
#define BUTTON3 10

// Lr Dial4 Switch Modes
int modeDial3 = 1;
int modeDial3Max = 3;

// Encoders parameters
#define ROTARYSTEPS 1
#define ROTARYMIN 0
#define ROTARYMAX 127

// Setup a RotaryEncoder with 4 steps per latch for the 2 signal input pins:
RotaryEncoder encoder0(OUTPUT_A, OUTPUT_B, RotaryEncoder::LatchMode::FOUR3);
RotaryEncoder encoder1(OUTPUT_A1, OUTPUT_B1, RotaryEncoder::LatchMode::FOUR3);
RotaryEncoder encoder2(OUTPUT_A2, OUTPUT_B2, RotaryEncoder::LatchMode::FOUR3);
RotaryEncoder encoder3(OUTPUT_A3, OUTPUT_B3, RotaryEncoder::LatchMode::FOUR3);


// Mode Button with 2 modes
#define MODE 13 // pin 13
int mode = 1;
int modeMax = 3;



// MIDI CONFIG
void noteOn(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOn = {0x09, 0x90 | channel, pitch, velocity};
  MidiUSB.sendMIDI(noteOn);
}

void noteOff(byte channel, byte pitch, byte velocity) {
  midiEventPacket_t noteOff = {0x08, 0x80 | channel, pitch, velocity};
  MidiUSB.sendMIDI(noteOff);
}

void controlChange(byte channel, byte control, byte value) {
  midiEventPacket_t event = {0x0B, 0xB0 | channel, control, value};
  MidiUSB.sendMIDI(event);
}

long lastClickTime = 0;
long tempCount = 0;


// - - - - - - - - - - - - - - - - - SETUP - - - - - - - - - - - - - - - - - - - //

void setup() {

  Serial.begin(115200);
  // Enable Keyboard Library
  Keyboard.begin();
  lcd.begin(16, 2);
  lcd.clear();
  lcd.setCursor(0, 0);

  // Rotary Encoders positions
  encoder0.setPosition(64 / ROTARYSTEPS);
  encoder1.setPosition(64 / ROTARYSTEPS);
  encoder2.setPosition(64 / ROTARYSTEPS);
  encoder3.setPosition(64 / ROTARYSTEPS);


  // Dial 1 pins
  pinMode(OUTPUT_A, INPUT);
  pinMode(OUTPUT_B, INPUT);
  pinMode(BUTTON, INPUT_PULLUP);

  //Dial 2 pins
  pinMode(OUTPUT_A1, INPUT);
  pinMode(OUTPUT_B1, INPUT);                                                                                                                                                                                           pinMode(OUTPUT_B1, INPUT);
  pinMode(BUTTON1, INPUT_PULLUP);

  //Dial 3 pins
  pinMode(OUTPUT_A2, INPUT);
  pinMode(OUTPUT_B2, INPUT);                                                                                                                                                                                           pinMode(OUTPUT_B1, INPUT);
  pinMode(BUTTON2, INPUT_PULLUP);

  //Dial 4 pins
  pinMode(OUTPUT_A3, INPUT);
  pinMode(OUTPUT_B3, INPUT);                                                                                                                                                                                           pinMode(OUTPUT_B1, INPUT);
  pinMode(BUTTON3, INPUT_PULLUP);

  // Mode trigger pin
  pinMode(MODE, INPUT_PULLUP);

}



// - - - - - - - - - - - - - - - - - LOOP - - - - - - - - - - - - - - - - - - - //

void loop() {

  static int pos[4] = {0, 0, 0, 0};
  int newPos[4] = {encoder0.getPosition() * ROTARYSTEPS, encoder1.getPosition() * ROTARYSTEPS, encoder2.getPosition() * ROTARYSTEPS, encoder3.getPosition() * ROTARYSTEPS};

  encoder0.tick();
  encoder1.tick();
  encoder2.tick();
  encoder3.tick();

  // MODE SWITCH - - - - - - - - - - - - - - //

  if (digitalRead(MODE) == LOW) {
    if (lastButtonState[4] == HIGH) {
      mode ++;
      lcd.setCursor(0, 1);
      lcd.print("                ");

      if (mode == modeMax) {
        mode = 1;
      }
    }
    delay(500);
    Keyboard.releaseAll();
    Serial.println(mode);
    lastClickTime = millis();
    lastButtonState[4] = LOW;
  }

  else {
    lastButtonState[4] = HIGH;
  }
  // END OF MODE SWITCH - - - - - - - - - - - //


  // CASES - TOOGLE BETWEEN PREMIERE AND LIGHTROOM //
  switch (mode) {

    case 1: // PREMIERE

      lcd.setCursor(0, 0);
      lcd.print("Premiere   ");

      // Dial 1 - - - - - - - - - - - - - - - - - - - - - - -

      if (pos[0] != newPos[0]) {
        if ((int)(encoder0.getDirection()) == 1) {
          Keyboard.press(KEY_RIGHT_ARROW);
          delay(8);
        }

        else {
          Keyboard.press(KEY_LEFT_ARROW);
          delay(8);
        }
        Keyboard.releaseAll();
        pos[0] = newPos[0];
      }


      // Dial 1 trigger

      if (digitalRead(BUTTON) == LOW) {
        if (lastButtonState[0] == HIGH) {
          Keyboard.print(" ");
          delay(8);
        }
        lastButtonState[0] = LOW;
      }

      else {
        lastButtonState[0] = HIGH;
      }

      // END DIAL 1 - - - - - - - - - - - - - - - - - - - - - -

      // Dial 2- - - - - - - - - - - - - - - - - - - - - - -

      if (pos[1] != newPos[1]) {
        if ((int)(encoder1.getDirection()) == 1) {
          Keyboard.press(KEY_UP_ARROW);
          delay(8);
        }

        else {
          Keyboard.press(KEY_DOWN_ARROW);
          delay(8);
        }
        Keyboard.releaseAll();
        pos[1] = newPos[1];
      }


      // Dial 2 trigger

      if (digitalRead(BUTTON1) == LOW) {
        if (lastButtonState[1] == HIGH) {
          Keyboard.press(KEY_LEFT_GUI); // This is Mac Command key
          Keyboard.press('k');
          delay(100);
          Keyboard.releaseAll();
        }
        lastButtonState[1] = LOW;
      } else {
        lastButtonState[1] = HIGH;
      }

      // END DIAL 2 - - - - - - - - - - - - - - - - - - - - - - - -

      // Dial 3- - - - - - - - - - - - - - - - - - - - - - -

      if (pos[2] != newPos[2]) {
        if ((int)(encoder2.getDirection()) == 1) {
          Keyboard.press(KEY_UP_ARROW);
          delay(8);
        }
        else {
          Keyboard.press(KEY_DOWN_ARROW);
          delay(8);
        }
        Keyboard.releaseAll();
        pos[2] = newPos[2];
      }


      // Dial 2 trigger

      if (digitalRead(BUTTON2) == LOW) {
        if (lastButtonState[2] == HIGH) {
          Keyboard.press('k');
          delay(100);
          Keyboard.releaseAll();
        }
        lastButtonState[2] = LOW;
      } else {
        lastButtonState[2] = HIGH;
      }

      // END DIAL 3 - - - - - - - - - - - - - - - - - - - - - - - -

      // Dial 4- - - - - - - - - - - - - - - - - - - - - - -

      if (pos[3] != newPos[3]) {
        if ((int)(encoder3.getDirection()) == 1) {
          Keyboard.press(KEY_UP_ARROW);
          delay(8);
        }
        else {
          Keyboard.press(KEY_DOWN_ARROW);
          delay(8);
        }
        Keyboard.releaseAll();
        pos[3] = newPos[3];
      }


      // Dial 4 trigger

      if (digitalRead(BUTTON3) == LOW) {
        if (lastButtonState[3] == HIGH) {
          Keyboard.press('p');
          delay(100);
          Keyboard.releaseAll();
        }
        lastButtonState[3] = LOW;
      } else {
        lastButtonState[3] = HIGH;
      }

      // END DIAL 4 - - - - - - - - - - - - - - - - - - - - - - - -
      break;
    // = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - =  CASE 1 / PREMIERE - END //


    case 2: // LIGHTROOM



      lcd.setCursor(0, 0);
      lcd.print("Lightroom  ");

      buttonState1 = digitalRead(BUTTON);
      if (buttonState1 != lastButtonState[1]) {
        if (buttonState1 == LOW) {
          value = 1 - value;
        }
        delay(50);
        lastButtonState[1] = buttonState1;
      }

      switch (value) {

        case 0:

          lcd.setCursor(0, 1);
          lcd.print("Exposure       ");

          if (newPos[0] < ROTARYMIN) {
            encoder0.setPosition(ROTARYMIN / ROTARYSTEPS);
            newPos[0] = ROTARYMIN;

          } else if (newPos[0] > ROTARYMAX) {
            encoder0.setPosition(ROTARYMAX / ROTARYSTEPS);
            newPos[0] = ROTARYMAX;
          } // if
          if (lastPos[0] != newPos[0]) {
            controlChange(0, 101, newPos[0]);
            MidiUSB.flush();

            delay(2);
            lastPos[0] = newPos[0];
          }


          break;

        case 1:

          lcd.setCursor(0, 1);
          lcd.print("Contrast        ");
          if (newPos[0] < ROTARYMIN) {
            encoder1.setPosition(ROTARYMIN / ROTARYSTEPS);
            newPos[0] = ROTARYMIN;

          } else if (newPos[0] > ROTARYMAX) {
            encoder1.setPosition(ROTARYMAX / ROTARYSTEPS);
            newPos[0] = ROTARYMAX;
          } // if
          if (lastPos[0] != newPos[0]) {
            controlChange(0, 100, newPos[0]);
            MidiUSB.flush();
// adjust the current position
  void setPosition(long newPosition);

Thanks for the help man. I added that code before void setup, but the result was the same... Anything else I should have done?

Oh, forgot to say I tried using different encoders defined earlier:

for case two I tried enconder1.setPosition, usign also newPos and lastPos [1]. That's what actually make me think the info is stored in a previous state of the code, on the encoder itself. Even tried to have encoder0 and encoder1 in the same Pins but still got the same results... Don't even know if its 'allowed' to have different stuff in the same pins, but I tried hahah

if the encoder-library has a setPosition-function you can store the positions for each mode in its own variable and if you switch the mode first thing to do is a call to setPosition with the value of the variable you have switched to.

what cattledog posted is just the header of a definition of a function.

The body of this function has to written be written too
and you have to add function-calls as described above

as a rough sketchy sketch in pseudo-code

if "mode switched"
encoderx.setPosition( "variable that holds the position for this mode")

where "x" is the encoder-number 0, 1, 2 , 3 according to the button you have pressed.

You shouldn't do it all at the same time.
take that suggestion that seems to be easy to code and just modify your code to this
then test

then take the next suggestion change code and test again.
small change test
smal change test
....
in the end this will be faster because the lines of code to examine for a bug is much smaller

best regards Stefan

1 Like

It's a library function. I should have been more explicit.

1 Like

Thank you both! I'll try it!!