Go Down

Topic: Single Line LCD Scrolling (Read 1 time) previous topic - next topic

rajdarge

Nov 21, 2015, 01:26 pm Last Edit: Nov 21, 2015, 01:35 pm by rajdarge
This is a code stub (I think its called) as part of a much larger project. I am still struggling with functions , and how to pass variables and the correct syntax.
in my (working  :)  example) I am using a const char * scrollText to pass to my function LCDScroll.
I plan to use it for a char array of temperatures from a ds18b20 (5 of them) in proper readable text.
I can't have this as a const char as they will be changing all the time. So removing the const in the Function declaration will allow that to occur?
Also i use strlen and have cast it with unsigned int (they did that on cpluplus.com). Do I really need the cast? Also does using this function add a lot of overhead to my compile by having to include string.h (implicitly included). 
Finally I had to use lcd.init() as lcd.begin() wouldn't compile for some reason. I have just noticed that the first few and the last character is being truncated so I need to figure that out too.

anyway here is my code. Hopefully someone else will find it useful as I coudn't find a function to do this after looking with google for a few days (on and off).
Code: [Select]
//written for the public domain by RajDarge.
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#define LCD_COLS 20 //LCD is 20x4
#define LCD_ROWS 4

LiquidCrystal_I2C lcd(0x27,LCD_COLS,LCD_ROWS);
const char * scrollText = "01234567890 ABCDEFGH IJKLMNO PQRSTUVWX YZ 01234567890\0";
const int t_textstringlength = (unsigned int) strlen(scrollText);
void setup() {
// setup the LCD panel 20x4 here.
lcd.init();
lcd.setCursor(0,0);
lcd.backlight();
lcd.print("  Non Scrolling Text");
}
char t_chrBuffer[21];
void loop() {

lcd.setCursor(0,1);
LCDScroll(scrollText, t_chrBuffer ,  t_textstringlength);
lcd.print(t_chrBuffer);
delay(180);
}
// ****** - ******
// Function to scroll text
// INPUT: Txt2Scroll ----- const char string to scroll 
// INPUT: chrBuffer  ----- Returns buffer of substring
// INPUt: txtlen -- length of char string to print
// Returns true if succesful shift, false if to end of string
void LCDScroll(const char*  Txt2Scroll, char* chrBuffer, int txtLen)
{
  static int charIndex; //AVOIDING A GLOBAL

  if ((charIndex + LCD_COLS) > txtLen)
    charIndex = 0; //don't drop off the end of the earth
   
  for(int x = 0; x < LCD_COLS; x++)
    { // buffer containes string of LCD_COLS
      chrBuffer[x]= Txt2Scroll[charIndex + x];
    }
  charIndex++;
}

PaulS

#1
Nov 21, 2015, 04:04 pm Last Edit: Nov 21, 2015, 04:04 pm by PaulS
Quote
I can't have this as a const char as they will be changing all the time. So removing the const in the Function declaration will allow that to occur?
Yes. If some function you use causes the compiler to complain that the argument must be const char *, lie to it:

Code: [Select]
   char *whatToScroll = "Scroll this";

   someFunctionThatNeedsConstArg((const char *)whatToScroll);


Quote
Also i use strlen and have cast it with unsigned int (they did that on cpluplus.com). Do I really need the cast?
No. The value returned by strlen() is an unsigned int. There is no reason for it to be signed, since a string can't hold less than 0 characters. Casting a value of any type to the same type is pointless.

Casting from one type to another, and storing the cast value to a third type doesn't make sense, either. While you are not doing the first, you are doing the second.

Quote
Also does using this function add a lot of overhead to my compile by having to include string.h (implicitly included). 
No. The header file simply contains the function prototypes for a number of functions. None of that ends up in the hex file. The source file that contains strlen(), and a lot of other functions, will be compiled, creating an object file with a lot of functions. But, the linker only takes the functions from the object file that it needs.

Quote
Finally I had to use lcd.init() as lcd.begin() wouldn't compile for some reason.
The reason should have been quite obvious.

Code: [Select]
LCDScroll(scrollText, t_chrBuffer ,  t_textstringlength);
Passing global variables to a function is pointless.

I would expect a function with that name to have a static buffer that it worked with AND to actually display some text.

The whole idea behind scrolling is that you display only a part of a longer message, and that, at some time, you display the end of the message and the start of the message, with some space in between, at the same time.

That is, if you want to scroll "Happy Birthday, scumbag" on a 16 character wide display, you'd see
Code: [Select]
               H
              Ha
             Hap
            Happ
           Happy
          Happy
         Happy B
        Happy Bi
       Happy Bir
      Happy Birt
     Happy Birth
    Happy Birthd
   Happy Birthda
  Happy Birthday
 Happy Birthday,
