My program blocking lcd

Hello people,

I'm making a midi sequencer using an arduino uno and some potentiometers.

I'm using a 1602 16X2 Character LCD Display Module with IIC/I2C/TWI/SP​​I Interface to display the values of the potentiometers to the user. Because these values are musical notes, I want the the display to be very responsive. Having read in this forum, I implemented a buffer for the screen, and whenever the user twists a potentiometer, I firstly put the new value in an array and raise a flag. On the next loop, if the program sees the flag raised, it updates only the row which shows the pot values using the "buffer" array.

Today I finally made some sound using midi and programmed some sequences with arduino! What I noticed though is that whenever I move a knob, the speed in which the sequencer is running, drops.

That would not do for a musical thing. I searched if the functions arduino is running are heavy (what I programmed I mean), but soon enough I found that the LCD is doing the damage.

lcd.write // this is a blocking function

I looked upon many different lcd libraries but I found compatible only the one I was using, the LiquidCrystal_I2C.

Now, do you have to propose me a solution in order to have a fast and not "heavy" display for my project?

Should I use other type of display? Can I connect another arduino and separate the ui and the musical functions? Is that achievable? I couldn't find any info on this. What do you propose?

Thank you people.

:)

I hate to state the obvious, but, we can't see your code.

Oh, sorry, I thought that the length of it would be a problem.

This is the included library and the lines I will write on the lcd once at the program loading…

#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 3);
char line0[21] = {'b', 'p', 'm', '1', '8', '6', ' ', '>', '>', ' ', 'n', 'o', 't', 'e', ' ', 'm', 'o', 'd', 'e', ' ', 0};
char line1[21];
char line2[21] = {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 0};
char line3[21] = {' ', ' ', ' ', ' ', '1', ' ', '2', ' ', '3', ' ', '4', ' ', '5', ' ', '6', ' ', '7', ' ', '8', ' ', 0};

This is my setup function

void setup ()
{

  Serial.begin(9600);
  mySerial.begin(31250); // set the data rate for the SoftwareSerial port
  
  pinMode (s0, OUTPUT);
  pinMode (s1, OUTPUT);
  pinMode (s2, OUTPUT);
  pinMode (speakerPin, OUTPUT);
 
  midi2.begin(MIDI_CHANNEL_OFF);

  lcd.begin();
  lcd.print("Seq8");
  lcd.setCursor(0, 0);
  lcd.print(line0);
  lcd.setCursor(0, 3);
  lcd.print(line3);
}

And the loop…

void loop ()
{
  getPotValues();
  showNotes(); //show the pot values
  buzzPlay(true);
}

I have this function to read the pot values using a multiplexer

int readSensor (const byte which)
{
  // select correct MUX channel
  digitalWrite (s0, (which & 1) ? HIGH : LOW);  // low-order bit
  digitalWrite (s1, (which & 2) ? HIGH : LOW);
  digitalWrite (s2, (which & 4) ? HIGH : LOW);  // high-order bit
  return analogRead (muxInput); // now read the sensor
}



void getPotValues()
{
  for (int i = 0; i <= 7; i ++)
  {
    int sensorValue =  readSensor(pots[i]);
    if (sensorValue - 2 <= potValues[i] && potValues[i] <= sensorValue + 2) // This is the buffer
      continue;

    potValueChanged = true;
    potValues[i] = sensorValue; //this is the buffer array
  }
}

And this is the code that will be called if I need to update the lcd

void updateLine(int line) {
  char charArray[21];

  switch (line) {
    case 0:
      strcpy( charArray, line0 );
      break;
    case 1:
      strcpy( charArray, line1 );
      break;
    case 2:
      strcpy( charArray, line2 );
      break;
    case 3:
      strcpy( charArray, line3 );
      break;
  }

  lcd.setCursor(0, line);
  lcd.print(charArray);
}

And this piece of code, updates it

void showNotes()
{
  if (!potValueChanged)
    return NULL;

  int cursor = 4;
  for (int i = 0; i <= 7; i++) {
    int noteNumber = floor(map(potValues[i], 0, 1023, 33, 93)); // converting and saving the pot values to something useful in terms of midi
    //int noteNumber = potValues[i] >> 3 ; // I also tried this bitwise operation (0~1023 -> 0~127)

    midiNoteValues[i] = noteNumber;
    line2[cursor] = getNote(noteNumber);
    int octave = getOctave(noteNumber);
    line2[cursor + 1] = octave + '0';
    cursor += 2;
  }

  potValueChanged = false;
  updateLine(2); //tried to update by setting the cursor on the loop above, but nothing different

}

And the code that plays the midi notes is this:

void buzzPlay(bool play) {
  if (!play ) {
    return NULL;
  }


  currentMillis = millis();
  if (currentMillis - noteOnMillis >= noteDuration)  //test whether the period has elapsed
  {
    
    midi2.sendControlChange(123, 0, 1); // note off

    previousIndexPlayed = previousIndexPlayed + 1;
    if (previousIndexPlayed >= 8) {
      previousIndexPlayed = 0;
    }

    midi2.sendNoteOn(midiNoteValues[previousIndexPlayed], 127 , 1);
    noteOnMillis = currentMillis;
  }
}

Well, this won’t compile because it appears the code you posted is incomplete (missing includes, missing variable names etc – next time, please just post the whole thing or attach it) so I can’t tell you there’s no errors but consider this sort of approach:

#include <LiquidCrystal_I2C.h>
#include <SoftwareSerial.h>
#include <MIDI.h>

