Shop OBEX P1 Docs P2 Docs Learn Events
uCUS.c for the QS+HIB — Parallax Forums

uCUS.c for the QS+HIB

RsadeikaRsadeika Posts: 3,824
edited 2015-01-18 12:30 in Learn with BlocklyProp
This is a program that I am running in XMMC mode on the QuickStart with HIB. I used filetest.c by Dave Hein as a base, I added a softRTC to see if it in fact it runs on the QS+HIB, so far it is running as expected. I was thinking about changing the command names to resemble DOS commands, but I think I will leave it as xNIX commands, for now.

If you want to create a file, on your blank uSD card - 'echo >> test.txt'. You can also add something to the file - 'echo This is the one. >> test.txt'. And of course if you want to check the file contents - 'cat test.txt'. Yep, this is primitive stuff, if somebody would like to add an editor, that would be a nice addition. Now if only you had COGs in XMMC mode, then ...

I noticed in SimpleIDE, there is no QUICKSTART-SDXMMC selection, but I am not sure would you would gain with that. I do not know what the usage for that would be, is it necessary for the QS+HIB?

Ray

/*
  uCUS.c
  Based on filetest.c developed by Dave Hein.
*/
#include "simpletools.h"
#include "simpletext.h"
#include "fdserial.h"
#include <sys/rtc.h>

#define BUFFERLEN 10

/* SD card */
int DO = 0, CLK = 1, DI = 2, CS = 3;


char *FindChar(char *ptr, int val);

FILE *stdinfile;
FILE *stdoutfile;

/* Print help information */
void Help()
{
    printf("Commands are help, cat, rm, ls, ll, echo \n");
    printf(" cd, pwd, mkdir, rmdir, quit, time, date \n");
    printf(" setclock, \n");
}

void Cd(int argc, char **argv)
{
    if (argc < 2) return;

    if (chdir(argv[1]))
        perror(argv[1]);
}

void Pwd(int argc, char **argv)
{
    uint8_t buffer[64];
    char *ptr = getcwd(buffer, 64);
    if (!ptr)
        perror(0);
    else
        fprintf(stdoutfile, "%s\n", ptr);
}

void Mkdir(int argc, char **argv)
{
#ifndef __PROPELLER_LMM__
    int i;

    for (i = 1; i < argc; i++)
    {
        if (mkdir(argv[i], 0))
            perror(argv[i]);
    }
#else
    printf("Not enough memory for mkdir in LMM mode\n");
#endif
}

void Rmdir(int argc, char **argv)
{
#ifndef __PROPELLER_LMM__
    int i;

    for (i = 1; i < argc; i++)
    {
        if (rmdir(argv[i]))
            perror(argv[i]);
    }
#else
    printf("Not enough memory for rmdir in LMM mode\n");
#endif
}

/* This routine implements the file cat function */
void Cat(int argc, char **argv)
{
    int i;
    int num;
    void *infile;
    uint8_t buffer[40];

    for (i = 0; i < argc; i++)
    {
        if (i == 0)
        {
            if (argc == 1 || stdinfile != stdin)
                infile = stdinfile;
            else
                continue;
        }
        else
        {
            infile = fopen(argv[i], "r");
            if (infile == 0)
            {
                perror(argv[i]);
                continue;
            }
        }
        if (infile == stdin)
        {
            while (gets(buffer))
            {
                if (buffer[0] == 4) break;
                fprintf(stdoutfile, "%s\n", buffer);
            }
        }
        else
        {
            while ((num = fread(buffer, 1, 40, infile)))
                fwrite(buffer, 1, num, stdoutfile);
        }
        if (i)
            fclose(infile);
    }
    fflush(stdout);
}

/* This routine deletes the files specified by the command line arguments */
void Remove(int argc, char **argv)
{
    int i;

    for (i = 1; i < argc; i++)
    {
        if (remove(argv[i]))
            perror(argv[i]);
    }
}

/* This routine echos the command line arguments */
void Echo(int argc, char **argv)
{
    int i;
    for (i = 1; i < argc; i++)
    {
        if (i != argc - 1)
            fprintf(stdoutfile, "%s ", argv[i]);
        else
            fprintf(stdoutfile, "%s\n", argv[i]);
    }
}

/* This routine lists the root directory or any subdirectories specified
   in the command line arguments.  If the "-l" option is specified, it
   will print the file attributes and size.  Otherwise, it will just
   print the file names.  */
