SoftwareSerial Corruption with PMB688

I'm using a PMB688 from Parallax and I have been frustrated by serial communication issues. At first I thought I was just doing something wrong, so I removed all the components I could trim away and have the following test case:

  1. Using SoftwareSerial (not NewSoftSerial) I collected the following output. The format is the content of the GPGGA messages from the PMB688 followed by a space followed by my calculated checksum followed by a "CHK" or "ERR" to make it easier to see where they match:
 GPGGA,195245.000,4225.3697,N,07110.0223,W¬1,04,3.7,-16.5,M,-33.7,M,,0000 7B fb ERR
 GPGGA,195246.000,4225.3697,N,07110.0223,W,1,04,³.7,-16.5,M,-33.7,M,,0000 78 f8 ERR
 GPGGA,195247.000,4225.3697,N,07110.0223,W,1,04¬3.7,-16.5,M,-33.7,M,,0000 79 f9 ERR
 GPGGA,195248.000,4225.3697,N,07110.0223,W,1,04,3.7,-16.5,M,-33.7,M,,0000 76 76 CHK
 GPGGA,195249.000,4225.3697,N,07110.0223,W,1,04,3.7,-16.5,M,-33.7,M,,0000 77 77 CHK
 GPGGA,195250.000,4225.3697,N,07110.0223,W,1,04,3.7,-16.5,M,-33.7,M,,00°0 7F ff ERR
 GPGGA,195251.000,4225.369·,N,0·110.02²3,W,1,04,3.8,-16.5,M,-33.7,M,,0000 71 f1 ERR
 GPGGA,195252.000,4225.3697,N,07110.0223,W,1,04,3.8,-16.5,M,-33.7,M,¬0000 7² f2 ERR
 GPGGA,195253.000,4225.³647,N,07110.0152,W,1,04,3.8,-30.0,M,-33.7,M,,0000 7A fa ERR
 GPGGA¬195254.000,4²25.3645,N,07110.0152,W,1,04,3.8,-30.±,M,-³3.7,M,,0000 7E 7e CHK
 GPÇGA,195255.000,4225.3647,N,07110.0154,W,1,04¬3.8,-³0.1,M,-33.7,M,,0000 7B fb ERR
 GPGGA,19µ256.000,4225.3646,N,07110.0149,W,1,04¬3.8,-30.2,M,-33.7,M,,0000 76 76 CHK
 GPGGA,195257.000,4225.3641,N,07110.0138,W,1,04,3.8,-30.4,M,-33.7,M,,0000 70 70 CHK
 GPGGA,195258.000,4225.3633,N,07110.0128¬W,1,04,3.8,­30.5,M,-33.7,M,,0000 7A 7a CHK
 GPGGA,195259.000,4225.3624,N,07110.0115,W,1,04,3.8,-30.4,Í,-33®7,M,,0000 72 72 CHK
 GPGGA,195300.000,4225.3622,N,07110.0110,W,1,04,3.8,-30.5,M,-33.7,M,,0000 7D 7d CHK
 GPGGA,195301.000,4225.3618,Î,07110.0105,W,1,04,3.8,-30.3,M,­33.7,M,,000° 77 f7 ERR
 GPGGA,195302®000,4225.3609,N,07110.0093,W,1,05,4.2,-30.5,M,-33.7,M,,0000 70 f0 ERR
 GPGGA,195303.000,4225.³609,N,07110®0093,W,1,05,4.2,-30.5,M,-33.7,Í,,0000 71 f1 ERR
 GPGGA,195304.000,4225.3609,N,07110.0093,W,1,05,4.2,-30.5,M,-3³.7,M,,0000 7¶ f6 ERR
 GPGGA,195305.000,4225.3609,N,07110.0093,W,1,05,4.2,-30.5,M,-33.7,M,,0000 77 77 CHK
 GPGGA¬195306.000,4225.3587,N,07110.0°68,W,1,05,4®2,-32.7,M,-33.7,M,,0000 75 f5 ERR
 GPGGA,195307.000,4225.3578,N,07110.0066,W,1,04,3.8,-32.9,M,-33.7,M,,0000 78 78 CHK
 GPGGA,195308.000,4225.3541,N,07110.0039,W,1,04,3.8,-33.1,M,-33.7,M,,0000 7E 7e CHK
 GPGGA,195309.000,4225.3521,N,07110.0028,W,1,04,3.8,-33.7,M,-33.7,M,,0000 ·F 7f ERR
 GPGGA,195310.000,4225.3496,N,07110.0017,W,1,04,3.8,-34.2,M,-33.7,M,,0000 74 74 CHK
 GPGÇA,195311.000,4225.3479,N,07110.0006,W,1,05,4.2,-34®8,M,-33.7,M,,0000 72 72 CHK
 GPGGA,195312.000,4225.3458,N,07109.999³,W,1,05,4.2,­35.5,M,-33.7,M,,0000 7A 7a CHK
 GPGGA¬195313.000,4225.3458,N,07109.9993,W,1,05,4.2,-35.5,M,-33.7,M,,0000 7B fb ERR
 GPGGA,195314.000,42²5.34µ8,N,07109.9993,W,1,05,4.2,-35.5,M,-³3.7,M,,0000 7C fc ERR
 GPGGA,195315.000,422µ.3458¬N,07109.9993,W,1,05,4.2,-35.5,M,-33.7,M,¬0000 7D fd ERR
 GPGGA,195316.000,4225.3458,N,07109.9993,W,1,05,4.2,-35.5,M,-33.7,M,,0000 7E 7e CHK
 GPGGA,195317.000,4225.3458,N,07109.9993,W,1,05,4.2,-3µ.5,M,-33.7,M,,0000 7F ff ERR
 GPGGA,195318®000,42²5.3458,N,07109.9993,W,1,05,4.2,-35.µ,M,-3³.7,M,,0000 70 70 CHK
 GPGGA,19µ319.000,4225.3458,N,07109.9993,W,1,05,4.2,-35.5,M,-33.7,M,,0000 71 f1 ERR
 GPGGA,1953²0.000¬4225.3458,N,07109.9993,W,1,05,4.2,-35.5,M,-33.7,M,,0000 7B 7b CHK
 GPGGA,195321.000,4225.³458,N,07109.9993,W,1,05,4.2,-3µ.5,M¬-33.7,M,,0000 7A fa ERR
 GPGGA,195322.000,4225.³458,N,07109.9993,W,1,05,4.2,-35.5,M,-33.7¬M,,0000 79 79 CHK
 GPGGA,19µ323.000,4225.3458,N,07109.9993,W,1,0µ,4.2,-35.5,M,-33.7,M,,0000 78 78 CHK
 GPGGA,195324.000,4225.3458,N,07109.9993,W,1,05,4.2,-35.5,M,-33.7,M,,0000 7F 7f CHK
 GPGGA,195325.000,4225.3458,N,07109.9993,W,1,05¬4.2,-35.5,M,-33.7,M,,0000 7E fe ERR
 GPGÇA,195³26.000,4225.3458,N,07109.9993,W,1,04,3.8,-35.5,M¬-33.7,M,,0000 71 f1 ERR
 GPGGA,195327.000,4225.³458,N,07109.9993,W,1,04,3.8,-35.5,M¬-33.7,M,,0000 70 70 CHK
 GPGGA,195328.000,4225.3458,N,07109.9993,W,1,04,3.8,-35.5,M,-33.7,M¬,0000 ·F ff ERR
 GPGGA,195329.000,4225.3458,N,07109.9993,W,1,04,3.8,-35.5,M,-33.7,M,,0000 7E 7e CHK
 GPGGA,195331.000,422µ.3458,N,07109.9993,W,1,°4,3.8,-35.5,M,-33.7,M,,0000 77 77 CHK
 GPGGA,195332.000,4225.3458,N¬07109.9993,W,1,04,3.8,-35.5,M,-33.7,M,,0000 74 f4 ERR
 GPGGA,195333.000,4225.3458,N,07109.9993,W,1,04,3.8,-35.5,M,-33.7,M,,0000 75 75 CHK
 GPGGA,195334.000,4225.3528,N,07110.0138,W,1,04,3.8,-24.7,M,-33®7,M,,0000 7E fe ERR
 GPGGA,195335.000,4225.3µ29,N,07110.0138,W,1,04,3.8,-24.8,M,-33.7,Í,,0000 71 71 CHK
  1. Code:
#include <SoftwareSerial.h>

#define GPS_RX 6
#define GPS_TX 5
#define BUFFER_LEN 1000
int next = 0; // next place to read into the buffer;
char buffer[BUFFER_LEN];
SoftwareSerial gps(GPS_RX, GPS_TX);
unsigned long lastUpdate;

void setup()
{

  //set up gps
  pinMode(GPS_RX, INPUT);
  pinMode(GPS_TX, OUTPUT);
  gps.begin(4800);
  //set up serial
  Serial.begin(57600);
  Serial.println("\nSTART");
  
}

void loop()
{
  int val = gps.read();
  if (val != -1) {
    buffer[next] = (unsigned char)val;
    next ++;
    //Serial.print((unsigned char)val);
    if (buffer[next-1] == '\x0D') {
      handleMessage();
      clearBuffer();
    }
  }
}

void clearBuffer() {
 memset(buffer,0,BUFFER_LEN);
 next = 0; 
}

void handleMessage() {
 String raw = String(buffer);
 int start = raw.indexOf('
  1. You'll note that the number of bytes of serial data received is constant per message (except when the digit length changes, but I don't really care about that). However, there are a lot of corrupted characters. In theory I can deal with this by just ignoring the ones that don't checksum properly, but this is an obscene corruption percentage. What's going on? I hope I'm doing something wrong on a bit level, because otherwise I've got a hardware problem.

  2. As general information:

  • I'm using a new Arduino Fio purchased very recently.
  • I'm using a single cell LiPo that's 3.7V+, with the PMB688 in parallel off the battery to eliminate current sourcing issues. (A prior theory.)
  • I'm using Arduino 0022, on a Mac
  • I'm talking to my Mac via a 57600 baud serial connection over XBee, and to the PMB688 at 4800 baud as per spec and suggestion to reduce errors of software serial.);
    int tail = raw.indexOf('*');
    String message = raw.substring(start,tail+3);
    String content = raw.substring(start+1,tail);
    String checksum = raw.substring(tail+1,tail+3);
    unsigned char temp = 0;
    for (int i = 0; i < content.length(); i++) {
      temp = temp ^ (unsigned char)content.charAt(i);
    }
    String tempStr = String(temp,HEX);

//Serial.println(message);
Serial.print(" ");
Serial.print(content);
Serial.print(" ");
Serial.print(checksum);
Serial.print(" ");
Serial.print(tempStr);
if (tempStr.equalsIgnoreCase(checksum))
  Serial.println(" CHK");
else
  Serial.println(" ERR");
}