LiquidCrystal_I2C lcd(0x27, 16, 3);
SoftwareSerial mySerial( 2, 3 );

const int s0 = 4;
const int s1 = 5;
const int s2 = 6;
const int speakerPin = 7;

char line0[21] = {'b', 'p', 'm', '1', '8', '6', ' ', '>', '>', ' ', 'n', 'o', 't', 'e', ' ', 'm', 'o', 'd', 'e', ' ', 0};
char line1[21];
char line2[21] = {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 0};
char line3[21] = {' ', ' ', ' ', ' ', '1', ' ', '2', ' ', '3', ' ', '4', ' ', '5', ' ', '6', ' ', '7', ' ', '8', ' ', 0};

char *lineArray[] =
{
    line0,
    line1,
    line2,
    line3        
};

bool
    bDispUpdate;

void setup()
{
    Serial.begin(9600);
    mySerial.begin(31250); // set the data rate for the SoftwareSerial port
  
    pinMode( s0, OUTPUT );
    pinMode( s1, OUTPUT );
    pinMode( s2, OUTPUT );
    pinMode( speakerPin, OUTPUT );
 
    midi2.begin( MIDI_CHANNEL_OFF );

    lcd.begin();
    lcd.print( "Seq8" );
    lcd.setCursor( 0, 0 );
    lcd.print( line0 );
    lcd.setCursor( 0, 3 );
    lcd.print( line3 );

    bDispUpdate = false;
    
}//setup

void loop()
{
    getPotValues();
    showNotes(); //show the pot values
    buzzPlay( true );
    
    WriteLCDLine(0);    //parameter is not important for calls from loop
    
}//loop

int readSensor (const byte which)
{
    // select correct MUX channel
    digitalWrite( s0, (which & 1) ? HIGH:LOW );     // low-order bit
    digitalWrite( s1, (which & 2) ? HIGH:LOW );
    digitalWrite( s2, (which & 4) ? HIGH:LOW );     // high-order bit
    
    return( analogRead( muxInput ) );               // now read the sensor

}//readSensor

void getPotValues()
{
    for( int i=0; i<=7; i++ )
    {
        int sensorValue =  readSensor(pots[i]);
        if (sensorValue - 2 <= potValues[i] && potValues[i] <= sensorValue + 2) // This is the buffer
            continue;

        potValueChanged = true;
        potValues[i] = sensorValue; //this is the buffer array
        
    }//for
    
}//getPotValues

#define LINE_IDLE       0
#define LINE_SEND       1
void WriteLCDLine( byte line )
{
    byte
        ch;
    static char
        *linePtr;
    static byte
        lcd_line,
        idx;
    static byte
        stateLine = LINE_IDLE;

    //update not active? just leave
    if( !bDispUpdate )
        return;
        
    switch( stateLine )
    {
        case    LINE_IDLE:
            //first pass for this instance of bDispUpdate
            lcd_line = line;                //internal copy of line for y cursor placement
            idx = 0;                        //index for x cursor placement
            linePtr = lineArray[lcd_line];  //get a pointer to the line to update
            if( *linePtr )                  //check if there's at least one valid character there
                stateLine = LINE_SEND;      //if so, move to send state
            else
                bDispUpdate = false;        //if not, clear update flag and leave
            
        break;

        case    LINE_SEND:
            if( *linePtr )                          //is character non-NULL
            {
                lcd.setCursor(idx++, lcd_line);     //set the cursor position; may not be needed if LCD auto-incs but just in case...
                lcd.write( *linePtr );              //write the current character
                linePtr++;                          //move ptr to next character in line
                
            }//if
            else
            {
                stateLine = LINE_IDLE;              //current character is NULL; set up to return to IDLE state
                bDispUpdate = false;                //clear update flag
                       
            }//else
                        
        break;
        
    }//switch
    
}//WriteLCDLine

void showNotes()
{
    //don't update if already in process of updating a line
    if( bDispUpdate )
        return;

    if (!potValueChanged)
        return;

    int cursor = 4;
    for (int i=0; i<=7; i++) 
    {
        int noteNumber = floor(map(potValues[i], 0, 1023, 33, 93)); // converting and saving the pot values to something useful in terms of midi
        //int noteNumber = potValues[i] >> 3 ; // I also tried this bitwise operation (0~1023 -> 0~127)

        midiNoteValues[i] = noteNumber;
        line2[cursor] = getNote(noteNumber);
        int octave = getOctave(noteNumber);
        line2[cursor + 1] = octave + '0';
        cursor += 2;
        
    }//for

    potValueChanged = false;
    bDispUpdate = true;
    WriteLCDLine( 2 );

}//showNotes

void buzzPlay(bool play) 
{
    if (!play ) 
        return;

    currentMillis = millis();
    if (currentMillis - noteOnMillis >= noteDuration)  //test whether the period has elapsed
    {
        midi2.sendControlChange(123, 0, 1); // note off

        previousIndexPlayed = previousIndexPlayed + 1;
        if (previousIndexPlayed >= 8) 
        {
            previousIndexPlayed = 0;
        }

        midi2.sendNoteOn(midiNoteValues[previousIndexPlayed], 127 , 1);
        noteOnMillis = currentMillis;
        
    }//if
    
}//buzzPlay

The general idea is that the line update is done in a state machine, character by character, with a call to buzzPlay() between each character. In this way, buzzPlay() is given “high priority” over the writing of the LCD. The LCD should still be responsive enough without interfering with your MIDI timing.