Need Help Sorting Asset Tracker coordinates From Serial on Arduino

Hi all(again) @kennethlimcp @Scruff @cermak @bko

My asset tracker is sending the coordinates to my arduino mega over serial 5. My mega is reading from serial2. I have a logic level shifter, common ground, and I’m receiving the data(String). Since the asset tracker sends the plain string, I can’t figure out how to have a separate string(lat) to be assigned to the latitude in the serial buffer- and same with the longitude. For some reason, I can see the latitude but not the longitude…After several failed attempts, I’m out of ideas. :unamused:

My code:

void setup() {
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  
  // Start each software serial port
  Serial2.begin(9600);
}

void loop() {
  while (Serial2.available() > 0) {
    
 String lat = Serial2.readStringUntil(',');
    
    Serial.print(lat);
   
     String lon = Serial2.readStringUntil("");
      for(int i = 8; i < 18; i++){
       lon = lon.substring(i);
     Serial.print(lon);  
    }
  }
}

Did you use pull-up resistors on the I2C line?

1 Like

@kennethlimcp

Yes- 10K - but i think it is a firmware issue.

I wouldn’t call t.readLatLon() twice.
Call it once and store the returned string for multiple use.

1 Like

A couple of thoughts. @rickkas7 has a nice Particle I2C tutorial. To add to @kennethlimcp, are you using a level shifter? Are the grounds tied together? This can lead to anomalous comms.

The delay where it is may be questionable. The default queue size is 32 characters for I2C. Actual transmission is delayed until endTransmission takes place. Having a delay after the write might trigger a timeout on the slave?

If you transmit again too quickly you can interrupt the previous inprogress transmission and bind up comms.

I’d try an unusually long delay after endTrasmission as a trial and see if you get the entire transmission.

1 Like

I don't think this does what you want since readLatLon returns a String and Wire.write is just going to copy all the bytes in the object, not just the string characters. I would try getting the c_str pointer out of the String object and using that instead.

1 Like

Can you post the sending code?

This might be problematic.
String lon = Serial2.readStringUntil("");

I’d suggest adding an ending character? Say #? This would mark the end of the transmission. Either that or a linefeed (\n).

latitude,longitude(either # or \n)latitude,longitude…

Then this would become:

String lon = Serial2.readStringUntil('#');

or

String lon = Serial2.readStringUntil('\n');

The sending code:

#include <AssetTracker.h>
#include "Serial5/Serial5.h"

// Set whether you want the device to publish data to the internet by default here.
// 1 will Particle.publish AND Serial.print, 0 will just Serial.print
// Extremely useful for saving data while developing close enough to have a cable plugged in.
// You can also change this remotely using the Particle.function "tmode" defined in setup()
int transmittingData = 1;

// Used to keep track of the last time we published data
long lastPublish = 0;

// How many minutes between publishes? 10+ recommended for long-time continuous publishing!
int delayMinutes = 10;

// Creating an AssetTracker named 't' for us to reference
AssetTracker t = AssetTracker();

// A FuelGauge named 'fuel' for checking on the battery state
FuelGauge fuel;

// setup() and loop() are both required. setup() runs once when the device starts
// and is used for registering functions and variables and initializing things
void setup() {
    // Sets up all the necessary AssetTracker bits
    Wire.begin();
    t.begin();

    // Enable the GPS module. Defaults to off to save power.
    // Takes 1.5s or so because of delays.
    t.gpsOn();
Serial.begin(9600);
    // Opens up a Serial port so you can listen over USB
Serial5.begin(9600);         // via TX/RX pins

    // These three functions are useful for remote diagnostics. Read more below.
    Particle.function("tmode", transmitMode);
    Particle.function("batt", batteryStatus);
    Particle.function("gps", gpsPublish);
    
}

// loop() runs continuously
void loop() {
    // You'll need to run this every loop to capture the GPS output
    t.updateGPS();
     /*
           Wire.beginTransmission(8);
            //Wire.write("1");
            Wire.write(t.readLatLon());
            Wire.endTransmission(true);
             delay(5000);
             */
    // if the current time - the last time we published is greater than your set delay...
    
        // GPS requires a "fix" on the satellites to give good data,
        // so we should only publish data if there's a fix
       //if(millis()-lastPublish > delayMinutes*60*1000){
        // Remember when we published
        //lastPublish = millis();
       // Wire.beginTransmission(8);
      // Wire.write("HELLO");
       //delay(500);
      // Wire.endTransmission();
        //String pubAccel = String::format("%d,%d,%d",t.readX(),t.readY(),t.readZ());
        //Serial.println(pubAccel);
        //Particle.publish("A", pubAccel, 60, PRIVATE);
        
        // Dumps the full NMEA sentence to serial in case you're curious
        //Serial.println(t.preNMEA());
        Serial5.println(t.readLatLon());
        Serial5.flush();
       // Serial.flush();
        // GPS requires a "fix" on the satellites to give good data,
        // so we should only publish data if there's a fix
        if(t.gpsFix()){
            // Only publish if we're in transmittingData mode 1;
            
            if(transmittingData){
                // Short publish names save data!
                Particle.publish("G", t.readLatLon(), 60, PRIVATE);
                
            }
            // but always report the data over serial for local development
            //Serial.println(t.readLatLon());
        }
    //}
}

// Allows you to remotely change whether a device is publishing to the cloud
// or is only reporting data over Serial. Saves data when using only Serial!
// Change the default at the top of the code.
int transmitMode(String command) {
    transmittingData = atoi(command);
    return 1;
}

// Actively ask for a GPS reading if you're impatient. Only publishes if there's
// a GPS fix, otherwise returns '0'
int gpsPublish(String command) {
    if (t.gpsFix()) {
        Particle.publish("G", t.readLatLon(), 60, PRIVATE);
//W//ire.beginTransmission(8);
          //  Wire.print(t.readLatLon());
           // Wire.endTransmission();
            Serial.print(String(t.readLatLon()));
        // uncomment next line if you want a manual publish to reset delay counter
        // lastPublish = millis();
        return 1;
    } else {
      return 0;
    }
}

// Lets you remotely check the battery status by calling the function "batt"
// Triggers a publish with the info (so subscribe or watch the dashboard)
// and also returns a '1' if there's >10% battery left and a '0' if below
int batteryStatus(String command){
    // Publish the battery voltage and percentage of battery remaining
    // if you want to be really efficient, just report one of these
    // the String::format("%f.2") part gives us a string to publish,
    // but with only 2 decimal points to save space
    Particle.publish("B",
          "v:" + String::format("%.2f",fuel.getVCell()) +
          ",c:" + String::format("%.2f",fuel.getSoC()),
          60, PRIVATE
    );
    // if there's more than 10% of the battery left, then return 1
    if (fuel.getSoC()>10){ return 1;}
    // if you're running out of battery, return 0
    else { return 0;}

}

Your in luck, the Serial.println should be sending both a \r and then a \n.

Try:

String lon = Serial2.readStringUntil('\r');

Ok @cermak what’s \n and \r ?``

:flashlight: println()
Available on Serial, USBSerial1, Serial1, Serial2, Serial4, Serial5.
Prints data to the serial port as human-readable ASCII text followed by a carriage return character (ASCII 13, or ‘\r’) and a newline character (ASCII 10, or ‘\n’).

@cermak

I’m still only getting the lat in serial monitor but it’s a lot smoother this time though :grinning: !

Comment the above lines out. Its tromping on the lon String.

@cermak the lat & lon show up now! Although I don’t think they have their own string…

 while (Serial2.available() > 0) {
    
 String lat = Serial2.readStringUntil('\n');
    
    Serial.print(lat);
   Serial.print(" ");
     String lon = Serial2.readStringUntil('\r');
     // for(int i = 8; i < 18; i++){
       //lon = lon.substring(i);
     //Serial.print(lon);
   
    
  }
}

