Shop OBEX P1 Docs P2 Docs Learn Events
Issue with XBee AT command mode with PropGCC FDS — Parallax Forums

Issue with XBee AT command mode with PropGCC FDS

RR Posts: 5
edited 2013-01-13 15:14 in Propeller 1
I am using PropGCC to develop software that interacts with XBee Series 2 radios in AT (transparent) mode.

When my program first boots up it puts the XBee in command mode, then uses AT commands to echo configuration information back over stdout to the terminal.

My problem is that I seem to have to print a sacrficial character to the XBee after I read from the XBee serial, or else it doesn't work.

Here is a simplified part of my program that exhibits the behavior:


#include <stdio.h>
#include <propeller.h>
#include <driver.h>
extern _Driver _FullDuplexSerialDriver;


_Driver *_driverlist[] = {
  &_FullDuplexSerialDriver,
  NULL
};

// XBee line terminates with /r, not /n, so this is a re-implemented
// fgets that also acknowledges /r
char *
rfgets(char *buf, int size, FILE *fp)
{
  int c; 
  int count = 0;


  --size;
  while (count < size) {
    c = fgetc(fp);
    printf("Saw %d '%c'\n", c, c);
    if (c < 0) break;
    if (c == '\n' || c == '\r') break; // Swapped this to drop line terminator
    buf[count++] = c;
  }
  buf[count] = 0;
  return (count > 0) ? buf : NULL;
}


int main (int argc,  char* argv[])
{
   char serData[64];
   char atsh[16];
   char atsl[16];
   FILE *ser;
   ser = fopen("FDS:115200,26,27", "r+");
   if (ser == 0) {
       puts("FATAL: fopen(\"\n");
       return 0;
   }


   // Make sure chars are available promptly from radio
   ser->_flag &= ~_IOCOOKED;
   setvbuf(ser, NULL, _IONBF, 0);

   memset(serData, 0, 64);

   memset(atsh, 0, 9);
   memset(atsl, 0, 9);
   
   waitcnt(CNT+CLKFREQ); // Make sure the line has been quiet in case a restart just occurred
   
   puts("Getting XBee config...");
   fputs("+++", ser);
   puts("+++");
   rfgets(serData, 64, ser);
   puts(serData);
   
   fputc('x', ser); // If this line is removed, the rfgets below never receives data
   fputs("ATSH\r", ser);
   puts("ATSH");
   rfgets(atsh, 9, ser);
   puts(atsh);

   return 0;
}

The fputc('x', ser) above is the sacrificial character I mentioned above.

The terminal output from this is:
Loading Controller.elf to hub memory
10700 bytes sent                  
Verifying RAM ... OK
[ Entering terminal mode. Type ESC or Control-C to exit. ]
Getting XBee config...
+++
Saw 79 'O'
Saw 75 'K'
'aw 13 '
OK
ATSH
Saw 49 '1'
Saw 51 '3'
Saw 65 'A'
Saw 50 '2'
Saw 48 '0'
Saw 48 '0'
'aw 13 '
13A200
With the fputc('x', ser) commented, I only get:
Loading Controller.elf to hub memory
10684 bytes sent                  
Verifying RAM ... OK
[ Entering terminal mode. Type ESC or Control-C to exit. ]
Getting XBee config...
+++
Saw 79 'O'
Saw 75 'K'
'aw 13 '
OK
ATSH
Note the 'aw 13' is caused by the XBee just returning a /r (carriage return) with out a line feed, so that is the closing ' printed over the first character of Saw. Maybe this type of odd behavior is causing in issue in the FDS code?

My setup is:
  • Ubuntu 12.04
  • Propeller Professional Development Board
  • XBee Series 2 radio
  • PropGCC built from source (to get the _IONONBLOCK)
    • propeller-elf-gcc -v reports: gcc version 4.6.1 (propellergcc_v0_3_5_1766)
I have this same issue with the Linux distro's available from google code.

Thanks for taking the time to read over this and any insight you might have.

