Shop Learn
Simple file I/O in FlexBASIC — Parallax Forums

Simple file I/O in FlexBASIC

Here's a simple program to read a text file from the host PC and print it out on the serial port. It also counts how many bytes are in the file and prints the result. It works by communicating with the built-in file server in FlexProp over serial. Note that the file server data and the text printing can share the same serial line!
Adapting it to read from an SD card requires changing one line, as described in the README.md file in the Zip archive.
I tested this with FlexProp 5.1.0 (the latest) but it should work on older versions as well.

Comments

  • For those C lovers out there here is the same code written in C.

    #include <stdio.h>
    #include <fcntl.h>
    #include <propeller.h>
    #include <sys/vfs.h>
    
    uint8_t Buffer[256];
    
    
    int main(int argc, char** argv)
    {
        int fd;
    
        memset(Buffer, 0, sizeof(Buffer));
    
        printf("Mounting...\n");
        mount("/host", _vfs_open_host());
    
        fd = open("/host/hello.txt", O_RDONLY, 0);
    
        if (fd < 0)
        {
            printf("File Error: %d\n", fd);
            while (1)
                _waitms(1000);
        }
    
        read(fd, Buffer, 80);
    
        printf("Data: %s", Buffer);
    
        close(fd);
    
        printf("\nDone\n");
    
        while (1)
        {
            _waitms(1000);
        }
    }
    

    Note: the Hello.txt file needs to be in the same folder your running the code from and if you use your own terminal program you will see gibberish on your screen instead of the file opening.

    Mike

  • ersmithersmith Posts: 4,750

    Thanks Mike, but there are a few differences between your program and the BASIC one; the BASIC one counts the bytes read, and is able to read an arbitrary number of bytes (not limited to 80). I think this is a closer approximation to the BASIC code:

    //
    // simple file reader in C
    //
    #include <stdio.h>
    #include <sys/vfs.h>
    
    // normally _BAUD is set by the IDE, but if not provide a default
    #ifndef _BAUD
    #define _BAUD 230400
    #endif
    
    int main()
    {
        FILE *f;       // file we are reading
        int r;         // for results of function calls
        int size = 0;  // size of the file
    
        // mount the PC host file system on /data
        // change to _vfs_open_sdcard() for an SD card
        r = mount("/data", _vfs_open_host());
        if (r) {
            // an error happened
            perror("mount");
            return 1;
        }
        // open the file
        f = fopen("/data/hello.txt", "rb");
        if (!f) {
            // an error happened
            perror("fopen");
            return 1;
        }
        printf("reading data...\n");
        // now loop reading the data from the file
        for (;;) {
            r = fgetc(f);     // read next character
            if (r < 0) break; // got EOF
            putchar(r);       // print the character
            size++;
        }
        printf("done: read %d bytes\n", size);
        fclose(f);
        return 0;
    }
    
  • By the way, I had to rewrite your VFS SD card functions so I could use it with the P2 Edge card that does not have an SD socket yet.

    I needed to set the pins used to access an SD card reader and I did not want to have to change the include file that set the pins.

    I created a new function: vfs_set_sd(PIN_SS, PIN_CLK, PIN_MOSI, PIN_MISO); to set the pin definitions.

    I also enabled long file names since I need that to copy some files off an SD card.

    In addition, I wrote a time function so that new files get the current date and time stamp.

    I tested it with 16meg, 2g, 8g, and 16g SD cards with no issues at 300 MHz.

    Mike

  • Mike: post yer code snippets or it never happened. :):)

  • Hi

    Thanks Eric, that came just in time. :)
    I was about to ask that very thing.

    Dave

  • Hi Eric

    Yep works a treat.
    However-
    Was confused over the 'mount "/data", _vfs_open_host()' and 'open "/data/hello.txt" for input as #3'

    I guess that "/data" is 'handle' for the source of the file since there is no 'data' directory.
    If I think of 'data' (could be any name) as the place from which the typeit.bas file is found and reference everything to that.
    How would I direct it to say c:\hello.txt ?
    open "/data/c:/bhello.txt" for input as #3 does not work 'no such file or directory.
    I expect is just me being obtuse. :)

    Dave

  • ersmithersmith Posts: 4,750

    @iseries : Any chance you could send your patches my way? I'd be interested in expanding the capabilities of the SD card support.

    @tritonium : The directory being used on the host side is controlled by the loader (loadp2) and cannot be overridden by the P2 side. By default the loader provides access to the directory you're running the program from (and all of its subdirectories). If you want to make it work for your whole C: drive, you'd have to go to the "Commands > Configure Commands..." menu option and change the "Run command" entry. Replace the place where it says "-9%b" with something like "-9c:".

  • I was thinking of putting together a project to show the inns and out of the whole process.

    I built the code in a separate folder so you can run either one just by changing the mount command. Easy peasy.

    #define PIN_SS   20
    #define PIN_MISO 23
    #define PIN_CLK  22
    #define PIN_MOSI 21
    
    #include <fcntl.h>
    #include <sys/types.h>
    #include <stdio.h>
    #include <string.h>
    #include <propeller.h>
    #include <sys/vfs.h>
    
    
    char Buffer[512];
    
    
    int main(int argc, char** argv)
    {
        int i;
        DIR *dir;
        struct dirent *ent;
        int fd;
        struct tm tv;
        time_t t;
    
        tv.tm_year = 2021 - 1900;
        tv.tm_mon = 2;
        tv.tm_mday = 7;
        tv.tm_hour = 6;
        tv.tm_min = 0;
        tv.tm_sec = 0;
        t = mktime(&tv);
        settimeofday(&t, 0);
    
        vfs_set_sd(PIN_SS, PIN_CLK, PIN_MOSI, PIN_MISO);
    
        printf("Mounting...\n");
        mount("/sd", _vfs_open_sd());
    
        dir = opendir("/sd/");
    
        if (dir == NULL)
        {
            printf("Directory Failed!\n");
            while (1)
                _waitms(1000);
        }
    
        printf("Mounted..\n");
    
        while (ent = readdir(dir))
        {
            if (ent->d_type & 0x10)
                printf("d %s\n", ent->d_name);
            else
                printf("  %s\n", ent->d_name);
        }
    
        closedir(dir);
    
        printf("Opening file test.html\n");
    
        fd = open("/sd/OneCall.txt", O_RDONLY);
        if (fd < 0)
        {
            printf("File Not Found!\n");
            while (1)
                _waitms(1000);
        }
    
        i = read(fd, Buffer, 256);
    
        Buffer[i] = 0;
        printf("Buffer: %s\n", Buffer);
    
        close(fd);
    
        printf("Writing log file\n");
    
        fd = open("/sd/logfile.txt", O_RDWR | O_CREAT);
        if (fd < 0)
        {
            printf("File Creation Error!\n");
            while (1)
                _waitms(1000);
        }
    
        i =  write(fd, "Test Data only", 14);
    
        printf("Wrote %d\n", i);
    
        close(fd);
    
        printf("Done\n");
    
    
        while (1)
        {
            _waitms(1000);
        }
    }
    

    To make the code visable I added this line to vfs.h:
    struct vfs *_vfs_open_sd(void) _IMPL("filesys/fatfsx/filesystem.c");

    Mike

  • Eric

    Thanks. I'm happy to put any required files into the same directory as the code.
    The sd interests me most. An example of how a binary could be loaded and run from another cog would be appreciated, and I would prefer to use pins of my choice. In Propbasic I was able to write code to dump the directory of an sd and play an mp3 or wav file, thanks to Cluso showing a flow chart of the sd protocol process, even long filenames. But running another program from sd was beyond me.

    Dave

  • ersmithersmith Posts: 4,750

    @tritonium : When you say "how a binary could be loaded and run from another cog", what exactly do you mean hear? A simple binary that fits in a COG and just uses that COG's resources? A complete program? If the latter, do you expect it to replace the currently running program? It's quite a can of worms.

    Loading a simple binary into another COG is pretty easy, except for one gotcha: the current FlexBASIC has no way to do pure binary input, it only deals with text I/O. I still have to implement the PUT and GET binary I/O operations. It is possible (easy even) to do it from C -- you just read the binary file into a buffer, then do a _cogstart() with that buffer as the address. In BASIC you'd use the same idea, except that you'd (currently) have to have the file stored as hex and read it in that way.

    I hope to have PUT and GET done in the next version of FlexProp.

  • ersmithersmith Posts: 4,750

    @iseries: Thanks for the code sample!

  • Hi again Eric

    I guess you've realised by now that I am struggling with the 'language' to describe what I mean. I can just about write code to do what I want as long as it all fits into one flexbasic program. It would be nice if I had an SD card with a whole raft of flexbasic programs that has been proved and compiled ready to run and was able to start and stop at will any of those from say a menu in a front end program. I have a 4 inch touch sensitive lcd for which I have written a bunch of code for buttons etc etc and would like to make a crude tablet like device from which I can call up say a compass, t-o-f ranger, play music, display time and so on at the tap of a pen. I will not be connected to a pc while using this and the programs will have to be in hub code as produced by flexbasic. That code I would call a binary? Anyway I think you get the idea :)

    Sorry to be a pain.

    Dave

  • ersmithersmith Posts: 4,750

    @tritonium: Running a program from SD card is possible, but definitely goes beyond "Simple file I/O"; it's going to be fairly complicated. I think we should start a new thread for that :). I have some ideas, maybe I can come up with a demo on the weekend.

  • Eric

    Where do you get the time??
    Any crumbs you can throw me will be gratefully received. :)

    Dave

  • Cluso99Cluso99 Posts: 17,474

    I've had programs running from SD on P1 using my P1 OS for many years, so it's absolutely possible on P2.
    I have been working on my P2 OS. It's not ready for prime time yet. All the bases are done but they just won't play nice together yet.

    I can load programs and run them. I can list SD files - limited to the root directory tho at present. In fact, you can load programs from the SD root using the SD/Serial/Monitor/Debugger that resides in the P2 ROM, and I know TAQOZ in the ROM has some capabilities too. Oh, and Z80 + CPM runs on the P2 now.

    Just have a little patience. I have a P2 side project atm. Then I'll be back to complete the driver objects needed for my OS.

  • Cluso

    Yes it was knowing you had done this on P1 that made me think it was do-able. If I recall there was a problem getting addresses of where one could safely store programs on P2. I understand the mechanism on the P1 better than the P2 which I find baffling. In time as people discover and discuss this stuff I might get a handle on it. Nothing seems to phase Eric, his breadth of knowledge and abiblity to find solutions is impressive.
    I look forward to what you guys come up with. I don't know what Parallax would do without so many knowledgeable people prepared to put in their time and effort for little reward.

    Thanks both

    Dave

  • Cluso99Cluso99 Posts: 17,474

    Thank you Dave.
    I do it as a hobby so my time is spent on rewarding projects. P1 and now P2 projects are rewarding, and it’s great to work with a great company like Parallax, especially with Chip and Ken.

  • ersmithersmith Posts: 4,750

    I've added a new BASIC call, CHAIN, to FlexBASIC which will start a program from a file system (SD or host). The code is checked in to github now, but I still have a few bugs I'd like to fix before making a new release. The CHAINed program does not return; it takes over the system (although it in turn can CHAIN a new program if it wants to). There's also a technical limitation at the moment, which is that during the transition both programs will be in HUB memory simultaneously, so the total size of old and new programs must be less than available RAM. Once the chained program starts it does get to have the whole RAM, it's only while it's being loaded that it has to share with the original.

    To whet your appetite, here's a program (a simple shell) which can copy files between host and SD, and execute programs from host or SD. The commands available are:

    cd            :  show current directory path
    cd <dir>      :  change to directory dir
    copy <s> <d>  :  copy file s to d
    del <f>       :  delete ordinary file <f>
    dir           :  display contents of current directory
    dir <d>       :  display contents of directory d
    exec <f>      :  execute file <f> (never returns)
    mkdir <d>     :  create new directory d
    rmdir <d>     :  remove directory d
    

    The host is mounted at /host and the SD at /sd. So for example to copy a program from host to SD and execute it from there you'd do something like:

    copy /host/blink.bin /sd/blink.bin
    exec /sd/blink.bin
    

    We could also have just executed it directly from the host:

    exec /host/blink.bin
    

    Only the "exec" feature requires the new FlexProp, so if you comment out the line that reads "_execve(arg1, 0, 0)" you can compile this program with FlexProp 5.1.0 and get a shell that can copy files. There is a bug in 5.1.0 around subdirectories, though, so the "cd" command won't work properly; again, this is fixed in the current github source code for flexspin.

  • Eric

    Blimey that was quick!!
    I was thinking of the 'chain' command when writing my last post.
    I am beginning to realise that running code from hub means that there can only be one program running at a time no matter how many unused cogs there are. I can definitely see how 'chain' can do what I need so I can't wait to try it out, though it will take a while. I would like to choose my own pins for the SD card. I see they appear to be defined in 'sdmm.cc' in the 'fatfs' directory as pins 58 - 61 . Is it safe for me to edit that file and expect it to just work?

    Thanks once again

    Dave

  • Oh, HECK yes! Thanks, Eric!

Sign In or Register to comment.