Multiplay software controller program improvements

Hello, all.

I’ve been using a Windows software called Multiplay for cue playback and interaction which is controlled by keystrokes.

The Multiplay software is available at http://www.da-share.com/software/multiplay/ , if you’d like to take a look at it.

In order to get some security on accidental presses, I implemented the code below on a Arduino Leonardo to create a controller with an OLED display to provide some visual feedback on the controller.

Although the code works, I can’t stop thinking that it may be some arrangements that could be implemented in order to make it more efficient.

My main concern is the latency, between the graphics presented and the button press, for which I’d appreciate any help you could give me on changing the code to be as efficient as possible.

You can also find the Arduino IDE file attached.

Thank you in advance.

/*
 * Multiplay controller 1.1 by HBT
 * Arduino controller for the Windows Multiplay software
 * for cues controls on live shows.
 * Multiplay is available at http://www.da-share.com/software/multiplay/ .
 * 
 */
 
#include "U8glib.h"
#include <HID.h>
#include <Keyboard.h>

#define stop_button 2       // Pin for the STOP button
#define fade_button 3       // Pin for the FADE ALL button
#define play_button 4       // Pin for the PLAY button
#define previous_button 5   // Pin for the PREVIOUS button
#define next_button 6       // Pin for the NEXT button
#define secure_button 7     // Pin for the SECURE button
#define secure_led 13       // Pin for SECURE led
int waiting = 500;          // Delay in ms for keyboard presses after touch.
boolean firstboot = true;   // Flag to signal booting

U8GLIB_SH1106_128X64 u8g(12, 11, 10, 9); // SW SPI Com: CLK = 12, MOSI = 11, CS = 10, A0 = 9

// Setting up the assignated key on MULTIPLAY to the buttons on the controller. 
int stop_key = 32;        //[space]
int fade_key = 48;        //[0]
int play_key = 46;        //[.]
int previous_key = 217;   //[up arrow]
int next_key = 218;       //[down arrow]

//Functions to draw icons on screen
// Draw SECURE text
void drawSecure(void) {
  u8g.setFont(u8g_font_osb18);
  u8g.drawStr( 9, 48, "SECURE");
}

//Draw Credits 
void drawCredits(void) {
  u8g.setFont(u8g_font_helvB12);
  u8g.drawStr( 21, 17, "MULTIPLAY");
  u8g.drawStr( 2, 35, "Controller");
  u8g.drawStr( 23, 63, "ver 1.1");    //Change version number here
}

//Draw NOT SECURE text
void drawUnsecure(void) {
  //u8g.setFont(u8g_font_unifont);
  u8g.setFont(u8g_font_osb18);
  u8g.drawStr( 34, 24, "NOT");
  u8g.drawStr( 8, 60, "SECURE");
}

//Draw STOP icon
void drawStop(void) {
  u8g.drawBox(35,3, 60,60);
}

//Draw FADE icon
void drawFade(void) {
  u8g.drawLine(30,3 , 64,3);
  u8g.drawLine(64,3 , 98, 61);
}

//Draw PLAY icon
void drawPlay(void) {
  u8g.drawTriangle(34,3 , 94,32, 34,61);
}

//Draw PREVIOUS icon
void drawPrevious(void) {
  u8g.drawTriangle(30,16 , 62,32, 30,48);
  u8g.drawTriangle(64,16 , 96,32, 64,48);
}

//Draw NEXT icon
void drawNext(void) {
  u8g.drawTriangle(62,16 , 30,32, 62,48);
  u8g.drawTriangle(96,16 , 64,32, 96,48);
}

//Draw WARNING icon - not implemented yet
void drawCircle(void) {
  u8g.drawDisc(64, 32, 30);
}

//Clear the screen
void clearScreen(void) {
}

//SETUP
void setup(void) {
  // PULL UP enabled on all inputs, to have them all HIGH except if connected to ground (button press)
  pinMode(stop_button, INPUT_PULLUP);
  pinMode(fade_button, INPUT_PULLUP);
  pinMode(play_button, INPUT_PULLUP);
  pinMode(previous_button, INPUT_PULLUP);
  pinMode(next_button, INPUT_PULLUP);
  pinMode(secure_button, INPUT_PULLUP);
  pinMode(secure_led, OUTPUT);

  Keyboard.begin();
  
  if ( u8g.getMode() == U8G_MODE_BW ) {
    u8g.setColorIndex(1);         // pixel on
  }
}