void List(int argc, char **argv)
{
    int i, j;
    char *ptr;
    char fname[13];
    int32_t count = 0;
    uint32_t filesize;
    uint32_t longflag = 0;
    char *path;
    char drwx[5];
    int column;
    int prevlen;
    DIR *dirp;
    struct dirent *entry;

    // Check flags
    for (j = 1; j < argc; j++)
    {
        if (argv[j][0] == '-')
        {
            if (!strcmp(argv[j], "-l"))
                longflag = 1;
            else
                printf("Unknown option \"%s\"\n", argv[j]);
        }
        else
            count++;
    }

    // List directories
    for (j = 1; j < argc || count == 0; j++)
    {
        if (count == 0)
        {
            count--;
            path = "./";
        }
        else if (argv[j][0] == '-')
            continue;
        else
            path = argv[j];

        if (count >= 2)
            fprintf(stdoutfile, "\n%s:\n", path);

        dirp = opendir(path);

        if (!dirp)
        {
            perror(path);
            continue;
        }

        column = 0;
        prevlen = 14;
        while (entry = readdir(dirp))
        {
            if (entry->name[0] == '.') continue;
            ptr = fname;
            for (i = 0; i < 8; i++)
            {
                if (entry->name[i] == ' ') break;
                *ptr++ = tolower(entry->name[i]);
            }
            if (entry->name[8] != ' ')
            {
                *ptr++ = '.';
                for (i = 8; i < 11; i++)
                {
                    if (entry->name[i] == ' ') break;
                    *ptr++ = tolower(entry->name[i]);
                }
            }
            *ptr = 0;
            filesize = entry->filesize_3;
            filesize = (filesize << 8) | entry->filesize_2;
            filesize = (filesize << 8) | entry->filesize_1;
            filesize = (filesize << 8) | entry->filesize_0;
            strcpy(drwx, "-rw-");
            if (entry->attr & ATTR_READ_ONLY)
                drwx[2] = '-';
            if (entry->attr & ATTR_ARCHIVE)
                drwx[3] = 'x';
            if (entry->attr & ATTR_DIRECTORY)
            {
                drwx[0] = 'd';
                drwx[3] = 'x';
            }
            if (longflag)
                fprintf(stdoutfile, "%s %8d %s\n", drwx, filesize, fname);
            else if (++column == 5)
            {
                for (i = prevlen; i < 14; i++) fprintf(stdoutfile, " ");
                fprintf(stdoutfile, "%s\n", fname);
                column = 0;
                prevlen = 14;
            }
            else
            {
                for (i = prevlen; i < 14; i++) fprintf(stdoutfile, " ");
                prevlen = strlen(fname);
                fprintf(stdoutfile, "%s", fname);
            }
        }
        closedir(dirp);
        if (!longflag && column)
            fprintf(stdoutfile, "\n");
    }
}

/* This routine returns a pointer to the first character that doesn't
   match val. */
char *SkipChar(char *ptr, int val)
{
    while (*ptr)
    {
        if (*ptr != val) break;
        ptr++;
    }
    return ptr;
}

/* This routine returns a pointer to the first character that matches val. */
char *FindChar(char *ptr, int val)
{
    while (*ptr)
    {
        if (*ptr == val) break;
        ptr++;
    }
    return ptr;
}

/* This routine extracts tokens from a string that are separated by one or
   more spaces.  It returns the number of tokens found. */
int tokenize(char *ptr, char *tokens[])
{
    int num = 0;

    while (*ptr)
    {
        ptr = SkipChar(ptr, ' ');
        if (*ptr == 0) break;
        if (ptr[0] == '>')
        {
            ptr++;
            if (ptr[0] == '>')
            {
                tokens[num++] = ">>";
                ptr++;
            }
            else
                tokens[num++] = ">";
            continue;
        }
        if (ptr[0] == '<')
        {
            ptr++;
            tokens[num++] = "<";
            continue;
        }
        tokens[num++] = ptr;
        ptr = FindChar(ptr, ' ');
        if (*ptr) *ptr++ = 0;
    }
    return num;
}

/* This routine searches the list of tokens for the redirection operators
   and opens the files for input, output or append depending on the 
   operator. */
