Shop OBEX P1 Docs P2 Docs Learn Events
FlexBASIC P2 uSD explore — Parallax Forums

FlexBASIC P2 uSD explore

RsadeikaRsadeika Posts: 3,837
edited 2022-01-19 16:55 in BASIC (for Propeller)

I finally got my uSD to work on my P2 edge setup. Below is just a simple program to see if I am writing/reading anything to the uSD card.

I am using a 16GB uSD card, it seems to be reading and writing to the card without any problems, so far. With FlexBASIC, how large of a uSD card can it work with?

The next question, anybody have any FlexBASIC code to do things like DIR the card, delete files from the card, and so on.

Since, I think, it would not be practical to have some kind of mini editor, in FlexBASIC, is their a way to move files between _vfs_open_host() and _vfs_open_sdcardx(). I guess that way you could create a file on your host system, and then move the file to the uSD card, to be used with your program. This would be done without removing the uSD card.

Ray

' test1sd.bas

OPTION EXPLICIT                              ' no implicitly dec'd vars
CONST HEAPSIZE = 8192                        ' lots of elbow room

dim a$ as string
dim i as ubyte
dim msg$ as string
msg$ = "Just some text."

mount "/host", _vfs_open_host()
mount ("/sd", _vfs_open_sdcardx(5,4,3,2))  ' mount the file system at /SD node

    ' write some stuff
    open "/sd/test.txt" for output as #2 
                'print #2, "                                   " 
        'print #2, "This is another test line.";chr$(13)
        print #2, msg$;chr$(13)
                close #2                                       ' close the file

    'read it all back   
    print "Reading stuff from file..."
    open "/sd/test.txt" for input as #2       ' open for input
    do
        a$ = input$(1024, 2)                  ' get 1024 chars (or less if eof)
        print a$;                             ' print them
    loop until len(a$) = 0                    ' keep going until eof

    close #2                                  ' close file for safety before exit
    print "Test done."

