Shop OBEX P1 Docs P2 Docs Learn Events
Any suggestions on how to set up a scrolling message buffer? — Parallax Forums

Any suggestions on how to set up a scrolling message buffer?

Here's the setup:

On an lcd screen I select an area that will contain messages. Let's say it starts on line 30 and is 4 lines long. The messages can be up to 40 characters long. Each message is 1 line. I would like the first message to print on line 30, Next message on line 31, then next on line 32 then next on line 33. When the next message comes in I would like to take the message from line 30 and place it on line 31, line 31 to line 32, line 32 to line 33 and the new message on line 30. So as new messages come in they scroll down.

The only way I can think of doing this is by using 5 40 character byte buffers and copying one to the next as messages come in. Does this sound like a feasible way of doing this?

Anyone have any examples?

Thanks. Don

Comments

  • Don MDon M Posts: 1,652
    My one concern may be the overhead that this might take as messages come in.
  • DavidZemonDavidZemon Posts: 2,973
    edited 2016-06-30 22:44
    Sounds reasonable to me. Unless your LCD controller supports scrolling natively, I can't imagine any other way to do it. I suppose you could implement a compression algorithm :D I bet that requires more than 200 bytes though :P

    -- Edit --
    Apparently 5 * 40 is not 800
  • I must be under-thinking this. Treat the buffers as a circular list:

    You have five buffers locally. Once the fifth message comes in, you re-write the lcd screen beginning with the second buffer (2, 3, 4, 5). The sixth message get stored into buffer 1 and you re-write the screen using (3, 4, 5, 1). And so on.
  • kwinnkwinn Posts: 8,697
    I must be under-thinking this. Treat the buffers as a circular list:

    You have five buffers locally. Once the fifth message comes in, you re-write the lcd screen beginning with the second buffer (2, 3, 4, 5). The sixth message get stored into buffer 1 and you re-write the screen using (3, 4, 5, 1). And so on.

    Not under-thinking it, and you don't need an extra buffer line unless the screen needs constant refreshing. Once the newest line is received the entire buffer can be written to the screen and the new data can be written on the oldest line.
  • Don MDon M Posts: 1,652
    Here's my attempt at making something work... but it doesn't. Can someone tell me why?
    obj
      term   : "Parallax Serial Terminal"
      
    var
    
      byte  Mess_Buff1[40], Mess_Buff2[40], Mess_Buff3[40], Mess_Buff4[40], Mess_Buff5[40], Temp_Buff[40]
                  
    pub Start | idx, row, c, messagecounter, newmessage  
    
    '  term.start(31, 30, %0000, 115_200)                    ' start terminal (use PST)
      term.start(115_200)
     
      pause(2000)
    
      term.str(string("Message Buffer Test"))
    
      row := 5
      
      newmessage := 0
    
      bytefill(@Mess_Buff4, $00, 40)    'Clear buffers
      bytefill(@Mess_Buff3, $00, 40)
      bytefill(@Mess_Buff2, $00, 40)
      bytefill(@Mess_Buff1, $00, 40)
      bytefill(@Temp_Buff, $00, 40) 
    
      repeat
    
        c := term.CharIn
        if c <> -1
          idx := 0
          Temp_Buff[idx++] := c                             ' Store initial character in buffer
          repeat
            if idx => 40                                    ' If more than 40 characters inputted then quit and store a $00 at end
              Temp_Buff[39] := $00
              row++
              if row => 9                                   ' Try to keep message in 4 rows. 
                row := 5
              newmessage := 1  
              quit  
            c := term.CharIn                                ' Store characters in buffer
            if c == $0D                                     ' If "Return" key pressed then quit and store a $00 at end of string in buffer
              Temp_Buff[idx] := $00
              row++
              if row => 9
                row := 5
              newmessage := 1   
              quit
            Temp_Buff[idx] := c
            idx++
        if newmessage == 1                                  ' If new message stored...
          bytefill(@Mess_Buff4, @Mess_Buff3, 40)            ' Then move existing messages to line below
          bytefill(@Mess_Buff3, @Mess_Buff2, 40)
          bytefill(@Mess_Buff2, @Mess_Buff1, 40)
          bytefill(@Mess_Buff1, @Temp_Buff, 40)
          bytefill(@Temp_Buff, $00, 40)                     ' Store new message on first line 
          term.Position(1, row)                             ' Move to first line
          repeat 40                                         ' Clear line
            term.Char(32)
          term.str(@Mess_Buff1)                             ' Display message
          row++                                             ' And so on...
          term.Position(1, row)
          repeat 40
            term.Char(32)
          term.str(@Mess_Buff2)
          row++
          term.Position(1, row)
          repeat 40
            term.Char(32)
          term.str(@Mess_Buff3)
          row++
          term.Position(1, row)  
          repeat 40
            term.Char(32)
          term.str(@Mess_Buff4)
          newmessage := 0                                   ' Indicate new messages in new positions is complete
                                                            ' so as not to keep repeating
                
    pub pause(ms) | t
    
      t := cnt
      repeat ms
        waitcnt(t += MS_001)
        
    
  • Don MDon M Posts: 1,652
    Here's part of it fixed. To keep it at column 1 and then just change row. I forgot to tell it where to go after clearing line.
    if newmessage == 1                                  ' If new message stored...
          bytefill(@Mess_Buff4, @Mess_Buff3, 40)            ' Then move existing messages to line below
          bytefill(@Mess_Buff3, @Mess_Buff2, 40)
          bytefill(@Mess_Buff2, @Mess_Buff1, 40)
          bytefill(@Mess_Buff1, Temp_Buff, 40)
          bytefill(@Temp_Buff, $00, 40)                     ' Store new message on first line 
          term.Position(1, row)                             ' Move to first line
          repeat 40                                         ' Clear line
            term.Char(32)
          term.Position(1, row)                             ' Move to first line    
          term.str(@Mess_Buff1)                             ' Display message
          row++                                             ' And so on...
          term.Position(1, row)
          repeat 40
            term.Char(32)
          term.Position(1, row)  
          term.str(@Mess_Buff2)
          row++
          term.Position(1, row)
          repeat 40
            term.Char(32)
          term.Position(1, row)  
          term.str(@Mess_Buff3)
          row++
          term.Position(1, row)  
          repeat 40
            term.Char(32)
          term.Position(1, row)  
          term.str(@Mess_Buff4)
          newmessage := 0                                   ' Indicate new messages in new positions is complete
                                                            ' so as not to keep repeating
    
  • Don MDon M Posts: 1,652
    I think I found it... I need to use bytemove not bytefill.... hang on...
  • Don MDon M Posts: 1,652
    Ok this works. Is there any way of making it simpler? Just curious.
    obj
      term   : "Parallax Serial Terminal"
      
    var
    
      byte  Mess_Buff1[40], Mess_Buff2[40], Mess_Buff3[40], Mess_Buff4[40], Mess_Buff5[40], Temp_Buff[40]
                  
    pub Start | idx, row, c, messagecounter, newmessage  
    
    '  term.start(31, 30, %0000, 115_200)                    ' start terminal (use PST)
      term.start(115_200)
     
      pause(2000)
    
      term.str(string("Message Buffer Test"))
    
      row := 5
      
      newmessage := 0
    
      bytefill(@Mess_Buff4, $00, 40)    'Clear buffers
      bytefill(@Mess_Buff3, $00, 40)
      bytefill(@Mess_Buff2, $00, 40)
      bytefill(@Mess_Buff1, $00, 40)
      bytefill(@Temp_Buff, $00, 40) 
    
      repeat
    
        c := term.CharIn
        if c <> -1
          idx := 0
          Temp_Buff[idx++] := c                             ' Store initial character in buffer
          repeat
            if idx => 40                                    ' If more than 40 characters inputted then quit and store a $00 at end
              Temp_Buff[39] := $00
              row++
              if row => 9                                   ' Try to keep message in 4 rows. 
                row := 5
              newmessage := 1  
              quit  
            c := term.CharIn                                ' Store characters in buffer
            if c == $0D                                     ' If "Return" key pressed then quit and store a $00 at end of string in buffer
              Temp_Buff[idx] := $00
              row++
              if row => 9
                row := 5
              newmessage := 1   
              quit
            Temp_Buff[idx] := c
            idx++
        if newmessage == 1                                  ' If new message stored...
          bytemove(@Mess_Buff4, @Mess_Buff3, 40)            ' Then move existing messages to line below
          bytemove(@Mess_Buff3, @Mess_Buff2, 40)
          bytemove(@Mess_Buff2, @Mess_Buff1, 40)
          bytemove(@Mess_Buff1, @Temp_Buff, 40)             ' Store new message on first line  
          bytefill(@Temp_Buff, $00, 40)                     ' Clear temp buffer
          term.Position(1, row)                             ' Move to first line
          repeat 40                                         ' Clear line
            term.Char(32)
          term.Position(1, row)                             ' Move to first line    
          term.str(@Mess_Buff1)                             ' Display message
          row++                                             ' And so on...
          term.Position(1, row)
          repeat 40
            term.Char(32)
          term.Position(1, row)  
          term.str(@Mess_Buff2)
          row++
          term.Position(1, row)
          repeat 40
            term.Char(32)
          term.Position(1, row)  
          term.str(@Mess_Buff3)
          row++
          term.Position(1, row)  
          repeat 40
            term.Char(32)
          term.Position(1, row)  
          term.str(@Mess_Buff4)
          newmessage := 0                                   ' Indicate new messages in new positions is complete
                                                            ' so as not to keep repeating
                
    pub pause(ms) | t
    
      t := cnt
      repeat ms
        waitcnt(t += MS_001)
        
    
  • If you simply refactored all this into routines it would look a lot simpler and of course you would simply pass Mess_Buffx to the display routine etc etc.

    But is this what you mean by scrolling? Simply adding a new message in at the top and pushing the others down like a stack?
  • Don MDon M Posts: 1,652
    If you simply refactored all this into routines it would look a lot simpler and of course you would simply pass Mess_Buffx to the display routine etc etc.

    Care to share an example of what you mean?
    But is this what you mean by scrolling? Simply adding a new message in at the top and pushing the others down like a stack?

    Yes.



  • I already have a very simple version of what you are coding by simply reducing the message buffer to one big buffer, it makes everything so much simpler. I'm a bit rusty on Spin but this will only take a few minutes (or so between interruptions) before I post.
  • Peter JakackiPeter Jakacki Posts: 10,193
    edited 2016-07-01 03:06
    okay, this is my quick take on it all. The Spin compiler will arrange the message buffers back to back in memory and by putting them back to front as well it makes it easier to scroll with a single bytemove etc. There may be lots of mistakes perhaps as I haven't coded Spin in quite a while but it's mainly to give you an idea about how to approach it. I even had to download PST etc just to compile!
    obj
      term   : "Parallax Serial Terminal"
    var
    
      byte  msg4[40],msg3[40],msg2[40],msg1[40],msg0[40]
      byte  newmessage,idx,row
    
    con
    
    MS_001  = 80_000
    
    pub Start | c
    
    '  term.start(31, 30, %0000, 115_200)                    ' start terminal (use PST)
      term.start(115_200)
    
      pause(2000)
    
      term.str(string("Message Buffer Test"))
    
      row := 5
    
      newmessage ~
    
      bytefill(@msg4, $00, 200)    'Clear buffers
    
      repeat
        GetMessage
        if newmessage                                    ' If new message stored...
          Scroll
          Display(@Msg1)
          Display(@Msg2)
          Display(@Msg3)
          Display(@Msg4)
          newmessage ~                                   ' Indicate new messages in new positions is complete
    
    pub GetMessage | c
        c := term.CharIn
        if c <> -1
          idx := 0
          msg0[idx++] := c                             ' Store initial character in buffer
          repeat
            if idx => 40                                    ' If more than 40 characters inputted then quit and store a $00 at end
              msg0[39] := $00
              row++
              if row => 9                                   ' Try to keep message in 4 rows.
                row := 5
              newmessage := 1
              quit
            c := term.CharIn                                ' Store characters in buffer
            if c == $0D                                     ' If "Return" key pressed then quit and store a $00 at end of string in buffer
              msg0[idx] := $00
              row++
              if row => 9
                row := 5
              newmessage := 1
              quit
            msg0[idx] := c
            idx++
    
    pub Scroll
          bytemove(@msg4,@msg3,160)
          bytefill(@msg0, $00, 40)                          ' Clear temp buffer
                                                            ' so as not to keep repeating
    pub Display(msg)
          term.Position(1, row)                             ' Move to first line
          repeat 40                                         ' Clear line
            term.Char(32)
          term.Position(1, row)                             ' Move to first line
          term.str(msg)                             ' Display message
          row++                                             ' And so on...
    
    pub pause(ms) | t
    
      t := cnt
      repeat ms
        waitcnt(t += MS_001)
    

    EDIT: here is the compiler listing from BST to show that the compiler will place these message buffers contiguously in order in memory:
    VBASE Global Variables
    |===========================================================================|
    VBASE : 0000 BYTE Size 0028 Variable msg4
    VBASE : 0028 BYTE Size 0028 Variable msg3
    VBASE : 0050 BYTE Size 0028 Variable msg2
    VBASE : 0078 BYTE Size 0028 Variable msg1
    VBASE : 00A0 BYTE Size 0028 Variable msg0
    VBASE : 00C8 BYTE Size 0001 Variable newmessage
    VBASE : 00C9 BYTE Size 0001 Variable idx
    VBASE : 00CA BYTE Size 0001 Variable row
    |===========================================================================|
    
  • Don MDon M Posts: 1,652
    Awesome Peter. Thanks! I'll give it a go.
  • Don MDon M Posts: 1,652
    edited 2016-07-01 09:48
    It just dawned on me... I'm going to try 1 large buffer mess_buff [160] and see if I can embed a line feed at the 40th, 80th, 120th and 160th position and see if the display will respond to it in 1 write. Would save clearing each line. I'll let you know if I can make it work.
  • Don MDon M Posts: 1,652
    Actually a $00 at 160th to indicate message end
Sign In or Register to comment.