Shop OBEX P1 Docs P2 Docs Learn Events
fsrw demo to read and display file contents — Parallax Forums

fsrw demo to read and display file contents

RsadeikaRsadeika Posts: 3,837
edited 2011-05-05 11:36 in Propeller 1
Below is my attempt to add some lines to a created file, then open the file to be read, and the contents displayed to the VGA screen. The part that is not working is the displaying of contents to the screen. What am I missing here?

Thanks

Ray

''Open_sd_030.spin



CON

  _clkmode = xtal1 + pll16x
  _xinfreq = 5_000_000

'' Using the DEMO board with an SD card reader
'' attched to P0 ... P4

DO  =  0
CLK =  1
DI  =  2
CS  =  3

VAR

BYTE dirbuff[12]
BYTE filebuff[256]
BYTE filesize

OBJ

  sdfat  : "fsrw"
  vga    : "MikronautsVGA64"

PUB demo | mount

  vga.start(16)    '' Start the VGA driver
  vga.cls          '' Clear the screen


'' Mount the SD card
  mount:= \sdfat.mount_explicit(DO, CLK, DI, CS)
  if mount < 0
    vga.str(string("Failed to MOUNT",$0D))
    abort

  vga.str(string("SD card was found and MOUNTED."))
  vga.out($0D)
'' Add some txt to an exiting file
  sdfat.popen(string("test3.txt"),"a")
  sdfat.pputs(string("This is the xxxx line.",$0D))
  sdfat.pputs(string("This is the yyyy line.",$0D))
  sdfat.pclose
  vga.str(string("File has been wrtten to.",$0D))

'' Show files on the SD card
  sdfat.opendir
  sdfat.nextfile(@dirbuff)
  vga.str(@dirbuff)
  vga.out($0D)
  sdfat.nextfile(@dirbuff)
  vga.str(@dirbuff)  

'' Open the file for read and display contents
  sdfat.popen(string("text3.txt"),"r")
  filesize:=sdfat.get_filesize
  sdfat.pread(@filebuff,filesize)
  vga.str(@filebuff))

'' Unmount the SD card
  sdfat.unmount
  
  vga.out($0D)
  vga.str(string("SD card has been unmounted",$0D))


