I am having trouble getting my Arduino to accept more than 128 Characters over the Serial port.
Not sure if this is just a coincidence, but I know the ATMega1280 has 128KB of memory, but that is a whole lot more than 128Characters.
The code I have written is below.
I have double checked this by asking the programme to send back to my VB.2010 programme the data it has received and it is never more than 128 Characters.
Any help on how to overcome this would be much appreciated.
Thanks
Simon
#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
void setup() {
Serial.begin(9600);
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
pinMode(13,OUTPUT);
// Print a message to the LCD.
//lcd.print("hello, world!");
delay(1000);
}
void loop() {
String ScreenName;
String Message;
String SerialInput;
int MessageLength = 0;
int MessageChr = 0;
int StartChar;
int EndChar;
String MessageToPrint;
//Send a serial request for the screenname
// when characters arrive over the serial port...
if (Serial.available()) {
// wait a bit for the entire message to arrive
delay(100);
SerialInput = "";
while (Serial.available() > 0){
SerialInput.concat(char(Serial.read()));
}
//Data sent starts with ScreenName padded to 20 characters long
ScreenName = SerialInput.substring(0,14);
lcd.noAutoscroll();
lcd.setCursor(0,0);
lcd.clear();
lcd.print(ScreenName);
delay(2000);
lcd.setCursor(16,1);
Message = SerialInput.substring(14);
MessageLength = Message.length();
//lcd.autoscroll();
for (MessageChr = 0; MessageChr <= MessageLength; MessageChr++)
{
//Get Start Character for SubString
StartChar = MessageChr - 15;
if (StartChar < 0)
{
StartChar = 0;
}
MessageToPrint = Message.substring(StartChar,MessageChr);
lcd.clear();
lcd.setCursor(0,0);
lcd.print(ScreenName);
lcd.setCursor(0,1);
lcd.println(MessageToPrint);
delay(175);
}
delay(3000);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Simons");
lcd.setCursor(0,1);
lcd.print("Twitter Ticker");
}
// delay at the end of the full loop:
delay(1000);
}
The serial input buffers are all 128 characters long.
Try reading stuff out meore frequently, or in smaller blocks.
If you need more, jjust recompile the libraries.
The firmware serial input buffer is 128 bytes long. You can't do // wait a bit for the entire message to arrive and receive a string longer than 128 bytes. (but you're only waiting 100ms, which is only time for about 100 characters at 9600bps?)
I haven't looked at how the String class works, but I'd worry that
SerialInput.concat(char(Serial.read()));
would be a horribly inefficient way to build a string, potentially re-allocating the storage for the string after each byte...
On a small system like Arduino, you are better off allocating a buffer that is the maximum size of the message you could receive. It's not like you'll be freeing up the unused memory for some other thread to use; either your sketch will be able to handle the long message, or ... not.
However I am still only getting 128 characters. How do I code this to allow me to receive more than 128 characters?
Thanks
Simon
#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
void setup() {
Serial.begin(9600);
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
pinMode(13,OUTPUT);
// Print a message to the LCD.
//lcd.print("hello, world!");
delay(1000);
}
void loop() {
String ScreenName;
String Message;
String SerialInput;
int MessageLength = 0;
int MessageChr = 0;
int StartChar;
int EndChar;
String MessageToPrint;
//Send a serial request for the screenname
// when characters arrive over the serial port...
if (Serial.available()) {
// wait a bit for the entire message to arrive
delay(100);
SerialInput = "";
while (Serial.available() > 0){
SerialInput.concat(char(Serial.read()));
if(SerialInput.length() == 127){
Serial.flush();
}
}
//Data sent starts with ScreenName padded to 20 characters long
ScreenName = SerialInput.substring(0,14);
lcd.noAutoscroll();
lcd.setCursor(0,0);
lcd.clear();
lcd.print(ScreenName);
delay(2000);
lcd.setCursor(16,1);
Message = SerialInput.substring(14);
MessageLength = Message.length();
//lcd.autoscroll();
for (MessageChr = 0; MessageChr <= MessageLength; MessageChr++)
{
//Get Start Character for SubString
StartChar = MessageChr - 15;
if (StartChar < 0)
{
StartChar = 0;
}
MessageToPrint = Message.substring(StartChar,MessageChr);
lcd.clear();
lcd.setCursor(0,0);
lcd.print(ScreenName);
lcd.setCursor(0,1);
lcd.println(MessageToPrint);
delay(175);
}
delay(3000);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Simons");
lcd.setCursor(0,1);
lcd.print("Twitter Ticker");
}
// delay at the end of the full loop:
delay(1000);
}
The receive buffer is fixed at 128 characters; if you need more, you'll have to recompile the hardware serial library, but this will make your code non-portable.
Much simpler to restructure your sketch.
Warning; beware of using flush unless you really know what you're doing.
Each hardware serial buffer is defined to be 128 bytes - you could change the declaration and recompile, but as has already been pointed-out, this would make your code non-portable.
It would be much simpler to perform the buffering in your own sketch, by reading out characters at a rate sufficient to ensure that the input buffer doesn't overflow.
// wait a bit for the entire message to arrive
delay(100);
In 100 milliseconds at 9600 baud, you will receive fewer than 100 characters.
Waiting for the whole message to arrive is a waste of time. The message should have some sort of end of message marker that you can detect. On every pass through loop, read whatever data has arrived, and add it to the end of the data already read. (Using String::concat is NOT a good way to do that). When the end of message marker arrives, process the whole message that you have buffered.
Serial.flush() throws away any data that you have not yet read. Doing that will not help you read more data.
Thanks for the advice all, however everyone keeps saying not to use concat, but does not suggest what the alternative is. Hope that does not sound ungrateful, but if you could suggest what command I should use, that would be very helpful.
The concat function causes the String class to allocate enough space to hold the existing string and the new string, but no more. Therefore, every time you append one character to a string, the entire string is copied to some other location and the one character is added.
The alternative is to use fixed size arrays for the incoming data. If you know that the incoming data will not exceed 160 characters:
char inData[160];
byte index = 0;
Then, store the character read in the array at the position in index:
I did some testing with my serial test code (below) by reducing the delay in the serial capture loop. Using a 2ms delay, I captured the below ~200 character string with an x on the end (copied and ctrl-v pasted in the serial monitor). This delay tweeking probably won't work if you have other delays in your code. If you have control over what is sending the data, then there is where you may need to break up the long string into two seperate transmissions and rejoin it if necessary in the arduino code.
// zoomkat 8-6-10 serial I/O string test
// type a string in serial monitor. then send or enter
// for IDE 0019 and later
String readString;
void setup() {
Serial.begin(9600);
Serial.println("serial test 0021"); // so I can keep track of what is loaded
}
void loop() {
while (Serial.available()) {
delay(2);
if (Serial.available() >0) {
char c = Serial.read();
readString += c;}
}
if (readString.length() >0) {
Serial.println(readString);
readString="";
}
}
OK, here is my new code with the amendments made as per the suggestion by Pauls, but I still only get 128 Characters through the Serial Port.
Have I implemented the suggestion correctly? If so then I still need a suggestion on how to correct this issue. Can anyone help, there must be a way to do this.
Thanks
Simon
#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
void setup() {
Serial.begin(9600);
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
pinMode(13,OUTPUT);
// Print a message to the LCD.
//lcd.print("hello, world!");
delay(1000);
}
void loop() {
String ScreenName;
String Message;
String SerialInput;
int MessageLength = 0;
int MessageChr = 0;
int StartChar;
int EndChar;
char inData[160];
byte index = 0;
String MessageToPrint;
//Send a serial request for the screenname
// when characters arrive over the serial port...
if (Serial.available()) {
// wait a bit for the entire message to arrive
delay(200);
SerialInput = "";
while (Serial.available() > 0){
inData[index] = Serial.read();
index++;
inData[index] = '\0'; // Keep the string NULL terminated
}
SerialInput = inData;
Serial.print(inData);
//Data sent starts with ScreenName padded to 20 characters long
ScreenName = SerialInput.substring(0,14);
lcd.noAutoscroll();
lcd.setCursor(0,0);
lcd.clear();
lcd.print(ScreenName);
delay(2000);
lcd.setCursor(16,1);
Message = SerialInput.substring(14);
MessageLength = Message.length();
//lcd.autoscroll();
for (MessageChr = 0; MessageChr <= MessageLength; MessageChr++)
{
//Get Start Character for SubString
StartChar = MessageChr - 15;
if (StartChar < 0)
{
StartChar = 0;
}
MessageToPrint = Message.substring(StartChar,MessageChr);
lcd.clear();
lcd.setCursor(0,0);
lcd.print(ScreenName);
lcd.setCursor(0,1);
lcd.println(MessageToPrint);
delay(175);
}
delay(3000);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Simons");
lcd.setCursor(0,1);
lcd.print("Twitter Ticker");
}
// delay at the end of the full loop:
delay(1000);
}
You should put your code for reading serial data in a function that you call from loop.
You should get rid of the delay after you detect the presence of serial data.
You need to have your sender send some end of message marker.
You need to have your SerialRead() function not return until that marker has been read. You might want to set some timeout for the function, and return either when the end of message marker has been received or when the timeout expires.
You should get rid of the delay after you detect the presence of serial data.
That has been demonstrated to potentially cause issues if the checking of the serial buffer for contents is used as a test for exiting the buffer capture loop and moving on to other code.
One more point to be considered is that when a UART type of serial input hardware is used, the buffer size may be fixed in hardware. If a software serial port is used, there may be some possibility of having a user defined buffer capacity.
Yes, Yes, Yes. Have now managed to get this working.
Cant thank you all enough for helping me on this. The code from Zoomcat solved the problem. At first I was struggling even with that code, but then noticed that it was taking some time for the text I supplied by serial to get back to me. I wrapped all my code in an if(Serial.available()) statement and it now works. On this occasion this worked for me, but I could imagine in other situations this would cause a problem.
Any way here is the final code which works great.
// include the library code:
#include <LiquidCrystal.h>
String ScreenName;
String Message;
String SerialInput;
int MessageLength = 0;
int MessageChr = 0;
int StartChar;
int EndChar;
String MessageToPrint;
String readString;
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
void setup() {
Serial.begin(9600);
Serial.println("serial test 0021");
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
}
void loop() {
//char c;
//Send a serial request for the screenname
// when characters arrive over the serial port...
if(Serial.available()){
while (Serial.available()) {
delay(1);
if (Serial.available() >0) {
char c = Serial.read();
readString += c;}
}
if (readString.length() >0) {
//Serial.println(readString);
SerialInput = readString;
readString="";
}
//Data sent starts with ScreenName padded to 20 characters long
ScreenName = SerialInput.substring(0,14);
lcd.setCursor(0,0);
lcd.clear();
lcd.print(ScreenName);
delay(2000);
lcd.setCursor(16,1);
Message = SerialInput.substring(14);
MessageLength = Message.length();
//lcd.autoscroll();
for (MessageChr = 0; MessageChr <= MessageLength; MessageChr++)
{
//Get Start Character for SubString
StartChar = MessageChr - 15;
if (StartChar < 0)
{
StartChar = 0;
}
MessageToPrint = Message.substring(StartChar,MessageChr);
lcd.clear();
lcd.setCursor(0,0);
lcd.print(ScreenName);
lcd.setCursor(0,1);
lcd.println(MessageToPrint);
delay(175);
}
delay(3000);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Simons");
lcd.setCursor(0,1);
lcd.print("Twitter Ticker");
// delay at the end of the full loop:
delay(1000);
}
}
You should get rid of the delay after you detect the presence of serial data.
That has been demonstrated to potentially cause issues if the checking of the serial buffer for contents is used as a test for exiting the buffer capture loop and moving on to other code.
if (Serial.available()) {
// wait a bit for the entire message to arrive
delay(200);
No, with a 128 character buffer and up to 960 characters per second, it really had to go
No, with a 128 character buffer and up to 960 characters per second, it really had to go
Somewhat vague statement. 200ms is definately too long. I successfully used 2ms to capture the string. If you use no delay in a serial buffer checking loop, there will times in the loop that data is not detected possibly causing issues (previously demonstrated).