3) You'll note that the number of bytes of serial data received is constant per message (except when the digit length changes, but I don't really care about that). However, there are a lot of corrupted characters. In theory I can deal with this by just ignoring the ones that don't checksum properly, but this is an obscene corruption percentage. What's going on? I hope I'm doing something wrong on a bit level, because otherwise I've got a hardware problem.

4) As general information:
- I'm using a new Arduino Fio purchased very recently.
- I'm using a single cell LiPo that's 3.7V+, with the PMB688 in parallel off the battery to eliminate current sourcing issues. (A prior theory.) 
- I'm using Arduino 0022, on a Mac
- I'm talking to my Mac via a 57600 baud serial connection over XBee, and to the PMB688 at 4800 baud as per spec and suggestion to reduce errors of software serial.

I've yet to use softwareserial library but I think you have an error in how you are trying to read characters. You first must test to see if a character is ready to be read before reading it. Here is an example of proper useage from the newsoftwareserial example sketch:

void loop()                     // run over and over again
{

  if (mySerial.available()) {
      Serial.print((char)mySerial.read());
  }
  if (Serial.available()) {
      mySerial.print((char)Serial.read());
  }
}

Note testing performed before reading in the first if statement, this mimics the way you use the arduino serial library. So i suspect no matter what hardware or software serial software you use, you must first test if a character is in the buffer ready to be read before performing a read function.

EDIT: I did a quick read of the arduino supplied software serial library and I seem not see a ready to read type function, so I'm wrong in my diagnosis for you symptom. Sorry

Lefty

Lefty: To the best of my knowledge, the concept of available() doesn't exist for SoftwareSerial. That is a feature of NewSoftSerial and one of the reasons people love it. Instead, SS .read() always returns, which is why I check for a -1, which indicates no new characters are available.

If you could tell me where you got that example code I would be interested to look at it.

pquimby:
Lefty: To the best of my knowledge, the concept of available() doesn't exist for SoftwareSerial. That is a feature of NewSoftSerial and one of the reasons people love it. Instead, SS .read() always returns, which is why I check for a -1, which indicates no new characters are available.

If you could tell me where you got that example code I would be interested to look at it.

As I put in my edit of my first post, I was in error. Softserial has just a single read function that either returns a character or returns a -1 if not avalible. The examples for New softserial ship with the library folder, there are two:

#include <NewSoftSerial.h>

NewSoftSerial mySerial(2, 3);

void setup()  
{
  Serial.begin(57600);
  Serial.println("Goodnight moon!");

  // set the data rate for the NewSoftSerial port
  mySerial.begin(4800);
  mySerial.println("Hello, world?");
}

void loop()                     // run over and over again
{

  if (mySerial.available()) {
      Serial.print((char)mySerial.read());
  }
  if (Serial.available()) {
      mySerial.print((char)Serial.read());
  }
}
#include <NewSoftSerial.h>

NewSoftSerial nss(2, 3);
NewSoftSerial nss2(4, 5);

void setup()
{
  nss2.begin(4800);
  nss.begin(4800);
  Serial.begin(115200);
}

void loop()
{
  // Every 10 seconds switch from 
  // one serial GPS device to the other
  if ((millis() / 10000) % 2 == 0)
  {
    if (nss.available())
    {
      Serial.print(nss.read(), BYTE);
    }
  }
  
  else
  {
    if (nss2.available())
    {
      Serial.print(nss2.read(), BYTE);
    }
  }
}

Lefty

Starring at this data more: I see a few common substitutions:
'0' for '°', which is 48 0b110000
'1' for '±', which is 49 0b110001
',' for '¬', which is 44 0b101100
'.' for '®', which is 46 0b101110
'7' for '·', which is 55 0b110111
'2' for '²', which is 50 0b110010

What's even more confusing is that many of these chksum correctly. So... I'm left wondering if something is going wrong that's unrelated to the actual device, but rather my handling of character data. I would guess that I was messing up the sign, but the characters above that are wrong don't appear to be wrong by their initial binary digit.

Problem found (in part): The PMB688 was returning non-ASCII values (greater than 127). By subtracting 128 from any such values, the problem goes away. I suspect that somewhere a signed variable is causing this problem by corrupting the 8th bit, bit, but I can't find it.