Comments

  • TorTor Posts: 2,010
    edited 2013-01-12 16:13
    While waiting for those in the know to check in..[1] you could try 'fflush (ser);' instead of that extra fputc.

    [1] What's necessary to know is how the low-level library function handles buffering of the fopen'ed device - e.g. is there buffering, and if so, is it settable via a standard ioctl()? If there is buffering involved though then an fflush() is supposed to, er, flush it out.

    -Tor
  • RR Posts: 5
    edited 2013-01-12 17:55
    I also tried this with fflush(ser) after each fputs(). Somehow this caused other issues where after sending the +++ to enter command mode I would get back three ASCII null characters instead of the OK\r. I don't understand why fflush() would cause that behavior, I expected it to act the same as before or better, but not cause degradation.

    Is that a possible clue into what is going on?
  • ersmithersmith Posts: 6,054
    edited 2013-01-13 05:43
    Anytime you change between reading and writing on the same FILE handle you have to do an fflush(). That's standard C behavior, it's not unique to PropGCC, but it is a gotcha that catches a lot of people.

    It's generally easier and safer to have two separate handles for input and output (just like stdin and stdout are used to address the standard input/output, you would open "serin" for input and "serout" for output.

    Also note that "puts" always appends a \n to the data it's sending, which may or may not be what you want -- I usually like to use fputs instead, since then I can explicitly specify exactly what's being sent.

    Eric
  • RR Posts: 5
    edited 2013-01-13 12:57
    Thanks Eric and Tor!

    It seems it was an fflush() issue after all! My initial attempt at dumping in an fflush() was just where I had to put the fputc('x', ser) hack in, if you look at the code this is after I've already gone from a write, to a read, then am going back to a write mode. Putting fflush() between every transition as Eric mentioned is exactly what I needed. Below is the updated code that works, for anyone else with a similar issue :

    #include <stdio.h>
    #include <propeller.h>
    #include <driver.h>
    extern _Driver _FullDuplexSerialDriver;
    
    
    _Driver *_driverlist[] = {
      &_FullDuplexSerialDriver,
      NULL
    };
    
    
    char *
    rfgets(char *buf, int size, FILE *fp)
    {
      int c; 
      int count = 0;
    
    
      --size;
      while (count < size) {
        c = fgetc(fp);
        printf("Saw %d '%c'\n", c, c==13?' ':c);
        if (c == '\n' || c == '\r') break; // Swapped this to drop line terminator
        if (c < 0) break;
        buf[count++] = c;
      }
      buf[count] = 0;
      return (count > 0) ? buf : NULL;
    }
    
    
    int main (int argc,  char* argv[])
    {
       char serData[64];
       char atsh[9];
       char atsl[9];
       FILE *ser;
       ser = fopen("FDS:115200,26,27", "r+");
       if (ser == 0) {
          puts("FATAL: fopen(\"\n");
          return 0;
       }
    
    
       memset(serData, 0, 64);
    
    
       memset(atsh, 0, 9);
       memset(atsl, 0, 9);
       waitcnt(CNT+CLKFREQ); // Make sure the line has been quiet in case a restart just occurred
       puts("Getting XBee config...");
       puts("+++");
       
       fputs("+++", ser);
       fflush(ser);
       rfgets(serData, 64, ser);
       
       puts(serData);
       puts("ATSH");
       
       fflush(ser);
       fputs("ATSH\r", ser);
       fflush(ser);
       rfgets(atsh, 9, ser);
       
       puts(atsh);
       puts("ATSL");
       
       fflush(ser);
       fputs("ATSL\r", ser);
       fflush(ser);
       rfgets(atsl, 9, ser);
       
       puts(atsl);
    
    
       return 0;
    }
    
    

    I added a few more write/read interactions from my actual use case to show that the serial line continues to behave nicely.
  • RR Posts: 5
    edited 2013-01-13 13:26
    After going to litter my code with fflush()'s, I realized my embarrassment for not having pursued the serIn/serOut solution.

    Here is the same code, albeit much cleaner and easier to read, with separate FILE's for reading and writing. This is tested and works the same as the prior solution.
    #include <stdio.h>#include <propeller.h>
    #include <driver.h>
    extern _Driver _FullDuplexSerialDriver;
    
    
    _Driver *_driverlist[] = {
      &_FullDuplexSerialDriver,
      NULL
    };
    
    
    char *
    rfgets(char *buf, int size, FILE *fp)
    {
      int c; 
      int count = 0;
    
    
      --size;
      while (count < size) {
        c = fgetc(fp);
        printf("Saw %d '%c'\n", c, c==13?' ':c);
        if (c == '\n' || c == '\r') break; // Swapped this to drop line terminator
        if (c < 0) break;
        buf[count++] = c;
      }
      buf[count] = 0;
      return (count > 0) ? buf : NULL;
    }
    
    
    int main (int argc,  char* argv[])
    {
       char serData[64];
       char atsh[9];
       char atsl[9];
       FILE *serIn;
       FILE *serOut;
       serIn  = fopen("FDS:115200,26,27", "r");
       serOut = fopen("FDS:115200,26,27", "w");
       if (serIn == 0) {
          puts("FATAL: fopen(serIn)\n");
          return 0;
       }
       if (serOut == 0) {
          puts("FATAL: fopen(serOut)\n");
          return 0;
       }
    
    
       // This avoids the need to fflush() when entering
       // command mode (The +++ sequence can't have a line
       // terminator, else you violate the gaurd time)
       setvbuf(serOut, NULL, _IONBF, 0);
    
    
       memset(serData, 0, 64);
    
    
       memset(atsh, 0, 9);
       memset(atsl, 0, 9);
       waitcnt(CNT+CLKFREQ); // Make sure the line has been quiet in case a restart just occurred
       puts("Getting XBee config...");
       puts("+++");
       
       fputs("+++", serOut);
       rfgets(serData, 64, serIn);
       
       puts(serData);
       puts("ATSH");
       
       fputs("ATSH\n", serOut);
       rfgets(atsh, 9, serIn);
       
       puts(atsh);
       puts("ATSL");
       
       fputs("ATSL\n", serOut);
       rfgets(atsl, 9, serIn);
       
       puts(atsl);
    
    
       return 0;
    }
    

    BTW, I did put in some debug code to ensure that even though fopen() is called to FDS twice, only one additional cog is used. I've ommitted that code from here since it wasn't relevant.
  • ersmithersmith Posts: 6,054
    edited 2013-01-13 15:14
    [QUOTE=R
Sign In or Register to comment.