Error when copying Long to Char Array using sprintf

I created a couple subroutines to compress and decompress GPS data so I can send it over my LoRa radios. Unfortunately, I cannot get sprintf to work correctly in this case. If I use a String and then the .toCharArray function everything works correctly. I've tested and Googled for days now so I'm finally reaching out. What am I doing incorrectly?

The fractional part of the GPS coordinate frequently gets corrupted when using sprintf in line 98. When wrong, the error is always too low by 65537. I assume this is a clue but can't figure it out.

The commented out String part (line 72 to 96) will work exactly as desired. It's there for reference.

The For loop in setup() is for testing purposes only.

float mylatitude;
float mylongitude;
char gpsdata[9]; 
String myString;  

float receivedlatitude;
float receivedlongitude;

void setup() {
  Serial.begin(38400);
  randomSeed(analogRead(0));
  long test;

//for testing purposes only
  for (int mytest = 0; mytest < 1000; mytest++) {
    test = random(0, 180000);
    mylongitude = (float)test / 1000.0;

    test = random(0, 90000);
    mylatitude = (float)test / 1000.0;

    if (random(0, 2) == 1) {
      mylongitude = -mylongitude;
    }
    if (random(0, 2) == 1) {
      mylatitude = -mylatitude;
    }

    Serial.print(F("sent:"));
    Serial.print(mylatitude, 5);
    Serial.print(":");
    Serial.println(mylongitude, 5);
    gpscompressor();
    gpsdecompressor();
    Serial.print(F("recv:"));
    Serial.print(receivedlatitude, 5);
    Serial.print(":");
    Serial.println(receivedlongitude, 5);
    Serial.println("");
  }

}

void loop() {
  // put your main code here, to run repeatedly:

}

void gpscompressor() {
  gpscompress(mylatitude, 0);
  gpscompress(mylongitude, 4);
}

void gpscompress(float gps_coordinate, uint8_t x) {
  uint8_t signvalue = 0;
  long fractionalpart;  
  char tempdata[7];  //check length
  uint8_t char1;
  uint8_t char2;


  if (gps_coordinate < 0) {
    signvalue = 1; //if positive then 0. if negative then 1
    gps_coordinate = -gps_coordinate;
  }

  gpsdata[x] = (int)gps_coordinate; //stores integer portion of coordinate

  fractionalpart = ((float)gps_coordinate - (int)gps_coordinate) * 100000;


  //This works well but uses a String
/*
  if (fractionalpart  >= 10000) {
    myString = (String)fractionalpart;
  }
  else if (fractionalpart  >= 1000)
  {
    myString = "0" + (String)fractionalpart;
  }
  else if (fractionalpart  >= 100)
  {
    myString = "00" + (String)fractionalpart;
  }
  else if (fractionalpart  >= 10)
  {
    myString = "000" + (String)fractionalpart;
  }
  else if (fractionalpart  >= 1)
  {
    myString = "0000" + (String)fractionalpart;
  }
  else {
    myString = "00000" + (String)fractionalpart;
  }
  myString.toCharArray(tempdata, 6);
*/

  sprintf(tempdata, "%05d", fractionalpart);  //This fails a large percentage of the time.  When incorrect the value is always too low by 65537.

  tempdata[5] = '\0';  //The terminator doesn't seem to matter in my application but it's something else I tried that didn't work.

  char1 = tempdata[0] - '0';
  char2 = tempdata[1] - '0';
  char1 = char1 * 10 + char2;
  gpsdata[x + 1] = char1; //first two numbers of fractional latitude

  char1 = tempdata[2] - '0';
  char2 = tempdata[3] - '0';
  char1 = char1 * 10 + char2;
  gpsdata[x + 2] = char1; //third and fouth numbers of fractional latitude

  char1 = tempdata[4] - '0';
  char1 = (char1 * 10) + signvalue;
  gpsdata[x + 3] = char1; //fifth number and sign of latitude

}

void gpsdecompressor() {
  gpsdecompress(0);
  gpsdecompress(4);
}

void gpsdecompress(uint8_t x) {
  uint8_t char1;
  uint8_t char2;
  uint8_t char3;
  float tempcoordinate;
  char tempdata[7];  //check length

  char1 = gpsdata[x] - '0';
  char1 += 48;
  tempcoordinate = char1;

  char1 = gpsdata[x + 1] - '0';
  char2 = gpsdata[x + 2] - '0';
  char3 = gpsdata[x + 3] - '0';
  char1 += 48;
  char2 += 48;
  char3 += 48;

  sprintf(tempdata, "%02d", char1);
  sprintf(tempdata + 2, "%02d", char2);
  sprintf(tempdata + 4, "%02d", char3);

  tempdata[6] = '\0';

  tempcoordinate = tempcoordinate + (float)atol(tempdata) / 1000000.0;

  char1 = tempdata[5] - '0';
  if (char1 == 1) {
    tempcoordinate = -tempcoordinate;
  }

  if (x == 0) {
    receivedlatitude = tempcoordinate;
  }
  else if (x == 4) {
    receivedlongitude = tempcoordinate;
  }
}

fractionalpart is a long; the format should reflect that
(it's a lowercase 'L', not a one)

1 Like

In "Preferences...", set 'Compiler warnings:' to 'All'. Then you will get this handy warning pointing out your mistake:

In function 'void gpscompress(float, uint8_t)':
99:43: warning: format '%d' expects argument of type 'int', but argument 3 has type 'long int' [-Wformat=]
   sprintf(tempdata, "%05d", fractionalpart);  //This fails a large percentage of the time.  When incorrect the value is always too low by 65537.
                                           ^
                                
1 Like

Thank you. That fixed it. I tried dozens of syntax variations of sprintf and never came across an example showing "ld".

Thank you for that great recommendation. I have updated my settings now.

As far as I can tell, the syntax is the same as for printf, which has a lot more documentation.
< edit > With the obvious exception of floats, which sprintf does not support on most arduino's.