Comments

  • MagIO2MagIO2 Posts: 2,243
    edited 2011-05-03 12:59
    Maybe your file is to big?

    You open it for appending. So, if you already run the program several times it's bigger than 256 bytes and thus reading the whole file will destroy your RAM (propably including the program).

    This is called a buffer overflow and is used to hack systems here and there. So, the best way to read any stream is by using a limit and rather read it in portions.
    In your case you can use a loop to read/print the file counting down the variable filesize. If I remember right pread returns how many bytes actually have been read.
    Also think about increasing the length of filebuffer by one byte and always store a 0 (zero-byte) after the last read character. Otherwise vga.str might print more than you want.
  • RsadeikaRsadeika Posts: 3,837
    edited 2011-05-04 05:53
    Ok, I got it to work. I guess the secret is, you have to display the contents one byte at a time. So, now it seems to be working as expected. Anybody have an idea as to how to delete the file(s) off the SD card?

    Ray

    ''Open_sd_030.spin
    
    
    
    CON
    
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
    
    '' Using the DEMO board with an SD card reader
    '' attched to P0 ... P4
    
    DO  =  0
    CLK =  1
    DI  =  2
    CS  =  3
    
    VAR
    
    byte tbuf[20]
    
    OBJ
    
      sdfat  : "fsrw"
      vga    : "MikronautsVGA64"
    
    PUB demo | mount,r
    
      vga.start(16)    '' Start the VGA driver
      vga.cls          '' Clear the screen
    
    
    '' Mount the SD card.
      mount:= \sdfat.mount_explicit(DO, CLK, DI, CS)
      if mount < 0
        vga.str(string("Failed to MOUNT",$0D))
        abort
    
      vga.str(string("SD card was found and MOUNTED."))
      vga.out($0D)  '' Newline
      vga.out($0D)
      
    '' Over write some txt to an exiting file.
      sdfat.popen(string("test3.txt"),"w")
      sdfat.pputs(string("This is the xxxx line.",13)) '' Just the 13 works, no 10
      sdfat.pputs(string("This is the yyyy line.",13))
      sdfat.pclose
      vga.str(string("File has been wrtten to.",$0D))
      vga.out($0D)   ''Newline
      
    '' Show files on the SD card.
      vga.str(string("SD card Directory",$0D))
      sdfat.opendir
      repeat while 0 == sdfat.nextfile(@tbuf)
        vga.str(@tbuf)
        vga.out($0D)
        
      vga.out($0D)
      
    '' Open the file for read and display contents.
      vga.str(string("Opening the file...",$0D))
      r:=sdfat.popen(string("test3.txt"),"r")
      vga.str(string("File is Open!"))
      ''vga.dec(r)
      
      repeat 2
        vga.out($0D)
    
      repeat
        r:=sdfat.pgetc    '' Reads the open file one byte at a time.
        if r < 0
          quit
       vga.out(r)         '' One char at a time.
      sdfat.pclose        '' Close the open file.
        
      sdfat.unmount       '' Unmount the card.
      
      vga.out($0D)
      vga.str(string("SD card has been unmounted",$0D))
    
    
  • Dave HeinDave Hein Posts: 6,347
    edited 2011-05-04 06:00
    sdfat.popen(string("test3.txt"),"d")
  • JonnyMacJonnyMac Posts: 9,208
    edited 2011-05-04 08:37
    I'm working on a project that reads servo data from a CSV file (created by a PC app). Since there is no line reading in FSRW I came up with this:
    pub readln(pntr, n) | c, len
    
    '' reads line of text from open file
    '' -- terminated by LF or EOF
    '' -- pntr is address of string buffer
    '' -- n is maximum number of characters to read from line
    
      len := 0                                                      ' length of returned string
    
      repeat n
        c := \sdcard.pgetc                                          ' get a character
        if ((c == LF) or (c < 0))                                   ' LF or EOF?
          quit
        else
          byte[pntr++] := c                                         ' move c to buffer
          len++                                                     ' update character count
    
      byte[pntr] := 0                                               ' terminate end of line
      
      return len
    

    The attached demo will probably be helpful, and contains a bunch of string methods that may be handy, too.
  • lonesocklonesock Posts: 917
    edited 2011-05-04 09:08
    Looks good, Jon! I'm looking into a few updates for FSRW...what do you think of the proposed modification to your function? Any way to speed it up, or make it more robust?
    pub preadln(pntr, n) : len | c
    
    '' reads line of text from open file
    '' -> pntr is address of string buffer
    '' -> n is maximum number of characters to read from line\
    '' <- len is the length of returned string
    '' -- terminated by LF or EOF
    
      repeat n
        c := pgetc                                          ' get a character
        if ((c == 10) or (c =< 0))                           ' LF or EOF? Oops, I had CR(13) instead of LF(10)
          quit                                              ' all done
        byte[pntr][len++] := c                              ' move c to buffer
      byte[pntr][len] := 0                                  ' terminate end of line
    
    thanks,
    Jonathan
  • MagIO2MagIO2 Posts: 2,243
    edited 2011-05-04 11:23
    I'd propose to have a look into my ConfigReader I added to the ObEx a while ago. It reads more than single bytes, which of course reduces the number of function calls and makes things a bit faster.
    As far as I remember it reads 512 bytes. With the function getLine it simply searches for the lineend and replaces it with zero, so the buffer can simply be used as string.
    With the next call you get back the address of the next line. If there is no lineend in the remaining buffer, the remaining part is copied to the beginning and the next bunch is loaded. That's the reason why the buffer has more than 512 bytes. It needs max. linelength more bytes.

    The config reader is doing even more. It not only reads a file, it parses it's content. If you change the separator from " " to "," you can easily read CSV files with it and find the values in an array of longs.
  • Dave HeinDave Hein Posts: 6,347
    edited 2011-05-04 11:28
    Jonathan,

    You should use "c == 10" in your comparison instead of "c == 13" (the LF character is 10). Windows text files also contain a CR, but it is always before the LF. You may want to include the CR and LF characters in the string, some programs may want to know how the string was terminated, or you could have another version of the routine that strips them out. You could make it faster if you duplicate some of the code from pread in preadln.

    Dave
  • lonesocklonesock Posts: 917
    edited 2011-05-04 11:49
    MagIO2, I like that object! The only thing I'm worried about pre-buffering the data is that the user might want to read a long right after the string, or something like that, then the data is already in the buffer, and not available from the next pgetc call. FSRW buffers up each 512-byte block already, so I could possibly do something like you did with the block juggling, just not sure if string-reading is worth the extra code/work to put into FSRW proper, especially if I could just say "go use MagIO2's ConfigReader!"

    Dave, good point about the 10 vs 13, it's my "yes, I'm using Windows" tell...sorry about that! And yes, there's plenty of room for speed-ups, again just not sure if the code-space/speed tradeoff is justified, especially for people using the Propeller IDE instead of BST (where the unused functions would get stripped). My personal leaning is to just add in the simple version of the function (maybe with a terminating character as a function parameter).

    Thanks for the feedback, everybody!
    Jonathan
  • RsadeikaRsadeika Posts: 3,837
    edited 2011-05-04 12:41
    Shown below is demo2, which is not working. In the type_file PUB, r:= sdfat.popen(string(@lbuffer),"r"), the @lbuffer is not exceptable. What I want to do is have a command 'type', then in the PUB it will ask for the file name, which will be inserted into the popen "r" command. For some reason the @lbuffer is supposed to be in the DAT section. What I would like to have is a command 'type xxxxxxxx.txt', which will open up the file and print out its contents.

    Ray

    ''demo2_sd_030.spin
    
    
    
    CON
    
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
    
    
    PS2_DATA = 26
    PS2_CLK  = 27
    
    DO  = 0
    CLK = 1
    DI  = 2
    CS  = 3
    
    ccnt = 32
    lccnt = 12
    
    VAR
    
    byte tbuf[20]
    
    byte buffer[ccnt]
    long index
    
    byte lbuffer[lccnt]
    long lindex
    
    OBJ
    
      vga    : "MikronautsVGA64"
      sdfat  : "fsrw"
      kbd    : "keyboard_010"
    
    
    PUB Start | mount
    
      vga.start(16)
      vga.cls
    
      kbd.start(PS2_DATA, PS2_CLK)
      kbd.clearkeys
    
      mount:= \sdfat.mount_explicit(DO, CLK, DI, CS)
      if mount < 0
        vga.str(string("SD Failed to MOUNT!",$0D))
        abort
    
      vga.str(string("SD card was found and MOUNTED!",$0D,$0D))
      
      Main
    
    PUB Main
    
      vga.str(string("DEMO2 SD use and control!",$0D))
      vga.out(">")
    
      repeat
        get_keystring
        vga.blink(100)
    
    
    
    PUB get_keystring | c
    
      if (kbd.gotkey == TRUE)
        c:=kbd.key
        vga.out(c)
    
        if index < constant(ccnt-1)
          buffer[index++]:=c
    
      if c == $0D
        buffer[index-1]:=0
    
        if (@buffer <> @dir)
        if (@buffer <> @type)
          vga.str(string("No such command!",$0D))
    
    
        if strcomp(@dir,@buffer)
          dir_display
    
        if strcomp(@type,@buffer)
          type_file
    
       index~
       bytefill(@buffer, 0, ccnt)
    
       vga.out(">")
    
    
    PUB type_file | r,c
    
      vga.cls
      vga.gotoxy(0,0)
    
      vga.str(string("File Name: "))  '' Type in the file name 8.3
      repeat
        c:=kbd.key
        if lindex < constant(lccnt-1)
          lbuffer[lindex++]:=c
    
        if c == $0D
          lbuffer[lindex-1]:=0
    
        
        lindex~
        bytefill(@lbuffer, 0, lccnt)
        
    
      vga.str(string("Opening a file...",$0D))
      ''r:= sdfat.popen(string("test3.txt"),"r")
      r:= sdfat.popen(string(@lbuffer),"r")     '' @lbuffer does not work
    
      repeat 2
        vga.out($0D)
    
      repeat
        r:=sdfat.pgetc
        if r < 0
          quit
        vga.out(r)
    
      sdfat.pclose
    
    
      
      vga.out($0D)
      
    
    PUB dir_display
    
      vga.cls
      vga.gotoxy(0,0)
      vga.str(string("SD Directory: ",$0D,$0D))
    
      sdfat.opendir
      repeat while 0 == sdfat.nextfile(@tbuf)
        vga.str(@tbuf)
        vga.out($0D)
    
      
    
    DAT
    dir  byte "dir",0
    type byte "type",0
    
    
  • MagIO2MagIO2 Posts: 2,243
    edited 2011-05-04 13:27
    r:= sdfat.popen(string(@lbuffer),"r")
    remove the bold part.
  • Dave HeinDave Hein Posts: 6,347
    edited 2011-05-04 13:54
    You have a number of errors in your program.
        if (@buffer <> @dir)
        if (@buffer <> @type)
          vga.str(string("No such command!",$0D))
    
    should be
        if not strcomp(@buffer, @dir) and not strcomp(@buffer, @type)
          vga.str(string("No such command!",$0D))
    
    or you could do something like
        if strcomp(@dir,@buffer)
          dir_display
        elseif strcomp(@type,@buffer)
          type_file
        else
          vga.str(string("No such command!",$0D))
    

    Also,
      if (kbd.gotkey == TRUE)
        c:=kbd.key
        vga.out(c)
    
        if index < constant(ccnt-1)
          buffer[index++]:=c
    
    should be something like
      repeat
        repeat while not kbd.gotkey
        c := kbd.key
        vga.out(c)
        if c == 13
          quit
        buffer[index++] := c
        if index => constant(ccnt-1)
          quit
      buffer[index] := 0
    
  • RsadeikaRsadeika Posts: 3,837
    edited 2011-05-04 14:07
    The program is very crude, but it works. There are two commands 'dir', and 'type'. 'dir' lists the files on the SD, and 'type' shows the contents of the file. I guess now I have to clean up the code, any suggestions?

    Ray
    ''demo2_sd_030.spin
    
    
    
    CON
    
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
    
    
    PS2_DATA = 26
    PS2_CLK  = 27
    
    DO  = 0
    CLK = 1
    DI  = 2
    CS  = 3
    
    ccnt = 32
    lccnt = 12
    
    VAR
    
    byte tbuf[20]
    
    byte buffer[ccnt]
    long index
    
    byte lbuffer[lccnt]
    long lindex
    
    OBJ
    
      vga    : "MikronautsVGA64"
      sdfat  : "fsrw"
      kbd    : "keyboard_010"
    
    
    PUB Start | mount
    
      vga.start(16)
      vga.cls
    
      kbd.start(PS2_DATA, PS2_CLK)
      kbd.clearkeys
    
      mount:= \sdfat.mount_explicit(DO, CLK, DI, CS)
      if mount < 0
        vga.str(string("SD Failed to MOUNT!",$0D))
        abort
    
      vga.str(string("SD card was found and MOUNTED!",$0D,$0D))
      
      Main
    
    PUB Main
    
      vga.str(string("DEMO2 SD use and control!",$0D))
      vga.out(">")
    
      repeat
        get_keystring
        vga.blink(100)
    
    
    
    PUB get_keystring | c
    
      if (kbd.gotkey == TRUE)
        c:=kbd.key
        vga.out(c)
    
        if index < constant(ccnt-1)
          buffer[index++]:=c
    
      if c == $0D
        buffer[index-1]:=0
    
        if (@buffer <> @dir)
        if (@buffer <> @type)
          vga.str(string("No such command!",$0D))
    
    
        if strcomp(@dir,@buffer)
          dir_display
    
        if strcomp(@type,@buffer)
          type_file
    
       index~
       bytefill(@buffer, 0, ccnt)
    
       vga.out(">")
    
    
    PUB type_file | r,c,f
    
      vga.cls
      vga.gotoxy(0,0)
    
      vga.str(string("File Name: "))  '' Type in the file name 8.3
      repeat
        if (kbd.gotkey == TRUE)
          c:=kbd.key
          vga.out(c)
           if lindex < constant(lccnt-1)
             lbuffer[lindex++]:=c
        vga.blink(100)
      until c == $0D
    ''    if c == $0D    
         lbuffer[lindex-1]:=0
         vga.str(string("Opening a file...",$0D))
    ''  '   'r:= sdfat.popen(string("test3.txt"),"r")
         r:= sdfat.popen(@lbuffer,"r")     '' @lbuffer does not work
    
          repeat 2
           vga.out($0D)
    
          repeat
           r:=sdfat.pgetc
           if r < 0
    ''     ''   quit
            return 
           vga.out(r)
        
       lindex~
       bytefill(@lbuffer, 0, lccnt)
        
    
    
    
      sdfat.pclose
    
    
      
      vga.out($0D)
      
    
    PUB dir_display
    
      vga.cls
      vga.gotoxy(0,0)
      vga.str(string("SD Directory: ",$0D,$0D))
    
      sdfat.opendir
      repeat while 0 == sdfat.nextfile(@tbuf)
        vga.str(@tbuf)
        vga.out($0D)
    
      
    
    DAT
    dir  byte "dir",0
    type byte "type",0
    
  • JonnyMacJonnyMac Posts: 9,208
    edited 2011-05-04 15:40
    @Jonathan: I tried the trim version and it doesn't work. It looks to be missing a post-increment operator for moving c to the character buffer, but even fixing that yielded an error. I'm off to acting class, I'll have detailed look tomorrow.
  • lonesocklonesock Posts: 917
    edited 2011-05-04 15:42
    Oops, forgot to change the 13 to a 10! That may be the issue. Thanks for looking!

    Jonathan
    (going to edit that right now)
  • JonnyMacJonnyMac Posts: 9,208
    edited 2011-05-04 15:50
    Whoops, my bad: adding the post-increment operator where I thought it was missing was the problem. Here's your update of my method (for use until FSRW is updated):
    pub readln(pntr, n) | c, len
    
    '' reads line of text from open file
    '' -- terminated by LF or EOF
    '' -- pntr is address of string buffer
    '' -- n is maximum number of characters to read from line
    
      len := 0                                                      ' length of returned string
    
      repeat n
        c := \sdcard.pgetc                                          ' get a character
        if ((c == LF) or (c =< 0))                                  ' LF or EOF?
          quit   
        byte[pntr][len++] := c                                      ' move c to buffer
    
      byte[pntr][len] := 0                                          ' terminate end of line
      
      return len
    

    Any chance the udpate is going to include multi-file support? I don't care about folders for my projects, but I'd love to be pulling audio and animatronic control data from the same SD card.
  • lonesocklonesock Posts: 917
    edited 2011-05-04 15:59
    Actually multi-file support already works in FSRW 2.6, just declare an array of FSRW objects, and initialize only one (they all share the block driver). Just don't manipulate each file from different cogs...for that to work I'd need to add a lock....there's a thread around here somewhere about this.

    Jonathan

    EDIT:
    if you make len your return variable, it is initialized to 0 for you, and is returned at the end of the routine, saving 2 lines of code.
  • AribaAriba Posts: 2,690
    edited 2011-05-04 18:31
    Why not make it more general and pass the termination character as a parameter.
    So you can also read a string to the next comma or space or any other character:
    pub readStr(pntr, tchar, n) : len | c
    
    '' reads text from open file
    '' -- terminated by tchar or EOF
    '' -- pntr is address of string buffer
    '' -- tchar is the termination character (i.e. 0 10 13 " " "," ";")
    '' -- n is maximum number of characters to read from line
    '' <- returns lenght of string
    
      repeat n
        c := \sdcard.pgetc                                          ' get a character
        if c == tchar or c =< 0                                     ' termination char or EOF?
          quit   
        byte[pntr][len++] := c                                      ' move c to buffer
    
      byte[pntr][len] := 0                                          ' terminate end of line
    

    Andy
  • RsadeikaRsadeika Posts: 3,837
    edited 2011-05-05 05:39
    The zip file contains the upgraded demo2, which has some added commands. I added 'del' - delete a file, 'create' - create a file. The 'create', does not have any editing features, I am still thinking about whether I want to go down that road.

    So, basically you should type in 'help' to list the command list, please let me know if there are any major problems. Of course this works with the VGA monitor, in the 64x24 mode, and is using the standard 5MHz crystal.

    Ray
  • lonesocklonesock Posts: 917
    edited 2011-05-05 08:02
    Andy, looks nice and clean!

    Ray, there is an example similar to this which I think is in the FSRW 2.6 distribution zip, but I'm including it here as an attachment. It uses the serial terminal instead of VGA and keyboard.

    Jonathan
  • RsadeikaRsadeika Posts: 3,837
    edited 2011-05-05 08:36
    I decided to add 'pedit', this creates and adds some text to a file.

    Since everything is geared for TV, or serial terminal, I decided that I wanted to work in VGA. Now, everybody has some choices, and you are not funneled into TV, or serial terminal mode. Long live VGA LOL

    Ray
  • Dave HeinDave Hein Posts: 6,347
    edited 2011-05-05 08:45
    Ray,

    You should change the logic at the beginning of get_keystring so that it returns "if kbd.gotkey <> true". Currently, if kbd.gotkey is not true the "if c == $0D" statement is executed using an uninitialized value for c. c is a stack variable, and it's initial value is unknown. Your code is working OK because vga.blink is probably changing the stack location that c uses to something other than $0D.

    You should also include the keyboard_010.spin and MikronautsVGA64.spin objects that you are using in the ZIP file. keyboard_10.spin is not in the ZIP file, and the copy of MikronautsVGA64.spin that is in the ZIP file doesn't have the blink and gotoxy methods.
  • RsadeikaRsadeika Posts: 3,837
    edited 2011-05-05 10:13
    The zip file contains all the files that I am working with. I guess I have to be a little more careful with my uploads. I forgot to mention you need to use 'Ctrl q' to end a 'pedit' session.

    Ray
  • Dave HeinDave Hein Posts: 6,347
    edited 2011-05-05 11:36
    That works better. Now it will compile. You should still add some code to handle the uninitialized state for the variable c in get_keystring. You could initialize it to zero by changing the method definition to "PUB get_keystring : c". It's either that or add a comment that says you are depending on random code outside of the get_keystring method to ensure that c does not have a value of $0D.
Sign In or Register to comment.