//LOOP
void loop(void) {

//BOOT SCREEN
  if(firstboot == true){
    //Draw initial CREDITS on display
    u8g.firstPage();  
    do {
      drawCredits();
    } while( u8g.nextPage() );
    //Waits 3 seconds
    delay(3000);
    //Set firstboot variable to false to avoid looping all over credits again
    firstboot = false;
    //Clear the screen
    u8g.firstPage();  
    do {
      clearScreen();
    } while( u8g.nextPage() );
    delay(1000);
    }
    
//SECURE MODE
  //Check if the secure button is unpressed, to avoid sending keyboard stroke on accidental one button press.
  if(digitalRead(secure_button)==HIGH){
    //Turn secure led off 
    digitalWrite(secure_led, LOW);
    
    //Draw SECURE on display
    u8g.firstPage();  
    do {
      drawSecure();
    } while( u8g.nextPage() );    
  }
  
// UNSECURE MODE - Allows PLAY, STOP and FADE press
  //If secure button is pressed
  if(digitalRead(secure_button)==LOW){
     //Turn on secure led to warn the secure button is pressed.
    digitalWrite(secure_led, HIGH);
    
    //Draw NOT SECURE on display
    u8g.firstPage();  
    do {
      drawUnsecure();
    } while( u8g.nextPage() );
    
    /*//Draw WARNING circle on display
    u8g.firstPage();  
    do {
      drawCircle();
    } while( u8g.nextPage() );
    */ 
    
// STOP - Stops all cues
    //If STOP button is pressed
    while(digitalRead(stop_button)==LOW){
      //Draw STOP icon on display
      u8g.firstPage();  
      do {
        drawStop();
      } while( u8g.nextPage() );
      //Send stop_key keystroke
      keystroke(stop_key, waiting);
    }
    
// FADE - Fade out all cues
    //If FADE button is pressed
    while(digitalRead(fade_button)==LOW){
      //Draw FADE icon on display
      u8g.firstPage();  
      do {
        drawFade();
      } while( u8g.nextPage() );
      //Send fade_key keystroke
      keystroke(fade_key, waiting);
    } 
    
//PLAY - Play selected cue
    //If PLAY button is pressed
    while(digitalRead(play_button)==LOW){

      //Draw PLAY icon on display
      u8g.firstPage();  
      do {
        drawPlay();
      } while( u8g.nextPage() );
      //Send play_key keystroke
      keystroke(play_key, waiting);
    }
  }
  
// PREVIOUS - Move up on the cue list
  //If PREVIOUS button is pressed
  while(digitalRead(previous_button)==LOW){
  
    //Draw PREVIOUS icon on display
    u8g.firstPage();  
    do {
      drawPrevious();
    } while( u8g.nextPage() );
    //Send previous_key keystroke
    keystroke(previous_key, waiting);
  } 

//NEXT - Move down on the cue list
  //If NEXT button is pressed
  while(digitalRead(next_button)==LOW){
    
    //Draw NEXT icon on display 
    u8g.firstPage();  
    do {
      drawNext();
    } while( u8g.nextPage() );
    //Send next_key keystroke
    keystroke(next_key, waiting);
  } 
}

//KEYSTROKE
//Function to invoke key press with delay dly to debounce key press
void keystroke(int key, int dly){
    Keyboard.write(key);
    delay(dly);
}

multiplay_controller_with_display_UTG8.ino.ino (5.72 KB)

int stop_key = 32;        //[space]
int fade_key = 48;        //[0]
int play_key = 46;        //[.]
int previous_key = 217;   //[up arrow]
int next_key = 218;       //[down arrow]

Are you sure you don't need longs for these? Appropriate data types are appropriate for a reason.

On every pass through loop(), you draw something is a switch IS high or IS low. You should look at the state change detection example, and only draw if the state has changed.

I don't think I need the long type to hold the key value, since the Leonardo is intended to work as HID and all the documentation I could find, assigned an int value to the specific key you'd like pressed.

The drawings on the OLED display work just fine, since I'm looping through a condition check, it always has something to draw depending on the state of the keys.

Since there are not so many keys to be pressed, it was pretty straightforward thinking of all possible outcomes on key presses.

Also, as any display is being rendered consecutively on each loop iteration (because the draw functions work like this), my main problem was related to efficiency on the button press and the display, since they can't happen at the same time on procedural programming, and since I don't have a way to work simultaneous threads on a arduino (simultaneous, meaning one core for each function).

I just wanted to understand if was there a more efficient way of arranging the code I built. I don't know if interrupts make some sense here or some other programming method I may not be aware of.

Thank you for your time and interest on my humble code.

I don't think I need the long type to hold the key value, since the Leonardo is intended to work as HID and all the documentation I could find, assigned an int value to the specific key you'd like pressed.

The point was that you don't need an int to hold a byte.

The drawings on the OLED display work just fine, since I'm looping through a condition check, it always has something to draw depending on the state of the keys.

It does not need to. How often is the switch state changing? On every pass through loop()? Why do you need to draw more often than the switch state changes?

my main problem was related to efficiency on the button press and the display, since they can't happen at the same time on procedural programming

So, the less often you draw, the less often you have a problem.

I just wanted to understand if was there a more efficient way of arranging the code I built.

Of arranging the code? No. Of writing the code? Yes. Don't do anything you do not need to do.

If the switch state changes to HIGH, draw the high art. If the switch state changes to LOW, draw the low art. Do not draw the high art every time through loop() when you see that the switch IS high. Do not draw the low art every time through loop() when you see that the switch IS low. Draw only when the switch BECOMES pressed or BECOMES released.

Thank you, Paul.

Your explanation said it all.

It makes sense to only draw the art if needed.

I'll try to implement the changes you suggested. For what I could understand, I'll need to create a variable to hold the switch change and only draw new art, if a change on the switch was detected.

I'll think about the logic and will post here my new implementation as soon as I can.

For what I could understand, I'll need to create a variable to hold the switch change

You need a variable to hold the previous state of the switch, so you can compare it to the current state, to determine if a change occurred.

Hello again, PaulS.