Comments

  • pik33pik33 Posts: 2,366

    Now I asked the same question in another topic :) The Basic help file doesn't list any function, while there are functions we need in C. There can be 2 solutions:

    • either there are functions available in Basic, but not listed in the help file
    • or there are not, so we have to write a wrapper class in C and include it into the Basic code
  • @Rsadeika said:
    I finally got my uSD to work on my P2 edge setup. Below is just a simple program to see if I am writing/reading anything to the uSD card.

    I am using a 16GB uSD card, it seems to be reading and writing to the card without any problems, so far. With FlexBASIC, how large of a uSD card can it work with?

    Any size will work, but be aware that cards larger than 32GB will come preformatted as exFAT, you'll have to reformat to FAT32. Obnoxiously Windows' format dialog won't let you, you need a special tool.

  • pik33pik33 Posts: 2,366
    edited 2021-12-05 17:21

    you need a special tool.

    A linux PC or a Raspberry Pi can do this job, there is also a GUI available (gparted) - I have 128 GB SD with fat32 on it. The main problem is how to access the basic directory access/manipulation functions from... Basic :)

  • AribaAriba Posts: 2,690

    @Rsadeika said:
    ...
    Since, I think, it would not be practical to have some kind of mini editor, in FlexBASIC, is their a way to move files between _vfs_open_host() and _vfs_open_sdcardx(). I guess that way you could create a file on your host system, and then move the file to the uSD card, to be used with your program. This would be done without removing the uSD card.

    The Command shell for P2 in the Special Menu of FlexProp can do this.
    You can also look at the C source code for it in samples/shell, maybe the used C functions work also in flexBasic.

  • I checked out using : "The Command shell for P2 in the Special Menu of FlexProp can do this.". If I were using the P2 eval board, then it probably would work. BUT, I am using the P2 Edge rev-b board with a uSD Add-on board.

    The shell program uses the uSD pin locations that are on the P2 eval board. Using the P2 uSD Add-on board, you will have a numerous choice of pin locations. Their are problems when you use the shell.c program in the FlexBASIC program.

    In my test program I used - dim shell as class using "shell.c", this compiled without any errors , but it did not work as expected. I think this has to be run as a stand alone in FlexBASIC, not sure how to do this. The other option is to rewrite shell. c so it would work as expected, in a FlexBASIC program.

    Probably the best choice would be, use the shell in the special menu, but that means that shell program would have to have a rewrite to support the sdcardx() option.

    Ray

  • shell.c isn't designed to be used as a class, it's a stand-alone program. To modify it to run on your P2 Edge, you'll just have to change the _vfs_open_sdcard() call (near line 206 of the main() function) to the appropriate _vfs_open_sdcardx()).

    In general to copy a file from host to SD card, or vice-versa, you would mount both host and SD, then open the source file, create the destination file, and copy data from source to destination until end of file. Or, you run the shell program and type something like "copy /host/src.txt /sd/dest.txt"

  • Directory listings aren't supported from FlexBasic yet. I'll probably try to add the DIR function from FreeBasic to a future release, but for now you'll have to import a C module that uses opendir/readdir.

  • I just tried out ersmith suggestions, and the shell program worked as expected. I tried the 'exec' command, not sure what kind of file it is supposed to run.

    In my flexprop GUI program I load in shell.c, and the test.bas program. From there I can compile and run the shell.c program, when I need access to the shell stuff.

    Just curious, does anybody know what pins the uSD uses on the P2 Edge rev c board, hopefully it would be the same pins that are used on the P2 eval board.

    Ray

  • @Rsadeika said:
    I just tried out ersmith suggestions, and the shell program worked as expected. I tried the 'exec' command, not sure what kind of file it is supposed to run.

    Just normal P2 BIX binaries.

    Just curious, does anybody know what pins the uSD uses on the P2 Edge rev c board, hopefully it would be the same pins that are used on the P2 eval board.

    Yes, it's the same pins (as dictated by the bootloader)

  • Here's a simple test.bas program for showing a listing file (on the host, in this case). It uses a C class in "dir.c". The "DIR" function in that class acts very much like the FreeBasic DIR function, and I'll probably add it to the FlexBASIC language in a future release.

    c
    c
    3K
    inc
    246B
  • pik33pik33 Posts: 2,366

    To make this complete, cd is needed, and then we can explore the file system using Basic. If I remember, a shell example has cd implemented, so it should be not a problem to impotr this too.

  • The chdir function is already implemented and is in fact used in the test.bas that I posted.

  • pik33pik33 Posts: 2,366

    Now I can see it :) After a third look at the code... coffee needed...

  • pik33pik33 Posts: 2,366

    I can't find chdir in Flexprop's Basic help. How many other gems are hidden in there?

  • @pik33 said:
    I can't find chdir in Flexprop's Basic help. How many other gems are hidden in there?

    Lots. If it is a standard C call, odds are really good you can get to it from FlexBASIC as long as it doesnt return a FILE pointer.

  • pik33pik33 Posts: 2,366
    edited 2021-12-23 18:39

    It seems this basic needs a new manual.

    It doesn't understand "chdir" unless you use something else. For example "print". Then it magically understands chdir.

    as long as it doesnt return a FILE pointer.

    It also understands "fopen". This returns a FILE pointer, so... I have a lot to experiment with.

  • pik33pik33 Posts: 2,366
    edited 2022-01-19 16:01
    1. chdir cannot reach any subdirectory on SD card or I don't know how to use it.

    Edit: it returns 0 if the directory exists,, but the example dir function doesn't list anything from it. If the directory doesn't exist, it returns -1 and then the dir function lists the main directory on SD card.
    So chdir works, but then something is wrong or I don't know how to use it

    Edit 2: I added a "shell.c" as a class to the Basic program. When I called its 'main()' from Basic, it worked. It can cd, dir, etc (even doing printf using my video driver after Basic captured channel #0)
    I can call getcwd directly from Basic and it works, showing the correct current directory after several chdir called from Basic.
    However getcwd called from inside shell.c, called from Basic, (from do_dir) calls the current directory is "/" (while called from Basic directly tells "/sd/mod" which is correct.

    Edit 3: I wrote a custom cwd2 function in C, which calls cwd and returns it result. It returned "/"(wrong), while cwd called from Basic returned /sd/mod(right). Why? :(

    1. To moderators: please move this topic to Basic category.
  • Yes, since this is my thread, please move to Basic category.

    Ray

  • @pik33 said:
    1. chdir cannot reach any subdirectory on SD card or I don't know how to use it.

    chdir can reach the directories, but dir$() only works on the root directory. This is a bug in dir$(); actually in the underlying file system code (dir$() worked fine on the P9 file system code, but the FAT file system code is a bit more rigid in what it will accept). To fix it, replace the file flexprop\include\libc\unix\mount.c with the one attached to this post.

    Edit 3: I wrote a custom cwd2 function in C, which calls cwd and returns it result. It returned "/"(wrong), while cwd called from Basic returned /sd/mod(right). Why? :(

    I'd have to see the exact code, but it's possible that the class has gotten its own internal working directory that is different from the main BASIC one.

    c
    c
    4K
  • pik33pik33 Posts: 2,366
    edited 2022-01-19 19:26

    (Edit: I have to check something... )

    OK, let's write again.

    To test what is wrong with dir, I tried your shell.c example, which works without any problem, changing and listing directories on SD card.

    Then I added the shell.c as a class to Basic and called its main()

    dim cdir as class using "shell.c"
    cdir.main()
    
    

    It worked without any problems. Adding a video driver and attacung it to the channel #0 makes the shell output text on my screen :) instead of the serial port :) while stil working fine.

    So I tried to call do_dir from Basic, which failed.

    Basic -> main -> do_dir works, while Basic->do_dir does not

    I found something is wrong with cwd, so I added this function to shell.c to check it:

    char* cwd2(){
        static char* tempname1[100];
        getcwd(tempname1,100);
        return (char *) &tempname1;
        }
    
    

    Then from Basic I did this:

    mount "/sd",  _vfs_open_sdcard()
    chdir ("/sd")
    chdir("MOD")
    
    a$=cdir.cwd2()
    print a$
    
    getcwd(a$,100)
    print a$
    
    

    and got

    /
    /sd/MOD
    
    
    
  • pik33pik33 Posts: 2,366
    edited 2022-01-19 22:13

    I replaced mount.c on my PC/Flexprop 5.9.6, cwd problem remained. To test, I have to prepare a new SD card with several dirs in it (I left it at the university) and build a 5.9.7 on my RPi as there is no 5.9.7 on PC, then test if 5.9.7+new mount.c works.

    Edit: a lot of strange things happened while installing and using the flexprop 5.9.7 on the RPi 400 with a new 64-bit OS. A warning: if someone wants to connect the P2 Eval to RPi4/400, don't connect it to the USB3 port or strange things can happen (Flexprop cannot see a serial port, the RPi hangs up and cannot boot again from external SSD until P2 Eval is disconnected. A conflict somewhere on USB between SSD and FTDI, or something like this, not flexprop related. RPi 400 cannot boot with both SSD and P2 Eval are connected to USB3 port. Use USB2 port instead.

    I still have problems compiling and uploading to P2 using 5.9.7/RPi400/64bit OS. It is too late now to check what may be wrong and I am now way too tired.

  • @pik33 : I haven't had a chance to dig into this yet, but I suspect that the cdir class and the BASIC main program have different current directories. The "shell.c" example was never really designed to be a re-usable class, so it's probably better not to rely on any of its behavior.

    Here's the test program I used to get a listing from an SD card (using the modified mount.c I posted above):

    '
    ' SD Card directory listing sample
    '
    #include "dir.bi"
    
    dim fname as string
    
    ' the next two lines are for testing SD
    mount "/sd", _vfs_open_sdcard()
    chdir "/sd/sub"
    
    ' the next two lines are for testing the P9 host file system
    'mount "/host", _vfs_open_host()
    'chdir "/host/debug"
    
    print "current directory is: "; curdir$()
    print "Listing:"
    
    fname = dir$("*.*", 0)
    while fname <> "" and fname <> nil
      print fname
      fname = dir$()
    end while
    print "done"
    
  • pik33pik33 Posts: 2,366
    edited 2022-01-20 20:02

    It works, but not 100% correct. Some strange things happen.

    If the directory is long, the program fails after several (something over 100) listed files.

    So I tried to check if the number of listed files is not 128 :) and modified the code:

    '
    ' SD Card directory listing sample
    '
    #include "dir.bi"
    
    dim fname as string
    
    ' the next two lines are for testing SD
    mount "/sd", _vfs_open_sdcard()
    chdir "/sd/mod"
    
    ' the next two lines are for testing the P9 host file system
    'mount "/host", _vfs_open_host()
    'chdir "/host/debug"
    
    print "current directory is: "; curdir$()
    print "Listing:"
    
    fname = dir$("*.*", 0)
    var i=0
    while fname <> "" and fname <> nil
      i=i+1
      print i, fname
      fname = dir$()
      waitms(100)
    end while
    print "done"
    
    

    What I added is (1) var i, (2) waitms to see what is printed (3) print i.

    If there is only

    print fname

    I was correct. The number of listed files is indeed 128.

    What is strange, if I

    print i, fname

    the number of listed files is less (and = 114)


    Edit: it is HEAPSIZE that limits the number of listed files. It seems every loop allocates the memory on the heap but does not return it so the heap becomes full. HEAPSIZE=8192 increased the count to ~256

  • pik33pik33 Posts: 2,366
    edited 2022-01-20 20:23

    This line:

    string = _gc_alloc(strlen(ent->d_name)+1);

    in basic_dir.c allocates the memory for the directory entry. Nothing frees it.

    That's why the heap gets full.

    I patched this, adding a static variable for this:

    static char string[256];

    and then commented out the allocation

     //   string = _gc_alloc(strlen(ent->d_name)+1);
     //   if (string) {
            strcpy(string, ent->d_name);
     //   }
        return string;
    

    After this ugly patch there is no more limits for the number of listed files (I have 1054 of them in this directory). The problem is now worked around.

  • @pik33 : Ah, that line should have called _gc_alloc_managed instead of _gc_alloc; then the string could have been garbage collected. Thanks for the bug report, I'll fix that.

  • pik33pik33 Posts: 2,366
    edited 2022-02-07 15:18

    A glitches discovered while trying to explore SD card:

    1. There is no '..' in directory list.
    2. Attributes fbSomething are case sensitive. Edit: now I know why, they are C #defined and C is case sensitive
    3. FbNormal lists also directories

    Edit: I patched basic_dir.c, now fbNormal lists only files. The differences are: dir and system properties are checked at the end instead of beginning and then there is = instead of |= which ecludes directories from search results if fbDirectory is not specified.

    char *_basic_dir(const char *pattern = 0, unsigned attrib = 0) {
        static DIR* olddir = 0;
        static unsigned oldattrib = 0;
        static const char *oldpattern = "";
        struct dirent *ent;
        struct stat sbuf;
        int r;
        unsigned mode;
        char *string;
    
        if (pattern && *pattern) {
            // start a new search
            if (olddir) closedir(olddir);
            olddir = opendir(".");
            oldattrib = attrib;
            oldpattern = pattern;
        }
        if (!olddir) {
    #ifdef _DEBUG
            __builtin_printf("_basic_dir: olddir is NULL\n");
    #endif        
            return "";
        }
        for(;;) {
            ent = readdir(olddir);
            if (!ent) {
                closedir(olddir);
                olddir = 0;
    #ifdef _DEBUG
                __builtin_printf("_basic_dir: entry is NULL\n");
    #endif        
                return "";
            }
    #ifdef _DEBUG
            __builtin_printf("_basic_dir: entry name is %s\n", ent->d_name);
    #endif        
            /* see if the entry matches the pattern */
            if (!_pat_match(ent->d_name, oldpattern)) continue;
            /* now see if the entry matches the attributes */
            if (oldattrib != 0) {
                r = stat(ent->d_name, &sbuf);
                if (r) {
                    // error in stat, break
                    return "";
                }
                mode = sbuf.st_mode & S_IFMT;
                attrib = 0;
    
                if (ent->d_name[0] == '.') {
                    attrib |= fbHidden;
                }
                if (0 == (sbuf.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH))) {
                    attrib |= fbReadOnly;
                } else {
                    attrib |= fbArchive;
                }
    
                if ( mode == S_IFDIR ) {
                    attrib = fbDirectory;
                } else if ( mode == S_IFCHR || mode == S_IFBLK || mode == S_IFIFO) {
                    attrib = fbSystem;
                }
    
                if ( 0 == (attrib & oldattrib) ) {
                    continue;
                }
            }
            string = _gc_alloc_managed(strlen(ent->d_name)+1);
            if (string) {
                strcpy(string, ent->d_name);
            }
            return string;
        }
    }
    
Sign In or Register to comment.