Can't figure out how to endlessly feed text to arduino for display on LCD

I'm trying to send a continuous feed of messages, each message about 200 chars, to the arduino, then display them one at a time, scrolling them off the screen before the next one appears. At first, it was displaying only 1 or 2 messages before it stopped. I increased my RX_BUFFER_SIZE in HardwareSerial.cpp to 1024, which has enabled me to now display closer to 15 messages, but it still stops. How do I get past the stopping part and have the messages continuously to display on the LCD?

I've tried Serial.flush, Serial.end followed by another Serial.begin, I've tried doing things with the rx buffer head and tail, etc. None of it has enabled me to achieve the non-stop display of messages I'm looking for.

Any help would be greatly appreciated?

Thanks,
Tim

You probably have to save them in an array but without seeing your code (Hint :)) it's hard to say.


Rob

Does your messages come from a PC ?

If so then i tink it would be better to implement some kind of flow control, where your Arduino will signal to the PC "hey i'm ready for the next message"
If you just keep sending a stream of messages from the PC you will sooner or later run into memory issues on Arduino.

Thanks for the replies.

MikMo:
Does your messages come from a PC ?
If so then i tink it would be better to implement some kind of flow control, where your Arduino will signal to the PC "hey i'm ready for the next message"
If you just keep sending a stream of messages from the PC you will sooner or later run into memory issues on Arduino.

I get what you're saying about memory and flow control. I'm trying to figure out exactly what happens when flow control is implemented that allows you to continuously take in the next message. In that flow control process, something is reset/cleared (eg. rx buffer head & tail, pointer(s), etc.) Since I don't care if a message is lost or overwritten in the buffer once it has been written to the LCD, I either want to keep a continuous FIFO process of writing a message to the LCD and, if the buffer is full, reading in a new message to the buffer which overwrites the one that has been displayed or simply resetting the buffer each time a message is taken in and displayed so that, basically, the buffer only really holds one message at a time.

Graynomad:
You probably have to save them in an array but without seeing your code (Hint :)) it's hard to say.

Unfortunately, I have many different version of the code, none of which have worked. At the moment, I'm trying to get at the underlying process of what I'd like to achieve and then start from there. The basic outline, though, seems to be one of these two:

read a number of strings into the buffer -> display them FIFO on the LCD -> allow just-displayed message to be overwritten by new string taken in, which becomes the last string at the end of the queue

or

read in a single string -> display it on the LCD -> reset the buffer -> take in next string

Hope this makes sense.

Thanks again,
Tim

This seems simple enough unless I don't grasp the idea.

As you say have a FIFO of strings and print them one at a time to the LCD.

As long as the overall frequency of inputs string is <= output string printing time (bearing in mind that LCDs are very slow) there shouldn't be a problem.

If this is not the case then you will have issues, and flow control won't help.


Rob

If you have control of the code on the PC that sends the messages, then you could just implement the flow control i suggested, that would eliminate the need for any buffering beyond the single message to be displayed.

Arduino -> PC "I'm ready, send a mesage"

PC -> Arduino "mesage"

Arduino displays message

and you start over

As long as the overall frequency of inputs string is <= output string printing time (bearing in mind that LCDs are very slow) there shouldn't be a problem.

If this is not the case then you will have issues, and flow control won't help.

Flow control WILL help, but reducing the frequency of input strings to what can be managed by the Arduino. It will shift the burden of dealing with buffering strings to the sending system, though.

Yes, but if the overall input data speed is > the output data speed all flow control does is, as you say, shift the problem somewhere else. However I don't think that helps unless filling the PC memory and/or disk is considered a reasonable option.

Trouble is we don't know where the data is coming from, how important it is, who's reading the strings and why, are they really going to read every one.

Maybe it's OK to drop strings at the source.

OP?


Rob

Sorry, I thought I was getting email notification whenever this thread was updated, but apparently not. I appreciate all of the responses.

Graynomad:
This seems simple enough unless I don't grasp the idea.
As you say have a FIFO of strings and print them one at a time to the LCD.
As long as the overall frequency of inputs string is <= output string printing time (bearing in mind that LCDs are very slow) there shouldn't be a problem.
If this is not the case then you will have issues, and flow control won't help.
Rob