I was thinking of establishing the following logic in the program:

  • declare a current_state variable which could handle defined values for each different button press, such as [SECURE] , [NOTSECURE] , [PLAY] , [STOP] , [FADE] , [NEXT] and [PREVIOUS]

  • declare a previous_state variable, which would get a copy of the current_state variable at the end of the loop(). This variable should be set to None or Empty on the boot.

  • On each while loop for each button press, I’d change the current_state to the defined value and implement a if loop, which checks if the current_state is equal to the previous_state. If statement is TRUE, would break the while loop and go back to the end of the loop(). If statement is FALSE, draw the correspondent art.

  • At the end of the loop(), current_state should be copied to previous_state.

My approach didn’t work out properly, as sometimes I have the art being drawn and sometimes not. As previous and next button, don’t need the secure button to be pressed, I expected at least these two buttons to work.

I may be interpreting the draw method the wrong way, but I’m having some trouble understanding it properly. The ug8.nextPage() function isn’t very clear to me.

Would you mind helping me debugging the code below?

/*
   Multiplay controller 1.2 by HBT
   Arduino controller for the Windows Multiplay software
   for cues controls on live shows.
   Multiplay is available at http://www.da-share.com/software/multiplay/ .

*/

#include "U8glib.h"
#include <HID.h>
#include <Keyboard.h>

#define stop_button 2       // Pin for the STOP button
#define fade_button 3       // Pin for the FADE ALL button
#define play_button 4       // Pin for the PLAY button
#define previous_button 5   // Pin for the PREVIOUS button
#define next_button 6       // Pin for the NEXT button
#define secure_button 7     // Pin for the SECURE button
#define secure_led 13       // Pin for SECURE led
long waiting = 500;          // Delay in ms for keyboard presses after touch.
boolean firstboot = true;   // Flag to signal booting

U8GLIB_SH1106_128X64 u8g(12, 11, 10, 9);	// SW SPI Com: CLK = 12, MOSI = 11, CS = 10, A0 = 9

// Setting up the assignated key on MULTIPLAY to the buttons on the controller.
long stop_key = 32;           //[space]
long fade_key = 48;           //[0]
long play_key = 46;           //[.]
long previous_key = 217;      //[up arrow]
long next_key = 218;          //[down arrow]
long current_state = 0;    // Use 1 for SECURE, 2 for NOT SECURE, 3 for STOP, 4 for FADE, 5 for PLAY, 6 for PREVIOUS, 7 for NEXT
long previous_state = 0;   // Use 1 for SECURE, 2 for NOT SECURE, 3 for STOP, 4 for FADE, 5 for PLAY, 6 for PREVIOUS, 7 for NEXT


//Functions to draw icons on screen
// Draw SECURE text
void drawSecure(void) {
  u8g.setFont(u8g_font_osb18);
  u8g.drawStr( 9, 48, "SECURE");
}

//Draw Credits
void drawCredits(void) {
  u8g.setFont(u8g_font_helvB12);
  u8g.drawStr( 21, 17, "MULTIPLAY");
  u8g.drawStr( 2, 35, "Controller");
  u8g.drawStr( 23, 63, "ver 1.2");    //Change version number here
}

//Draw NOT SECURE text
void drawUnsecure(void) {
  //u8g.setFont(u8g_font_unifont);
  u8g.setFont(u8g_font_osb18);
  u8g.drawStr( 34, 24, "NOT");
  u8g.drawStr( 8, 60, "SECURE");
}

//Draw STOP icon
void drawStop(void) {
  u8g.drawBox(35, 3, 60, 60);
}

//Draw FADE icon
void drawFade(void) {
  u8g.drawLine(30, 3 , 64, 3);
  u8g.drawLine(64, 3 , 98, 61);
}

//Draw PLAY icon
void drawPlay(void) {
  u8g.drawTriangle(34, 3 , 94, 32, 34, 61);
}

//Draw PREVIOUS icon
void drawPrevious(void) {
  u8g.drawTriangle(30, 16 , 62, 32, 30, 48);
  u8g.drawTriangle(64, 16 , 96, 32, 64, 48);
}

//Draw NEXT icon
void drawNext(void) {
  u8g.drawTriangle(62, 16 , 30, 32, 62, 48);
  u8g.drawTriangle(96, 16 , 64, 32, 96, 48);
}

//Draw WARNING icon - not implemented yet
void drawCircle(void) {
  u8g.drawDisc(64, 32, 30);
}

//Clear the screen
void clearScreen(void) {
}

//SETUP
void setup(void) {
  // PULL UP enabled on all inputs, to have them all HIGH except if connected to ground (button press)
  pinMode(stop_button, INPUT_PULLUP);
  pinMode(fade_button, INPUT_PULLUP);
  pinMode(play_button, INPUT_PULLUP);
  pinMode(previous_button, INPUT_PULLUP);
  pinMode(next_button, INPUT_PULLUP);
  pinMode(secure_button, INPUT_PULLUP);
  pinMode(secure_led, OUTPUT);

  Keyboard.begin();

  if ( u8g.getMode() == U8G_MODE_BW ) {
    u8g.setColorIndex(1);         // pixel on
  }
}