Happy Birthday,
appy Birthday, s
ppy Birthday, sc
py Birthday, scu
y Birthday, scum
 Birthday, scumb
Birthday, scumba
irthday, scumbag
rthday, scumbag
thday, scumbag H
hday, scumbag Ha
etc.


But, your code never wraps around and fills the end of the array with the start of the string, when the array can hold more than the remainder of the string to scroll.

Serial.print() statements are very useful for understanding where things go wrong.

rajdarge

#2
Nov 23, 2015, 09:42 am Last Edit: Nov 23, 2015, 10:59 am by rajdarge
Thanks a heap for those suggestions and explanations. Do i need to use a const char for the text I want to scroll? or can I just use char.  I moved stuff around an it seems to be working as expected, however I it only scrolls the characters as is without any white space around. Also I think I have some problems with the code as it crashes the MC after a period of time - I think I have a memory leak somewhere --- I don't think I have the skills yet to work out where.
I still don't really understand why LCD.init() works and LCD.begin() doesnt? I only use init because some other code I had used that and begin didn't seem to work. Sorry if I seem dense, but this is the second function I've written myself.
Also I was unsure about the need to put a 0 on the end of the char to be printed. I was getting weird results when a non zero terminated char array was printed to the lcd.
Here is version 2 of the code.
Code: [Select]
//written for the public domain by RajDarge.
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#define LCD_COL 20 //LCD is 20x4
#define LCD_ROWS 4

LiquidCrystal_I2C lcd(0x27,LCD_COL,LCD_ROWS);
const char * scrollText = "Your welcome Fruitloop ";

void setup()
  {
    // setup the LCD panel 20x4 here.
    Serial.begin(250000);
    lcd.init();
    lcd.setCursor(0,0);
    lcd.backlight();
    lcd.print("  Non Scrolling Text");
  }

void loop()
  {
 

    LCD_Juumpy_scroll(scrollText, LCD_COL, 1); //just keep calling the function
    delay(60); //make text readable
 
  }
// ****** - ******
// Function to scroll text
// INPUT: Txt2Scroll ----- const char string to scroll
// INPUT: LCD_col ----- number of columns
// INPUT: t_Row ----- which row to scroll

void LCD_Juumpy_scroll(const char*  Txt2Scroll, int LCD_col, int t_Row)
{
  static int charIndex = 0 ; //AVOIDING A GLOBAL
  int txtLen =  strlen(Txt2Scroll);// get the length of the text
  char chrBuffer[LCD_col+1];//width of the sceen
  for (int x = 0; x < LCD_col; ++x) //do characters till the end
    chrBuffer[x]=char(32); //initialize chrBuffer
  chrBuffer[LCD_col]=char(0); // put a zero at theend
  if ((charIndex + LCD_col +1 ) > txtLen) //loop to end of cols
    {
      int x = 0; //use x for both loops must be outside for loop
      for( ; x < (txtLen-charIndex);x++) //if off end get till the end.
      {
        chrBuffer[x] = Txt2Scroll[charIndex + x]; //grab characters form Txt2scroll from the end
      }
       int currx = x; //memorise the counter for the chrBuffer
      for( ; x < LCD_col; x++) //continue using the x for a counter
      {
        chrBuffer[x] = Txt2Scroll[x-currx]; //Grab the text from the beginning and attach.
      }
      if (charIndex == txtLen)
        charIndex = 0; //reset counter
    }
  else
  {
    int x = 0;
      for( ; x < LCD_col; x++) 
    { // buffer containes string of LCD_col
      chrBuffer[x]= Txt2Scroll[charIndex + x];
     }
  }
 
  charIndex++; //update charindex
  chrBuffer[LCD_col]=char(0);
  lcd.setCursor(0,t_Row); //move to row of cursor.
  lcd.print(chrBuffer); //print it :)
}

PaulS

Quote
I still don't really understand why LCD.init() works and LCD.begin() doesnt?
For what definition of "works"?


rajdarge

#4
Nov 23, 2015, 12:58 pm Last Edit: Nov 23, 2015, 01:00 pm by rajdarge
well LCD.init() compiles, and LCD.begin() doesn't.
I have an idea it is because the LiquidCrystal_I2C is different to the standard LiquidCrystal library?

So I have tried to make this code as black boxy as possible, I think the reason for memory leak is because I am getting char lengths wrong somewhere (mental arithmetic was never my strong point). though it seems to be running in my main project pretty well now for a couple of hours. 

Is there somewhere I can post this thing so other people can use it?

va_cristi

#5
Mar 07, 2016, 05:37 am Last Edit: Mar 13, 2016, 08:15 pm by va_cristi
I used code with small modifications and behold the result.
Thanks for the idea.

https://www.youtube.com/watch?v=Nf1kRbXIvPk



rajdarge

great, I'm glad someone found it useful.

Go Up