int CheckRedirection(char **tokens, int num)
{
    int i, j;

    for (i = 0; i < num-1; i++)
    {
        if (!strcmp(tokens[i], ">"))
        {
            stdoutfile = fopen(tokens[i+1], "w");
            if (!stdoutfile)
            {
                perror(tokens[i+1]);
                stdoutfile = stdout;
                return 0;
            }
        }
        else if (!strcmp(tokens[i], ">>"))
        {
            stdoutfile = fopen(tokens[i+1], "a");
            if (!stdoutfile)
            {
                perror(tokens[i+1]);
                stdoutfile = stdout;
                return 0;
            }
        }
        else if (!strcmp(tokens[i], "<"))
        {
            stdinfile = fopen(tokens[i+1], "r");
            if (!stdinfile)
            {
                perror(tokens[i+1]);
                stdinfile = stdin;
                return 0;
            }
        }
        else
            continue;
        for (j = i + 2; j < num; j++) tokens[j-2] = tokens[j];
        i--;
        num -= 2;
    }
    return num;
}

/* This routine closes files that were open for redirection */
void CloseRedirection()
{
    if (stdinfile != stdin)
    {
        fclose(stdinfile);
        stdinfile = stdin;
    }
    if (stdoutfile != stdout)
    {
        fclose(stdoutfile);
        stdoutfile = stdout;
    }
}

void Get_Date(int argc, char **argv)
{
  char timebuff[16];
  time_t now = 0;
  
  now = time(&now);
  strftime(timebuff,16,"%b %d, %Y", localtime(&now));
  fprintf(stdoutfile,"%s\n",timebuff);
}

void Get_Time(int argc, char **argv)
{
  char timebuff[16];
  time_t now = 0;
  
  now = time(&now);
  strftime(timebuff,16,"%T", localtime(&now));
  fprintf(stdoutfile,"%s\n",timebuff);
}

void Set_Clock(int argc, char **argv)
{
  struct timeval tv;
  struct tm t;

  t.tm_isdst = getdst();
  t.tm_year = prompttime("Year")-1900;
  t.tm_mon = prompttime("Month")-1;
  t.tm_mday = prompttime("Day of the month");
  t.tm_hour = prompttime("Hour");
  t.tm_min = prompttime("Minute");
  t.tm_sec = prompttime("Second");

  tv.tv_sec = mktime(&t);
  settimeofday(&tv, 0);
}

int getdst()
{
  int rc = 0;

  printf("Use daylight savings time [y/n] ? ");
  //fflush(stdout);

  rc = (getchar() == 'y') ? 1 : 0;
  getchar();
  return rc;
}

int prompttime(char *prompt)
{
  int rc = 0;
  char *endp;
  char buffer[BUFFERLEN];

  do
  {
    printf("Enter %s: ",prompt);
    //fflush(stdout);

    fgets(buffer, BUFFERLEN,stdin);
    //fflush(stdin);

    rc = strtol(buffer, &endp, 10);

    if(endp == buffer)
    {
      if('\n' == *endp)
      {
        rc = 0;
        break;
      }
      printf("Invalid entry \"%c....\" Please enter a number.\n", *endp);
    }
  } while(endp == buffer);
  return rc;
}



/* The program starts the file system.  It then loops reading commands
   and calling the appropriate routine to process it. */
int main()
{
    int num;
    char *tokens[20];
    uint8_t buffer[80];

    stdinfile = stdin;
    stdoutfile = stdout;

    // Wait for the serial terminal to start
    waitcnt(CNT + CLKFREQ);
    _rtc_start_timekeeping_cog();
    pause(100);

    sd_mount(DO, CLK, DI, CS);

    Help();

    while (1)
    {
       // printf("\n> ");
        printf("> ");
        fflush(stdout);
        gets(buffer);
        num = tokenize(buffer, tokens);
        num = CheckRedirection(tokens, num);
        if (num == 0) continue;
        if (!strcmp(tokens[0], "help"))
            Help();
        else if (!strcmp(tokens[0], "quit"))
            break;
        else if (!strcmp(tokens[0], "cat"))
            Cat(num, tokens);
        else if (!strcmp(tokens[0], "ls"))
            List(num, tokens);
        else if (!strcmp(tokens[0], "ll"))
        {
            tokens[num++] = "-l";
            List(num, tokens);
        }
        else if (!strcmp(tokens[0], "rm"))
            Remove(num, tokens);
        else if (!strcmp(tokens[0], "echo"))
            Echo(num, tokens);
        else if (!strcmp(tokens[0], "cd"))
            Cd(num, tokens);
        else if (!strcmp(tokens[0], "pwd"))
            Pwd(num, tokens);
        else if (!strcmp(tokens[0], "mkdir"))
            Mkdir(num, tokens);
        else if (!strcmp(tokens[0], "rmdir"))
            Rmdir(num, tokens);
        else if (!strcmp(tokens[0], "date"))
            Get_Date(num, tokens);
        else if (!strcmp(tokens[0], "time"))
            Get_Time(num, tokens);
        else if (!strcmp(tokens[0], "setclock"))
            Set_Clock(num, tokens);
        else
        {
            printf("Invalid command\n");
            Help();
        }
        CloseRedirection();
    }
    printf("System Stop\n");
    return 0;
}