//LOOP
void loop(void) {

  //BOOT SCREEN
  if (firstboot == true) {
    //Draw initial CREDITS on display
    u8g.firstPage();
    do {
      drawCredits();
    } while ( u8g.nextPage() );
    //Waits 3 seconds
    delay(3000);
    //Set firstboot variable to false to avoid looping all over credits again
    firstboot = false;
    //Clear the screen
    u8g.firstPage();
    do {
      clearScreen();
    } while ( u8g.nextPage() );
    delay(1000);
  }

  //SECURE MODE
  //Check if the secure button is unpressed, to avoid sending keyboard stroke on accidental one button press.
  if (digitalRead(secure_button) == HIGH) {
    current_state = 1;
    //Turn secure led off
    digitalWrite(secure_led, LOW);

    if (current_state == previous_state) {
      //Draw SECURE on display
      u8g.firstPage();
      do {
        drawSecure();
      } while ( u8g.nextPage() );
    }
  }

  // UNSECURE MODE - Allows PLAY, STOP and FADE press
  //If secure button is pressed
  if (digitalRead(secure_button) == LOW) {
    current_state = 2;
    //Turn on secure led to warn the secure button is pressed.
    digitalWrite(secure_led, HIGH);

    if (current_state == previous_state) {
      //Draw NOT SECURE on display
      u8g.firstPage();
      do {
        drawUnsecure();
      } while ( u8g.nextPage() );
    }
    /*//Draw WARNING circle on display
      u8g.firstPage();
      do {
      drawCircle();
      } while( u8g.nextPage() );
    */

    // STOP - Stops all cues
    //If STOP button is pressed
    while (digitalRead(stop_button) == LOW) {
      current_state = 3;

      if (current_state == previous_state) {
        //Draw STOP icon on display
        u8g.firstPage();
        do {
          drawStop();
        } while ( u8g.nextPage() );
        //Send stop_key keystroke
        keystroke(stop_key, waiting);
      }
    }

    // FADE - Fade out all cues
    //If FADE button is pressed
    while (digitalRead(fade_button) == LOW) {
      current_state = 4;

      if (current_state == previous_state) {
        //Draw FADE icon on display
        u8g.firstPage();
        do {
          drawFade();
        } while ( u8g.nextPage() );
        //Send fade_key keystroke
        keystroke(fade_key, waiting);
      }
    }

    //PLAY - Play selected cue
    //If PLAY button is pressed
    while (digitalRead(play_button) == LOW) {
      current_state = 5;

      if (current_state == previous_state) {
        //Draw PLAY icon on display
        u8g.firstPage();
        do {
          drawPlay();
        } while ( u8g.nextPage() );
        //Send play_key keystroke
        keystroke(play_key, waiting);
      }
    }
  }

  // PREVIOUS - Move up on the cue list
  //If PREVIOUS button is pressed
  while (digitalRead(previous_button) == LOW) {
    current_state = 6;

    if (current_state == previous_state) {
      //Draw PREVIOUS icon on display
      u8g.firstPage();
      do {
        drawPrevious();
      } while ( u8g.nextPage() );
      //Send previous_key keystroke
      keystroke(previous_key, waiting);
    }
  }

  //NEXT - Move down on the cue list
  //If NEXT button is pressed
  while (digitalRead(next_button) == LOW) {
    current_state = 7;

    if (current_state == previous_state) {
      //Draw NEXT icon on display
      u8g.firstPage();
      do {
        drawNext();
      } while ( u8g.nextPage() );
      //Send next_key keystroke
      keystroke(next_key, waiting);
    }
  }
  previous_state = current_state;
}
//KEYSTROKE
//Function to invoke key press with delay dly to debounce key press
void keystroke(int key, int dly) {
  Keyboard.write(key);
  delay(dly);
}

I can’t seem to find where I’m messing this up.

Thank you.

and implement a if loop

Yeah, well, good luck with that. An if statement does NOT loop.

My approach didn't work out properly

Of course not. I did suggest that you look at the state change detection example, to learn how to determine that a given switch has BECOME, rather than IS, pressed. You've ignored that advice, so I don't think I can offer any more help.

An if statement does NOT loop.

You're right. It's a conditional construct. It was obviously a mistake.

Of course not. I did suggest that you look at the state change detection example, to learn how to determine that a given switch has BECOME, rather than IS, pressed.

Sorry if I didn't interpret you correctly. You didn't mention the example and I tried to implement my own way of looking at the state change.

You've ignored that advice...

I didn't ignore your advice. I Tried to achieve your suggestion on my own. Only when you mentioned the stage detection example on your former answer, I got a hold that there was something more to see on your suggestion.

... so I don't think I can offer any more help.

I apologize if you felt offended by my question in any way. I didn't mean to do it, as you probably can see from the way I write.

I try to be as polite as possible when I ask for help in any forum. I appreciated your help and I don't think I did anything that would deserve your last answer.

You're not obligated to help me out, of course, and I appreciate the path you put me on to solve my problem, but you last sentence bothered me without a need to do so. You could just have said "Take a look at the switch state change example" and I would. Didn't ask you to write the code for me, but asked if it was possible to help me out achieving the solution.

Once again, I apologize if I bothered you in any way and it won't happen again surely.

Thank you for your time.

You didn't mention the example

Take a look at reply #1.

You should look at the state change detection example, and only draw if the state has changed.

My lapse then.

I apologize for that.

Either I didn’t understand the State Change example provided by PaulS, or at least my implementation trial on several buttons, or I’m having a problem understanding the next_page() method from u8glib.

I can display the SECURE and NOT SECURE art by unpressing or pressing the SECURE button, but when I press the PLAY, STOP or FADE button, no change on the display happens and no keystroke command is activated.