So, the for loop you might have assumed the lat was still in there as part of the message which is why you were going from 8 to 18. A good guess.

When you start reading lon, you are actually reading the lon at that point. The for loop in combination of setting lon at the end to lon.substring(18) or the 18th character of what you might have read… which is nothing. That is why you ended up with nothing printed. At any rate, those two statements were the main problem.

Ok, new problem. Both latitude and longitude are in lat.

String lat = Serial2.readStringUntil(',');
String lon = Serial2.readStringUntil('\r');

It may not strip the ‘,’ or the ‘\r’ and then the \n’. You may have to consume them.

String lat = Serial2.readStringUntil(',');
char comma = Serial2.read();
String lon = Serial2.readStringUntil('\r');
char carrageReturn = Serial2.read();
char lineFeed = Serial2.read();
Serial.print("Lat:");
Serial.print(lat);
Serial.print("  Lon:");
Serial.println(lon);

One step a time.

@cermak

Thanks, I don’t think I would’ve realized the problem with the lon in the lat. This code is for my robot that is going to drive to my phone’s location. So this aspect of reading the lat, lon was important to me. :grinning:

@cermak

Sorry just one last quick question. So your example works great(and I never would’ve been able to come up with that :joy:! Is there any way that you can convert the string(lat & lon) to a float after it’s been read?

In my code I get an error thats says cannot convert 'String' to 'float' in assignment

float geoBearing() {
  if (packetbuffer[1] == 'L') {
    float lat, lon, alt;
    lat = parsefloat(packetbuffer+2);
    lon = parsefloat(packetbuffer+6);
    alt = parsefloat(packetbuffer+10);
    String latt = Serial2.readStringUntil(',');
char comma = Serial2.read();
String lonn = Serial2.readStringUntil('\r');
char carrageReturn = Serial2.read();
char lineFeed = Serial2.read();
  phoneLat = lat;
  phoneLon = lon;
  **roverLat = latt;**
  **roverLon = lonn;**
  float y = sin(phoneLon-roverLon) * cos(phoneLat);
  float x = cos(roverLat)*sin(phoneLat) - sin(roverLat)*cos(phoneLat)*cos(phoneLon-roverLon);
  return atan2(y, x) * RADTODEG;
}

The easiest is using the atof() function. See if this works.

roverLat = atof(latt);
roverLon = atof(lonn);

@cermak

That doesn’t compile but

          roverLon =  lonn.toFloat();
          roverLat = latt.toFloat(); 

does. The coordinates will still be the same right?

Right, these are String objects. Yup, they should be.