Comments

  • RsadeikaRsadeika Posts: 3,824
    edited 2015-01-16 04:43
    I successfully added the QUICKSTART-SDXMMC selection, have not tried it out yet to see if it creates the xxx.pex file on the uSD card. Once this method is used I am assuming that the xxx.pex contains your whole program as it grows beyond the 32K limit. Now you will be able to start your program in stand alone, and have it run correctly, at least I think that is the way it works.

    Now, what do I do next with my QS+HIB contraption? I was thinking about making a stand alone with a VGA and keyboard, but I noticed in the Learn section examples, there is no C example for using a keyboard with the VGA, and I also did not notice if the C VGA version is 80x25 output. This will give me something to think about.

    Ray
  • RsadeikaRsadeika Posts: 3,824
    edited 2015-01-16 12:12
    A question for anybody that knows more about C than I do. I was looking at my QS+HIB setup, and I am thinking that I would like to add an XBee module to the setup, now is there some way of having the existing code, as written, access the XBee, and run the way it is running now? I will probably choose P7 and P8 to hook up to the XBee simm board, I think that is what it is called. I also think that if I were able to access P31 and P30, then hooking the XBee into those pins would produce the result that I am looking for, correct, no changes to the existing code?

    It would be real PIA, if I have to re-write the whole program just so it can run with an XBee attached, and then if I wanted too hook back up to the USB I would have to revert back to this existing program. Their has to be a more efficient way of doing these sort of things, yes?

    Ray
  • RsadeikaRsadeika Posts: 3,824
    edited 2015-01-17 04:11
    Now I am really lost with trying to figure out how that stdin and stdout work. The experimental code segment below compiles without error, but it does not work as expected. I thought what would happen is stdinfile = stdin = term, and it would display on the debug terminal, nope. I am trying to figure out how I can control stdinfile and stdoutfile to go out too stdout/stdin, or P31/P32, or P8/P7, or the VGA, without essentially rewriting the whole program. I am starting to get the feeling that the whole program has to be rewritten to match the devices that you want to use, this is a bummer.

    Ray

      term = fdserial_open(31, 30, 0, 115200);
      pause(100);
        //stdinfile = stdin;
        //stdoutfile = stdout;
      term = stdin;
      term = stdout;
      stdinfile = stdin;
      stdoutfile = stdout;
    
  • RsadeikaRsadeika Posts: 3,824
    edited 2015-01-17 06:11
    Is there something different about the QS Human Interface Board? I added an XBee module to the mix, the program is using P6 and P7, on the Auxilliary I/O socket, for connection to the XBee, and it is also using the 5V from there also. The problem is, when I run the program below, on an external terminal (putty serial), as soon as I hit a key, the terminal screen is showing a streaming "Unknown Command". On any other board, it waits for a keypress +CRLF, then it would wait for another keypress and so on. Why is it streaming on the HIB, anybody have any ideas, is there something about those pin states, that has changed?

    Ray
    /*
      uCUS1.c
      QS+HIB test base.
      Jan 17, 2013
    */
    #include "simpletools.h"
    #include "simpletext.h"
    #include "fdserial.h"
    
    serial *xbee;
    
    void menu();
    
    int main()
    {
      // Add startup code here.
      char inBuff[80];
    
    /*                     Rx Tx mode BAUD */  
      xbee = fdserial_open(7, 6,  0,  9600);
    /*
     * P7 -> DOUT XBee
     * P6 -> DIN  XBee
    */
     
      while(1)
      {
        // Add main loop code here.
        if(fdserial_rxReady(xbee))
        {
          readStr(xbee, inBuff, 80);
          if(!strcmp(inBuff, "help")) menu();
        }
        else
        {
          writeLine(xbee,"Unknown Command.");
        }            
      }
      return 0;  
    }
    
    void menu()
    {
      writeStr(xbee,"Menu - help, \n");
    }  
    
    
  • RsadeikaRsadeika Posts: 3,824
    edited 2015-01-17 09:49
    I guess I will have to do a conversion of the program. The below program works, but I cannot figure out why 'fprintf(xbee, "%s ", argv);' does not print out to the external terminal via xbee. Is xbee the wrong file type, I also thought that 'serial *xbee' is a file type, a serial file? I know I am going to have problems trying to make the CheckRedirection() and CloseRedirection() functions work, not sure how to get around 'stdinfile = stdin', for example. Without this I will not be able to get the '>, >>, <' to work correctly.

    Ray


    /*
      uCUS1.c
      QS+HIB test base.
      Jan 17, 2013
    */
    #include "simpletools.h"
    #include "simpletext.h"
    #include "fdserial.h"
    
    serial *xbee;
    
    void menu();
    int tokenize(char *ptr, char *tokens[]);
    char *SkipChar(char *ptr, int val);
    char *FindChar(char *ptr, int val);
    void Echo(int argc, char **argv);
    
    int main()
    {
      // Add startup code here.
      char inBuff[80];
      int num;
      char *tokens[20];
    /*                     Rx Tx mode BAUD */  
      xbee = fdserial_open(7, 6,  0,  9600);
    /*
     * P7 -> DOUT XBee
     * P6 -> DIN  XBee
    */
     
      while(1)
      {
        // Add main loop code here.
        if(fdserial_rxReady(xbee) == 1)
        {
          readStr(xbee, inBuff, 80);
          num = tokenize(inBuff, tokens);
          //if(num == 0) continue;
          if(!strcmp(tokens[0], "help")) menu();
          else if(!strcmp(tokens[0], "echo"))
            Echo(num, tokens);
          else
          {
            writeLine(xbee,"Unknown Command.");
          }
        }                
      }
      return 0;  
    }
    
    void menu()
    {
      writeStr(xbee,"Menu - help, \n");
    }  
    
    /* This routine extracts tokens from a string that are separated by one or
       more spaces.  It returns the number of tokens found. */
    int tokenize(char *ptr, char *tokens[])
    {
        int num = 0;
    
        while (*ptr)
        {
            ptr = SkipChar(ptr, ' ');
            if (*ptr == 0) break;
            if (ptr[0] == '>')
            {
                ptr++;
                if (ptr[0] == '>')
                {
                    tokens[num++] = ">>";
                    ptr++;
                }
                else
                    tokens[num++] = ">";
                continue;
            }
            if (ptr[0] == '<')
            {
                ptr++;
                tokens[num++] = "<";
                continue;
            }
            tokens[num++] = ptr;
            ptr = FindChar(ptr, ' ');
            if (*ptr) *ptr++ = 0;
        }
        return num;
    }
    
    
    /* This routine returns a pointer to the first character that doesn't
       match val. */
    char *SkipChar(char *ptr, int val)
    {
        while (*ptr)
        {
            if (*ptr != val) break;
            ptr++;
        }
        return ptr;
    }
    
    /* This routine returns a pointer to the first character that matches val. */
    char *FindChar(char *ptr, int val)
    {
        while (*ptr)
        {
            if (*ptr == val) break;
            ptr++;
        }
        return ptr;
    }
    
    
    /* This routine echos the command line arguments */
    void Echo(int argc, char **argv)
    {
        int i;
        for (i = 1; i < argc; i++)
        {
            if (i != argc - 1)
                //fprintf(stdoutfile, "%s ", argv[i]);
                writeStr(xbee,argv[i]);
            else
                //fprintf(stdoutfile, "%s\n", argv[i]);
                writeStr(xbee,argv[i]);
                writeStr(xbee,"\n");
        }
    }
    
  • RsadeikaRsadeika Posts: 3,824
    edited 2015-01-17 14:29
    Yes, and that is what I am trying to highlight.

    Ray
  • edited 2015-01-18 12:30
    Hi Ray,

    The standard libraries seem to be more or less written for boards like the C3 that are designed for extended memory model (XMM) operation, where programs are stored in and fetched from peripheral memory. In contrast, the simple libraries were written for the compact memory model (CMM), where programs are stored only in the Propeller chip's main RAM.

    This is all part of an in-progress open source project. The next step is to start testing the Simple Libraries with XMM and update any functions that are not currently compatible. Things like waitcnt might or might not need some adjustments to work with XMM. When that's up and running, the step after that might be to modify the functions and types so that they work with the standard libraries.

    Community involvement could help accelerate this process. I'll check with the development group and find out what needs to be made available so that an open project thread can be started.

    Andy
Sign In or Register to comment.