I can’t also display or make the NEXT and PREVIOUS art and buttons to work, which are not dependent on the SECURE button being pressed.

This happened on my first sketch and I was able to make it work using the while loop on the sections such has:

if (digitalRead(****_button) == HIGH) {

substituting the if on each section by:

while (digitalRead(****_button) == HIGH)

On the code below this change doesn’t solve the problem, and I’m currently unaware of where the problem lies.

It looks to me, I’m stuck on the SECURE and UNSECURE section of the code, and I’m not seeing what I’m doing wrong.

/*
 * Multiplay controller 1.2 by HBT
 * Arduino controller for the Windows Multiplay software
 * for cues controls on live shows.
 * Multiplay is available at http://www.da-share.com/software/multiplay/ .
 * 
 */
 
#include "U8glib.h"
#include <HID.h>
#include <Keyboard.h>

#define stop_button 2       // Pin for the STOP button
#define fade_button 3       // Pin for the FADE ALL button
#define play_button 4       // Pin for the PLAY button
#define previous_button 5   // Pin for the PREVIOUS button
#define next_button 6       // Pin for the NEXT button
#define secure_button 7     // Pin for the SECURE button
#define secure_led 13       // Pin for SECURE led
int waiting = 500;          // Delay in ms for keyboard presses after touch.
boolean firstboot = true;   // Flag to signal booting

U8GLIB_SH1106_128X64 u8g(12, 11, 10, 9);	// SW SPI Com: CLK = 12, MOSI = 11, CS = 10, A0 = 9

// Setting up the assignated key on MULTIPLAY to the buttons on the controller. 
int stop_key = 32;        //[space]
int fade_key = 48;        //[0]
int play_key = 46;        //[.]
int previous_key = 217;   //[up arrow]
int next_key = 218;       //[down arrow]

// Defining up Switch State Change variables - current and last state
int secure_button_state = 0;              
int stop_button_state = 0;
int fade_button_state = 0;
int play_button_state = 0;
int previous_button_state = 0;
int next_button_state = 0;
int last_secure_button_state = 0;    
int last_stop_button_state = 0;
int last_fade_button_state = 0;
int last_play_button_state = 0;
int last_previous_button_state = 0;
int last_next_button_state = 0;

//Functions to draw icons on screen
// Draw SECURE text
void drawSecure(void) {
  u8g.setFont(u8g_font_osb18);
  u8g.drawStr( 9, 48, "SECURE");
}

//Draw Credits 
void drawCredits(void) {
  u8g.setFont(u8g_font_helvB12);
  u8g.drawStr( 21, 17, "MULTIPLAY");
  u8g.drawStr( 2, 35, "Controller");
  u8g.drawStr( 23, 63, "ver 1.2");    //Change version number here
}

//Draw NOT SECURE text
void drawUnsecure(void) {
  //u8g.setFont(u8g_font_unifont);
  u8g.setFont(u8g_font_osb18);
  u8g.drawStr( 34, 24, "NOT");
  u8g.drawStr( 8, 60, "SECURE");
}

//Draw STOP icon
void drawStop(void) {
  u8g.drawBox(35,3, 60,60);
}

//Draw FADE icon
void drawFade(void) {
  u8g.drawLine(30,3 , 64,3);
  u8g.drawLine(64,3 , 98, 61);
}

//Draw PLAY icon
void drawPlay(void) {
  u8g.drawTriangle(34,3 , 94,32, 34,61);
}

//Draw PREVIOUS icon
void drawPrevious(void) {
  u8g.drawTriangle(30,16 , 62,32, 30,48);
  u8g.drawTriangle(64,16 , 96,32, 64,48);
}

//Draw NEXT icon
void drawNext(void) {
  u8g.drawTriangle(62,16 , 30,32, 62,48);
  u8g.drawTriangle(96,16 , 64,32, 96,48);
}

//Draw WARNING icon - not implemented yet
void drawCircle(void) {
  u8g.drawDisc(64, 32, 30);
}

//Clear the screen
void clearScreen(void) {
}

//SETUP
void setup(void) {
  // PULL UP enabled on all inputs, to have them all HIGH except if connected to ground (button press)
  pinMode(stop_button, INPUT_PULLUP);
  pinMode(fade_button, INPUT_PULLUP);
  pinMode(play_button, INPUT_PULLUP);
  pinMode(previous_button, INPUT_PULLUP);
  pinMode(next_button, INPUT_PULLUP);
  pinMode(secure_button, INPUT_PULLUP);
  pinMode(secure_led, OUTPUT);

  Keyboard.begin();
  
  if ( u8g.getMode() == U8G_MODE_BW ) {
    u8g.setColorIndex(1);         // pixel on
  }
}

//LOOP
void loop(void) {

//BOOT SCREEN
  if(firstboot == true){
    //Draw initial CREDITS on display
    u8g.firstPage();  
    do {
      drawCredits();
    } while( u8g.nextPage() );
    //Waits 3 seconds
    delay(3000);
    //Set firstboot variable to false to avoid looping all over credits again
    firstboot = false;
    //Clear the screen
    u8g.firstPage();  
    do {
      clearScreen();
    } while( u8g.nextPage() );
    delay(1000);
    }
    
//SECURE MODE
  //Check if the secure button is unpressed, to avoid sending keyboard stroke on accidental one button press.
  secure_button_state = digitalRead(secure_button);
  if(secure_button_state != last_secure_button_state){
    if(secure_button_state == HIGH){
      //Turn secure led off 
      digitalWrite(secure_led, LOW);
    
      //Draw SECURE on display
      u8g.firstPage();  
      do {
        drawSecure();
      } while( u8g.nextPage() );
      last_secure_button_state = secure_button_state;    
    }
  
  
  
    // UNSECURE MODE - Allows PLAY, STOP and FADE press
  
    //If secure button is pressed
    if(secure_button_state==LOW){
      //Turn on secure led to warn the secure button is pressed.
      digitalWrite(secure_led, HIGH);
    
      //Draw NOT SECURE on display
      u8g.firstPage();  
      do {
        drawUnsecure();
      } while( u8g.nextPage() );
      last_secure_button_state = secure_button_state;
    
      /*//Draw WARNING circle on display
      u8g.firstPage();  
      do {
        drawCircle();
      } while( u8g.nextPage() );
      */ 
      
      // STOP - Stops all cues
      stop_button_state = digitalRead(stop_button);
      if(stop_button_state != last_stop_button_state){
        //If STOP button is pressed
        while(stop_button_state==LOW){
          //Draw STOP icon on display
          u8g.firstPage();  
          do {
            drawStop();
          } while( u8g.nextPage() );
          //Send stop_key keystroke
          keystroke(stop_key, waiting);
          last_stop_button_state = stop_button_state;
        }
      }
    
      // FADE - Fade out all cues
      fade_button_state = digitalRead(fade_button);
      if(fade_button_state != last_fade_button_state){
        //If FADE button is pressed
        if(fade_button_state == LOW){
          //Draw FADE icon on display
          u8g.firstPage();  
          do {
            drawFade();
          } while( u8g.nextPage() );
          //Send fade_key keystroke
          keystroke(fade_key, waiting);
          last_fade_button_state = fade_button_state;
        }
      } 
    
      //PLAY - Play selected cue
      play_button_state = digitalRead(play_button);
      if(play_button_state != last_play_button_state){
        //If PLAY button is pressed
        if(play_button_state == LOW){
          //Draw PLAY icon on display
          u8g.firstPage();  
          do {
            drawPlay();
          } while( u8g.nextPage() );
          //Send play_key keystroke
          keystroke(play_key, waiting);
          last_play_button_state = play_button_state;
        }
      }
    }
  }
  
  // PREVIOUS - Move up on the cue list
  previous_button_state = digitalRead(previous_button);
  if(previous_button_state != last_previous_button_state){
    //If PREVIOUS button is pressed
    if(previous_button_state == LOW){
      //Draw PREVIOUS icon on display
      u8g.firstPage();  
      do {
        drawPrevious();
      } while( u8g.nextPage() );
      //Send previous_key keystroke
      keystroke(previous_key, waiting);
      last_previous_button_state = previous_button_state;
    }
  } 

  // NEXT - Move down on the cue list
  next_button_state = digitalRead(next_button);
  if(next_button_state != last_next_button_state){
    //If NEXT button is pressed
    if(next_button_state == LOW){
      //Draw NEXT icon on display
      u8g.firstPage();  
      do {
        drawNext();
      } while( u8g.nextPage() );
      //Send previous_key keystroke
      keystroke(next_key, waiting);
      last_next_button_state = next_button_state;
    }
  }
}

//KEYSTROKE
//Function to invoke key press with delay dly to debounce key press
void keystroke(int key, int dly){
    Keyboard.write(key);
    delay(dly);
}

I’d appreciate any help or feedback given on my code.

Thank you in advance.

multiplay_controller_with_display_UTG8_ver1.2.ino.ino (7.38 KB)

I'd appreciate any help or feedback given on my code.

More functions! Lots more functions. But, put them at the BOTTOM.

    //Draw initial CREDITS on display
    u8g.firstPage(); 
    do {
      drawCredits();
    } while( u8g.nextPage() );
    //Waits 3 seconds
    delay(3000);
    //Set firstboot variable to false to avoid looping all over credits again
    firstboot = false;
    //Clear the screen
    u8g.firstPage(); 
    do {
      clearScreen();
    } while( u8g.nextPage() );
    delay(1000);

This should be in a function. It should be properly indented.

There is no reason, when looking at loop(), to have to wade through all this stuff that seems to be working.

There is no reason to have to scroll forever to find loop(), when it is the most important function.

Put every { on a line by itself. Put every } on a line by itself.

  secure_button_state = digitalRead(secure_button);
  if(secure_button_state != last_secure_button_state)
  {
     if(secure_button_state == HIGH)
     {
        drawSecureScreen();
        last_secure_button_state = secure_button_state;   
     }
  }

With the curly braces properly lined up, and the actual work shoved into a function, can you see what the problem is?

I formated the code as your suggestion, PaulS. Indeed it helps looking at the code.

I also got the boot screen on a first_boot function. No problem here.

With the curly braces properly lined up, and the actual work shoved into a function, can you see what the problem is?

As for the problem on this section, I believe to have misplaced the last_state = current_state in each block, but it doesn’t work as expected. Keeps showing the same problem.

I’ve changed it this much and can’t seem to grasp where the problem stands.

I wrote some Serial.print lines in order to see if I could understand what was going on, but I’m not able to find out where the problem is originating from.

Even when I’m able to print the button state on the begining of the

if(****_button_state= 0)

and see it changing, the art doesn’t update and the keystroke is not sent.

Completely lost at this point.

/*
   Multiplay controller 1.2 by HBT
   Arduino controller for the Windows Multiplay software
   for cues controls on live shows.
   Multiplay is available at http://www.da-share.com/software/multiplay/ .

*/

#include "U8glib.h"
#include <HID.h>
#include <Keyboard.h>

#define stop_button 2       // Pin for the STOP button
#define fade_button 3       // Pin for the FADE ALL button
#define play_button 4       // Pin for the PLAY button
#define previous_button 5   // Pin for the PREVIOUS button
#define next_button 6       // Pin for the NEXT button
#define secure_button 7     // Pin for the SECURE button
#define secure_led 13       // Pin for SECURE led
int waiting = 500;          // Delay in ms for keyboard presses after touch.
boolean firstboot = true;   // Flag to signal booting

U8GLIB_SH1106_128X64 u8g(12, 11, 10, 9); // SW SPI Com: CLK = 12, MOSI = 11, CS = 10, A0 = 9

// Setting up the assignated key on MULTIPLAY to the buttons on the controller.
int stop_key = 32;        //[space]
int fade_key = 48;        //[0]
int play_key = 46;        //[.]
int previous_key = 217;   //[up arrow]
int next_key = 218;       //[down arrow]

// Defining up Switch State Change variables - current and last state
int secure_button_state = 0;
int stop_button_state = 0;
int fade_button_state = 0;
int play_button_state = 0;
int previous_button_state = 0;
int next_button_state = 0;
int last_secure_button_state = 0;
int last_stop_button_state = 0;
int last_fade_button_state = 0;
int last_play_button_state = 0;
int last_previous_button_state = 0;
int last_next_button_state = 0;


//SETUP
void setup(void)
{
  // PULL UP enabled on all inputs, to have them all HIGH except if connected to ground (button press)
  pinMode(stop_button, INPUT_PULLUP);
  pinMode(fade_button, INPUT_PULLUP);
  pinMode(play_button, INPUT_PULLUP);
  pinMode(previous_button, INPUT_PULLUP);
  pinMode(next_button, INPUT_PULLUP);
  pinMode(secure_button, INPUT_PULLUP);
  pinMode(secure_led, OUTPUT);

  Keyboard.begin();

  Serial.begin(9600);

  if ( u8g.getMode() == U8G_MODE_BW )
  {
    u8g.setColorIndex(1);         // pixel on
  }
}

//LOOP
void loop(void)
{
  first_boot();

  //SECURE MODE
  //Check if the secure button is unpressed, to avoid sending keyboard stroke on accidental one button press.
  secure_button_state = digitalRead(secure_button);
  Serial.print("Secure state ");
  Serial.println(secure_button_state);
  Serial.print("Last Secure state ");
  Serial.println(last_secure_button_state);
  if (secure_button_state != last_secure_button_state)
  {
    if (secure_button_state == 1)
    {
      //Turn secure led off
      digitalWrite(secure_led, LOW);

      //Draw SECURE on display
      drawSecureScreen();
      last_secure_button_state = secure_button_state;
    }

    //UNSECURE MODE - Allows PLAY, STOP and FADE press
    //If secure button is pressed
    if (secure_button_state == 0)
    {
      //Turn on secure led to warn the secure button is pressed.
      digitalWrite(secure_led, HIGH);

      //Draw NOT SECURE on display
      drawUnsecureScreen();
      last_secure_button_state = secure_button_state;

      //STOP - Stops all cues
      stop_button_state = digitalRead(stop_button);
      Serial.print("Stop state ");
      Serial.println(stop_button_state);
      Serial.print("Last Stop state ");
      Serial.println(last_stop_button_state);
      if (stop_button_state != last_stop_button_state)
      {
        //If STOP button is pressed
        if (stop_button_state == 0)
        {
          clearScreen();
          drawStopScreen();
          last_stop_button_state = stop_button_state;

          //Send stop_key keystroke
          keystroke(stop_key, waiting);
        }
      }

      //FADE - Fade out all cues
      fade_button_state = digitalRead(fade_button);
      Serial.print("Fade state ");
      Serial.println(fade_button_state);
      Serial.print("Last Fade state ");
      Serial.println(last_fade_button_state);
      if (fade_button_state != last_fade_button_state)
      {
        //If FADE button is pressed
        if (fade_button_state == LOW)
        {
          drawFadeScreen();
          last_fade_button_state = fade_button_state;

          //Send fade_key keystroke
          keystroke(fade_key, waiting);
        }
      }

      //PLAY - Play selected cue
      play_button_state = digitalRead(play_button);
      Serial.print("Play state ");
      Serial.println(play_button_state);
      Serial.print("Last Play state ");
      Serial.println(last_play_button_state);
      if (play_button_state != last_play_button_state)
      {
        //If PLAY button is pressed
        if (play_button_state == LOW)
        {
          //Draw PLAY icon on display
          drawPlayScreen();
          last_play_button_state = play_button_state;

          //Send play_key keystroke
          keystroke(play_key, waiting);
        }
      }
    }
  }

  //PREVIOUS - Move up on the cue list
  previous_button_state = digitalRead(previous_button);
  Serial.print("Previous state ");
  Serial.println(previous_button_state);
  Serial.print("Last Previous state ");
  Serial.println(last_previous_button_state);
  if (previous_button_state != last_previous_button_state)
  {
    //If PREVIOUS button is pressed
    if (previous_button_state == LOW)
    {
      //Draw PREVIOUS icon on display
      drawPreviousScreen();
      last_previous_button_state = previous_button_state;
      //Send previous_key keystroke
      keystroke(previous_key, waiting);
    }
  }

  //NEXT - Move down on the cue list
  next_button_state = digitalRead(next_button);
  Serial.print("Next state ");
  Serial.println(next_button_state);
  Serial.print("Last Next state ");
  Serial.println(last_next_button_state);
  if (next_button_state != last_next_button_state)
  {
    //If NEXT button is pressed
    if (next_button_state == LOW)
    {
      //Draw NEXT icon on display
      drawNext();
      last_next_button_state = next_button_state;
      //Send previous_key keystroke
      keystroke(next_key, waiting);
    }
  }
}

//BOOT SCREEN
void first_boot(void)
{
  if (firstboot == true)
  {
    //Draw initial CREDITS on display
    u8g.firstPage();
    do
    {
      drawCredits();
    }
    while ( u8g.nextPage() );
    //Waits 3 seconds
    delay(3000);
    //Set firstboot variable to false to avoid looping all over credits again
    firstboot = false;
    //Clear the screen
    u8g.firstPage();
    do
    {
      clearScreen();
    } while ( u8g.nextPage() );
    delay(1000);
  }
}

//Functions to draw icons on screen
//Draw Credits
void drawCredits(void)
{
  u8g.setFont(u8g_font_helvB12);
  u8g.drawStr( 21, 17, "MULTIPLAY");
  u8g.drawStr( 2, 35, "Controller");
  u8g.drawStr( 23, 63, "ver 1.2");    //Change version number here
}

//Draw SECURE Screen
void drawSecureScreen(void)
{
  u8g.firstPage();
  do
  {
    drawSecure();
  }
  while ( u8g.nextPage() );
}

// Draw SECURE text
void drawSecure(void)
{
  u8g.setFont(u8g_font_osb18);
  u8g.drawStr( 9, 48, "SECURE");
}

//Draw NOT SECURE screen
void drawUnsecureScreen(void)
{
  u8g.firstPage();
  do
  {
    drawUnsecure();
  }
  while ( u8g.nextPage() );
}

//Draw NOT SECURE text
void drawUnsecure(void)
{
  //u8g.setFont(u8g_font_unifont);
  u8g.setFont(u8g_font_osb18);
  u8g.drawStr( 34, 24, "NOT");
  u8g.drawStr( 8, 60, "SECURE");
}

//Draw STOP screen
void drawStopScreen(void)
{
  u8g.firstPage();
  do
  {
    drawStop();
  }
  while ( u8g.nextPage() );
}

...

Switch states are HIGH or LOW, not 0 or 1. There is a reason for the named values.

secure_button_state must BE HIGH or LOW. There are not other possible values. So, if the value is not HIGH, there is no need to test that it is LOW.

You are still placing the code to copy the current state to the previous state in the wrong place. That should be done on every pass through loop(), not just when the state has changed.

Thank you, once again, for your help, PaulS.

I tried the values 0 and 1, to debug, but it made no difference on the overall results.

Now, I'm able to display the NEXT and PREVIOUS art when not pressing the SECURE button, but after the press, the art doesn't go back to NOT SECURE, unless I release the SECURE button, which makes display the SECURE art. After that pressing the SECURE button, will display the art for NOT SECURE again.

I'm still not able to present the PLAY, STOP and FADE art when pressing the SECURE button and the respective PLAY, STOP or FADE button. The keystroke command doesn't execute, which makes sense, since it comes after the DRAW function.

I think I need to check both conditions some other way, instead of nesting the if blocks after the SECURE button press.

So now I have something like this:

if(secure_button_state == LOW)
{
    if(play_button_state == LOW)
    {
     drawPlay();
     keystroke();
    }
    if(stop_button_state == LOW)
    {
     drawStop();
     keystroke();
    }
    if(fade_button_state == LOW)
    {
     drawFade();
     keystroke();
    }
}

This is not the actual code, just to ease up the interpretation.

When I get some time, I'll try to implement the conditionals as follows, since I believe it to make more sense and may solve the problem I have with art getting mixed on fast changes, meaning I get two arts overlaid sometimes, which messes up the display:

if(play_button_state && secure_button_state == LOW)
{
     drawPlay();
     keystroke(play);
}
else if(stop_button_state && secure_button_state == LOW)
{
     drawStop();
     keystroke(stop);
}
else if(fade_button_state && secure_button_state == LOW)
{
     drawFade();
     keystroke(fade);
}
else if(secure_button_state == LOW)
{
     drawUnsecure();
}

Anyway, regarding the first goal I had in mind on the start of this post, is getting me confused, since I'm running out of available memory to store variables and code on the arduino, which probably means, I won't be able to extend much more of the code.

Won't bigger code be more inneficient or harder to process on run time?

My first code is working and I've been using it without any problem so far, but I believe I'm gaining on trying to understand and work out some other more complex way of handling the code. It's helping me develop with another perspective and concerning myself with some details on the code.

I'll get my hands on this as soon as I can (busy month), to try to finish this one up and see the actual differences on the real world.