Trust me, I'm sure I'm the one who doesn't grasp the idea. Let me try to simplify the steps for my own sake and see if that helps.

1.) Java app on PC (in this instance the PC is an android) grabs a block of messages from an online source. Could be anything -- rss feed, twitter, facebook updates, stock market quotes. Each message could be up to 200 chars in length. Block may contain as many as 100 messages, but probably will be grabbing far fewer.

2.) Parse block of messages into an array of strings.

3.) Send the individual message strings, one at a time, to the arduino via (in this particular case) bluetooth.

4.) Arduino writes the string to the LCD.

5.) Next message string is sent from PC, and it continues on.

Step 1 & 2 go fine. Step 3 works to the extent that I'm getting messages on the arduino side. I start printing messages to the LCD and I run into two limits - the first being individual message length before part of the message is cut off, the second being total number of messages that print (i.e. combined number of characters for multiple messages.) The first one I can affect by increasing the "#define [sic] ByteBufferLenght" in MeetAndroid.h, but it doesn't eliminate the problem, just allows for longer messages. The second one I can affect by increasing the "#define RX_BUFFER_SIZE" in Hardware.cpp, but, once again, it doesn't eliminate the problem, just allows for more total characters.

Regarding flow control, I thought I read somewhere that MeetAndroid has some flow control built in in it's send & receive functions/statements by the way it implements the arduino Hardware library. I could be mistaken, but if it didn't, wouldn't a large array of strings overflow the arduino SRAM regardless what was happening with the LCD? (Anyone who is laughing right now, remember that I said I'm sure I'm the one who doesn't grasp the idea).

It's starting to make sense.

Does it matter if a string or strings are dropped?

If not then things get a lot easier.

part of the message is cut off

Meaning exactly what?


Rob

It's certainly preferable if strings don't get dropped. And I guess I'm wondering why they have to.

Let's say, for instance, the block of 30 messages is grabbed and parsed. I can conceivably hold onto, update, do whatever endlessly to that array of messages on the pc/android side. And if the flow control exists already through MeetAndroid (granted, that's an assumption) then the arduino is not getting flooded with message strings, it's just getting one at a time. So, based on the fact that I was able to affect the message length and combined char limits by increasing buffer settings, it would appear that my roadblock lies within the arduino rx buffer or the lcd's ddram, or both.

It just seems that the arduino should be able to store one complete 200 char message and send it to the LCD at a rate the LCD can handle, then allow it to be overwritten in either or both buffers by the next message, rather than the following messages building up in the buffer along with ones that have already printed (which aren't needed any longer, and if they were, they still exist on the PC/android side) until those buffers are full, which is what appears to be happening. But it wouldn't be the first time I was wrong.

Cheers,
Tim

OK, I've mentioned overall throughput a few times so we'll assume that

a) the overall rate of data in is < that of data out
b) flow control is happening

In this case all you should need is a simple ring buffer and the pseudo code should look something like this

if (serial.available() != 0) {
    add_char_to_buffer(serial.read());
    if ( buffer_nearly_full()) sendXOFF();
}

if (buffer_is_not_empty()) lcd.print (get_char_from_buffer ());

if (buffer_is_not_full()) sendXON();

Rob

Thanks, Rob. After doing some exhaustive testing, it appears there isn't flow control happening in the MeetAndroid send/receives that are passing my messages from the Android to the Arduino. There may be some mechanism for it already built in to the MeetAndroid library and I'm just not appending the proper end-of-message character to trigger it. I'll have to dig into it further and see if there is such a mechanism or if I need to implement something along the lines of your pseudo-code.

Your assistance is greatly appreciated.

Cheers,
Tim

Okay, here is my first attempt to implement some flow control, but it's not working. It stops after about 10 messages. Any thoughts?

#include <MeetAndroid.h>
#include <LiquidCrystal.h>

#define LCD_LINES         2
#define LCD_COLUMNS       40

LiquidCrystal lcd(10, 11, 12, 2, 3, 4, 5, 6, 7, 8, 9);

/*
  rs:  10
  rw:  13
  en:  11
  db0-db7: 2-9
*/


MeetAndroid meetAndroid;

char msgText[160];
byte msgLength;
byte charCounter;
byte positionMarker;
char space = ' ';

void setup() {
  lcd.begin(LCD_COLUMNS,LCD_LINES);
  Serial.begin(57600);
  meetAndroid.registerFunction(getMsgText, 'z');
}

void loop() {
  meetAndroid.receive();
}

void getMsgText(byte flag, byte numOfValues) {
  boolean lcdIsBusy = true;
  
  while (lcdIsBusy) {
    digitalWrite(13, HIGH);
    delay(300);
    lcdIsBusy = lcdBusy();
  }
  
  msgLength = meetAndroid.stringLength();
  meetAndroid.getString(msgText);
  
  lcd.clear();
  positionMarker = 1;

  // When the message length is less than the number of LCD columns, the message is written
  //   out to the LCD, there is a pause, then the message is scrolled off to the left

  if (msgLength < 42) {
    for (charCounter = 0; charCounter < msgLength - 1; charCounter++) {
      lcd.setCursor(charCounter, 1);
      lcd.write(msgText[charCounter]);
      delay(100);
    }
    delay(2000);
    for (charCounter = LCD_COLUMNS; charCounter < msgLength + LCD_COLUMNS; charCounter++) {
      for (int i = 0; i < LCD_COLUMNS; i++) {
        lcd.setCursor(i, 1);
        if (i + positionMarker >= msgLength - 1) {
          lcd.write(space);
        }
        else {
          lcd.write(msgText[i + positionMarker]);
        }
      }
      ++positionMarker;
      delay(175);
    }    
  }

  // If the message length is longer than the number of LCD columns, the first 40 chars are
  //   written out to the LCD, then at char 41, the message scrolls left until the last char
  //   of the message scrolls off the LCD

  else {
    for (charCounter = 0; charCounter < LCD_COLUMNS; charCounter++) {
      lcd.setCursor(charCounter, 1);
      lcd.write(msgText[charCounter]);
      delay(100);
    }
    for (charCounter = LCD_COLUMNS; charCounter < msgLength + LCD_COLUMNS; charCounter++) {
      for (int i = 0; i < LCD_COLUMNS; i++) {
        lcd.setCursor(i, 1);
        if (i + positionMarker >= msgLength - 1) {
          lcd.write(space);
        }
        else {
          lcd.write(msgText[i + positionMarker]);
        }
      }
      ++positionMarker;
      delay(175);
    }
  }

}


// Checks the LCD Busy Flag

boolean lcdBusy () {
  int busyFlag;
  digitalWrite (10, LOW);  // set RS to low
  digitalWrite (11, HIGH); // set RW to high
  delay(200);
  busyFlag = digitalRead (9); // check db7, which is pin 9
  if (busyFlag == HIGH) {
    return true;
  }
  else {
  digitalWrite (11, LOW);
  digitalWrite (10, HIGH);
  digitalWrite(13, LOW);
  delay(200);
  return false;
  }
}

Thanks,
Tim

Found this website -- LCD Tutorial for interfacing with Microcontrollers: Checking LCD busy flag - Rickey's World of Microcontrollers & Microprocessors -- and tried to adjust my lcdBusy() function accordingly. I'm still hitting a message limit, though. So, either it's not correct and not doing what I think it's doing or it is correct and I have a buffer/flow control problem in another segment of the data path.

It seems if I'm waiting to grab the next message string from the Android (meetAndroid.getString(msgText);) until after I've made sure the LCD isn't busy, the only other place I can see these messages backing up and causing overflow is the buffer in the Arduino that holds the new string from the getString which is then copied into my char string/array variable msgText. If new messages are accumulating in that buffer rather than overwriting previous, no-longer-needed messages, then the overflow could be happening with the Arduino SRAM. I've looked for places where I could reset/flush this buffer and the only one that seems directly accessible from my code is a function in the MeetArduino library called meetArduino.flush(), which I've place right after the getString command puts the latest string into msgText -- but it doesn't seem to do anything.

Any idea how to check if that buffer is actually getting cleared?

Latest version of my lcdBusy function:

boolean lcdBusy () {
  int busyFlag;
  pinMode(9, INPUT);      // set DB7/Pin 9 to input to be able to get busy flag status
  digitalWrite(12, HIGH); // set EN like this because of...
                          //   http://www.8051projects.net/lcd-interfacing/busyflag.php
                          //   I don't really understand the enable pin
  digitalWrite(10, LOW);  // set RS to low (for command register)
  digitalWrite(11, HIGH); // set RW to high (for reading)
  delay(50);
  busyFlag = digitalRead (9); // check status of DB7/Pin 9
  if (busyFlag == HIGH) {
    return true;
  }
  else {
  pinMode(9,OUTPUT);      // reset DB7/Pin 9 back to output
  digitalWrite(11, LOW);  // reset RW back to low (for writing)
  digitalWrite(10, HIGH); // reset RS back to high (for data register)
  digitalWrite(12, LOW);  // toggle EN first to low then to high because of...
  digitalWrite(12, HIGH); // http://www.8051projects.net/lcd-interfacing/busyflag.php
  delay(200);
  return false;
  }
}

Latest version of my complete code:

#include <MeetAndroid.h>
#include <LiquidCrystal.h>

#define LCD_LINES         2
#define LCD_COLUMNS       40

LiquidCrystal lcd(10, 11, 12, 2, 3, 4, 5, 6, 7, 8, 9);

/*
  rs:  10
  rw:  13
  en:  11
  db0-db7: 2-9
*/


MeetAndroid meetAndroid;

char msgText[160];
byte msgLength;
byte charCounter;
byte positionMarker;
char space = ' ';

void setup() {
  lcd.begin(LCD_COLUMNS,LCD_LINES);
  Serial.begin(57600);
  meetAndroid.registerFunction(getMsgText, 'z');
}

void loop() {
  meetAndroid.receive();
}

void getMsgText(byte flag, byte numOfValues) {
  boolean lcdIsBusy = true;
  
  while (lcdIsBusy) {
    digitalWrite(13, HIGH);
    delay(300);
    lcdIsBusy = lcdBusy();
  }
  
  msgLength = meetAndroid.stringLength();
  meetAndroid.getString(msgText);
 meetAndroid.flush();
 
  lcd.clear();
  positionMarker = 1;

  // When the message length is less than the number of LCD columns, the message is written
  //   out to the LCD, there is a pause, then the message is scrolled off to the left

  if (msgLength < 42) {
    for (charCounter = 0; charCounter < msgLength - 1; charCounter++) {
      lcd.setCursor(charCounter, 1);
      lcd.write(msgText[charCounter]);
      delay(100);
    }
    delay(2000);
    for (charCounter = LCD_COLUMNS; charCounter < msgLength + LCD_COLUMNS; charCounter++) {
      for (int i = 0; i < LCD_COLUMNS; i++) {
        lcd.setCursor(i, 1);
        if (i + positionMarker >= msgLength - 1) {
          lcd.write(space);
        }
        else {
          lcd.write(msgText[i + positionMarker]);
        }
      }
      ++positionMarker;
      delay(175);
    }    
  }

  // If the message length is longer than the number of LCD columns, the first 40 chars are
  //   written out to the LCD, then at char 41, the message scrolls left until the last char
  //   of the message scrolls off the LCD

  else {
    for (charCounter = 0; charCounter < LCD_COLUMNS; charCounter++) {
      lcd.setCursor(charCounter, 1);
      lcd.write(msgText[charCounter]);
      delay(100);
    }
    for (charCounter = LCD_COLUMNS; charCounter < msgLength + LCD_COLUMNS; charCounter++) {
      for (int i = 0; i < LCD_COLUMNS; i++) {
        lcd.setCursor(i, 1);
        if (i + positionMarker >= msgLength - 1) {
          lcd.write(space);
        }
        else {
          lcd.write(msgText[i + positionMarker]);
        }
      }
      ++positionMarker;
      delay(175);
    }
  }

}

// Checks the LCD Busy Flag

boolean lcdBusy () {
  int busyFlag;
  pinMode(9, INPUT);      // set DB7/Pin 9 to input to be able to get busy flag status
  digitalWrite(12, HIGH); // set EN like this because of...
                          //   http://www.8051projects.net/lcd-interfacing/busyflag.php
                          //   I don't really understand the enable pin
  digitalWrite(10, LOW);  // set RS to low (for command register)
  digitalWrite(11, HIGH); // set RW to high (for reading)
  delay(50);
  busyFlag = digitalRead (9); // check status of DB7/Pin 9
  if (busyFlag == HIGH) {
    return true;
  }
  else {
  pinMode(9,OUTPUT);      // reset DB7/Pin 9 back to output
  digitalWrite(11, LOW);  // reset RW back to low (for writing)
  digitalWrite(10, HIGH); // reset RS back to high (for data register)
  digitalWrite(12, LOW);  // toggle EN first to low then to high because of...
  digitalWrite(12, HIGH); // http://www.8051projects.net/lcd-interfacing/busyflag.php
  delay(200);
  return false;
  }
}

Thanks,
Tim

The code is riddled with delays(), I count about 2.5 seconds for each invocation of getMsgText() during which time nothing is happening.

I don't know if that's the problem but it can't help. How often does the Android send messages?


Rob

What does meetAndroid.receive() do?


Rob

Graynomad:
The code is riddled with delays(), I count about 2.5 seconds for each invocation of getMsgText() during which time nothing is happening.

I don't know if that's the problem but it can't help.

I went in and tweaked the delays a little bit, but the ones that remain are to control the speed of the text appearing on the LCD and the speed of the scrolling.

How often does the Android send messages?

From what I can tell, it pops a new message up/over to the arduino whenever the meetAndroid.receive command is reached in the main loop on the Arduino side.

What does meetAndroid.receive() do?

This is how I believe it works:
When the android app (see Android code below) reaches the Amarino.sendDataToArduino(this, DEVICE_ADDRESS, 'z', oneMessage); statement, it makes one string (in this case it's a string, but it could be int, float, or whatever) available to the Arduino, by either loading it to the Arduino or by notifying the Arduino that it has a string ready and available (I'm not sure which), doing this via the meetAndroid.receive statement on the Arduino side. Along with the string is sent a flag (in this case it's a 'z') which the meetAndroid.receive statement uses to trigger the registered function associated with that flag, which here is the getMsgText function. The getMsagText function triggers, at some point executing the getString statement, which grabs the string and places it into the array msgText. At some point within or at the end of these steps, the Amarino.sendDataToArduino of the Android app makes another string available (if there are more) to the Arduino and the cycle continues.

Java function that sends an array of strings to the Arduino one at a time:

    private void messagesToArduino(ArrayList<message> messagesToDisplay) {
    	int numMsgs = messagesToDisplay.size();
    	String oneMessage;
    	for (int i = 0; i < numMsgs; i++) {
    		oneMessage = messagesToDisplay.get(i).message;
		Amarino.sendDataToArduino(this, DEVICE_ADDRESS, 'z', oneMessage);
   	}
    }

Does this give you any ideas where it (or I) might be falling down?

With a quick Google I can't find anything about registerFunction() so I'll assume that registerFunction() sets a callback function that is in turn called by receive().

If this is the case then the entire string receiving is under your control because you call receive() whenever you like.

And if that is the case there should be no problems.

One thing I don't get though is why the two parameters of getMsgText (flag and numOfValues) are never used, surely they have a purpose.

I would get rid of all the LCD stuff and see if you can print strings to the serial monitor.

void getMsgText(byte flag, byte numOfValues) {
 
    meetAndroid.getString(msgText);
	serial.println (msgText);
	meetAndroid.flush();  // don't know if this is needed
}

Can you point to some documentation on the meetAndroid stuff?


Rob

I'll give a try to the printing to the serial monitor without the LCD stuff. The meetAndroid library can be found here:

http://amarino-toolkit.net/

There isn't a ton of documentation, just the files themselves and a few examples that are available when you load the library into the Arduino IDE.

More can be found here, but details are still a bit sparse:

http://www.amarino-toolkit.net/tl_files/doc/index.html

Graynomad:
If this is the case then the entire string receiving is under your control because you call receive() whenever you like. And if that is the case there should be no problems.

Yes, that's exactly what I've been thinking, yet I still have problems.

One thing I don't get though is why the two parameters of getMsgText (flag and numOfValues) are never used, surely they have a purpose.

With much of this new to me, I was even more confused by that than you. But this is how it has been implemented in all of the examples I've found and it works for me (to the extent that it works), so I have it like that. I'll take a look again at the library files. I'm sure I've looked at that, but I've now been through the files so many times trying so many different things, it's all starting to become one big blur.

Thanks again for your ongoing help.