Problem with a routine in a sketch

Hi all,

I’ve been working with two separate sketches, one that grabs the data from a GPS receiver and another that interrogates a vehicles Canbus and displays data (using SKPang’s Canbus shield).

Now I have my 20x4 sLCD working I wanted to combine the two so that I have the GPS output on Lines 0 & 1 and the Canbus data on Lines 3 & 4.

However, when I add the Canbus void loop code the GPS data stops displaying, if I comment the Canbus code out or remove it then the GPS data re-appears but the Canbus code vanishes!

I’ve commented out the actual Canbus interrogation in the sketch and replaced it with simple print commands whilst I debug, any help appreciated as to what I’m doing wrong here!

Here is the canbus code that when added breaks everything:

{
  //Canbus.ecu_req(ENGINE_RPM, canbuff);    
  sLCD.print(COMMAND, BYTE);                  
  sLCD.print(LINE2, BYTE);                     
  sLCD.print("Temp");                         
 
  //digitalWrite(LED3, HIGH);
  
  //Canbus.ecu_req(VEHICLE_SPEED, canbuff); 
  sLCD.print(COMMAND,BYTE);
  sLCD.print(LINE2 + 10,BYTE);                     
  sLCD.print("Speed");
      
  //Canbus.ecu_req(ENGINE_COOLANT_TEMP, canbuff);
  sLCD.print(COMMAND, BYTE);
  sLCD.print(LINE3, BYTE);                                   
  sLCD.print("RPM");
   
  //Canbus.ecu_req(VEHICLE_SPEED, canbuff); 
  sLCD.print(COMMAND,BYTE);
  sLCD.print(LINE3 + 10,BYTE);                     
  sLCD.print("Throttle");
     
  //digitalWrite(LED3, LOW); 
  delay(50);
  }

And here is the full sketch with the full code:

// Combined GPS & CANBUS Sketch outputting to sLCD 20x4

#include <NewSoftSerial.h>
#include <Canbus.h>

NewSoftSerial mySerial =  NewSoftSerial(4, 5);
NewSoftSerial sLCD =  NewSoftSerial(3, 14);
#define COMMAND 0xFE
#define CLEAR   0x01
#define LINE0   0x80
#define LINE1   0xC0
#define LINE2   0x94                      
#define LINE3   0xD4                      
#define GPSRATE 4800

int LED2 = 8;
int LED3 = 7;

#define BUFFSIZ 90 // plenty big
char buffer[BUFFSIZ];
char canbuff[20];
char *parseptr;
char buffidx;
uint8_t hour, minute, second, year, month, date;
uint32_t latitude, longitude;
uint8_t groundspeed, trackangle;
char latdir, longdir;
char status;

void setup() 
{ 
  pinMode(13, OUTPUT);
  pinMode(LED2, OUTPUT); 
  pinMode(LED3, OUTPUT); 
  Serial.begin(GPSRATE);
  mySerial.begin(GPSRATE);
   
  sLCD.begin(9600);
  sLCD.print(COMMAND, BYTE);
  sLCD.print(CLEAR, BYTE);
   
 // if(Canbus.init(CANSPEED_500 ))             /* Initialise MCP2515 CAN controller at the specified speed */
 // {
  sLCD.print("CANBUS"); 
 // } else
 // {
 //   sLCD.print("CANBUS FAIL");
 // }    
 // delay(1000);
} 
 
 
void loop() 
{ 
  {
  //Canbus.ecu_req(ENGINE_RPM, canbuff);    
  sLCD.print(COMMAND, BYTE);                  
  sLCD.print(LINE2, BYTE);                     
  sLCD.print("Temp");                         
 
  //digitalWrite(LED3, HIGH);
  
  //Canbus.ecu_req(VEHICLE_SPEED, canbuff); 
  sLCD.print(COMMAND,BYTE);
  sLCD.print(LINE2 + 10,BYTE);                     
  sLCD.print("Speed");
      
  //Canbus.ecu_req(ENGINE_COOLANT_TEMP, canbuff);
  sLCD.print(COMMAND, BYTE);
  sLCD.print(LINE3, BYTE);                                   
  sLCD.print("RPM");
   
  //Canbus.ecu_req(VEHICLE_SPEED, canbuff); 
  sLCD.print(COMMAND,BYTE);
  sLCD.print(LINE3 + 10,BYTE);                     
  sLCD.print("Throttle");
     
  //digitalWrite(LED3, LOW); 
  delay(50);
  }
   
  uint32_t tmp;
  readline();
  if (strncmp(buffer, "$GPRMC",6) == 0)
  {  
    // hhmmss time data
    parseptr = buffer+7;
    tmp = parsedecimal(parseptr); 
    hour = tmp / 10000;
    minute = (tmp / 100) % 100;
    second = tmp % 100;
    
    parseptr = strchr(parseptr, ',') + 1;
    status = parseptr[0];
    parseptr += 2;
    
    // grab latitude & long data
    
    // latitude
    latitude = parsedecimal(parseptr);
    if (latitude != 0)
    {
      latitude *= 10000;
      parseptr = strchr(parseptr, '.')+1;
      latitude += parsedecimal(parseptr);
    }
    parseptr = strchr(parseptr, ',') + 1;
    
    // read latitude N/S data
    if (parseptr[0] != ',')
    {
      latdir = parseptr[0];
    }
    
    // longitude
    parseptr = strchr(parseptr, ',')+1;
    longitude = parsedecimal(parseptr);
    if (longitude != 0)
    {
      longitude *= 10000;
      parseptr = strchr(parseptr, '.')+1;
      longitude += parsedecimal(parseptr);
    }
    parseptr = strchr(parseptr, ',')+1;
    
    // read longitude E/W data
    if (parseptr[0] != ',')
    {
      longdir = parseptr[0];
    }
    
    // Groundspeed
    parseptr = strchr(parseptr, ',')+1;
    groundspeed = parsedecimal(parseptr);

    // Track angle
    parseptr = strchr(parseptr, ',')+1;
    trackangle = parsedecimal(parseptr);

    // Date
    parseptr = strchr(parseptr, ',')+1;
    tmp = parsedecimal(parseptr); 
    date = tmp / 10000;
    month = (tmp / 100) % 100;
    year = tmp % 100;
    
    sLCD.print(COMMAND, BYTE);
    sLCD.print(LINE0, BYTE); 
    sLCD.print("La");
   
    if (latdir == 'N')
    {
       Serial.print('+');
          sLCD.print("+");
    }
    else if (latdir == 'S')
    {
     Serial.print('-');
     sLCD.print("-");
    }

    sLCD.print(latitude/1000000, DEC); sLCD.print(0xDF, BYTE); sLCD.print(' ');
    sLCD.print((latitude/10000)%100, DEC); sLCD.print('\''); //sLCD.print(' ');
    sLCD.print((latitude%10000)*6/1000, DEC); sLCD.print('.');
    sLCD.print(((latitude%10000)*6/10)%100, DEC); sLCD.print('"');
    
    sLCD.print(COMMAND, BYTE);
    sLCD.print(LINE1, BYTE);
    sLCD.print("Ln");
   
    if (longdir == 'E')
    {
     sLCD.print('+');
    }
    else if (longdir == 'W')
    { 
     sLCD.print('-');
    }
    sLCD.print(longitude/1000000, DEC); sLCD.print(0xDF, BYTE); sLCD.print(' ');  // 0xDF is the degree sign
    sLCD.print((longitude/10000)%100, DEC); sLCD.print('\''); //sLCD.print(' ');
    sLCD.print((longitude%10000)*6/1000, DEC); sLCD.print('.');
    sLCD.print(((longitude%10000)*6/10)%100, DEC); sLCD.print('"');   
  }
}
uint32_t parsedecimal(char *str)
{
  uint32_t d = 0;
  
  while (str[0] != 0)
  {
   if ((str[0] > '9') || (str[0] < '0'))
     return d;
   d *= 10;
   d += str[0] - '0';
   str++;
  }
  return d;
}

void readline(void)
{
  char c;
  buffidx = 0; // start at beginning
  while (1)
  {
      c=mySerial.read();
      if (c == -1)
        continue;
      if (c == '\n')
        continue;
      if ((buffidx == BUFFSIZ-1) || (c == '\r'))
      {
        buffer[buffidx] = 0;
        return;
      }
      buffer[buffidx++]= c;
 }
}

The problem is in the fact that only one instance of NewSoftSerial can be active at one time. There are hints on the NewSoftSerial site about how to use two instances at the same time.

What is the hardware serial port being used for?

The hardware serial port is the GPS. The GPS sketch on its own uses two instances and works fine.

The GPS sketch reads GPS data and displays on a sparkfun Serial LCD, that works without issue?

The GPS sketch on its own uses two instances and works fine.

Two instances of NewSoftSerial? Or one NewSoftSerial (software serial) and one Serial (hardware serial)?

Two instances of NewSoftSerial? Or one NewSoftSerial (software serial) and one Serial (hardware serial)?

The GPS sketch uses the same entries as in the sketch I'm having an issue with?

NewSoftSerial mySerial =  NewSoftSerial(4, 5);
NewSoftSerial sLCD =  NewSoftSerial(3, 14);

But, are you using both NewSoftSerial ports, at the same time, in the sketch that works?

Yes, one for the GPS, the other for the Serial LCD?

You said that the GPS was connected to the hardware serial port.

Sorry, I'm not being deliberately unhelpful, I'm trying to get to grips with all of this and don't fully understand it all!

This is a schematic of the shield. If I am reading it correctly, D0 & D1 aren't used?

As this is a commercial shield ( link ) then I'm restricted to using what I'm given here.

The example sketches have GPS and Canbus working as separate demos but I wanted to have both running on the one 20x4 sLCD.

I thought that, if the GPS sketch works with the same NewSoftSerial config then combining Canbus and GPS should work?

In the GPS sketch, all the data is collected, then it is sent to the LCD. The GPS instance is activated when it's read() method is called. If the GPS instance was not active, it becomes active when the read method is called. The script throws away any bad data, like one would get from trying to read from an inactive instance (which became the active instance in the process).

In the sketch using the GPS, the Canbus, and the LCD, the various software serial instance calls are mixed.

What happens if you try to collect all the data from the Canbus, without displaying it, then collect the GPS data, without displaying it, then use the sLCD.available() function to activate the sLCD instance, and then display all the data?

But at the moment, I'm not even invoking any of the Canbus functions. I've commented out that to debug the problem. At the moment I'm simply printing text to the sLCD in place of the data that would come from the Canbus if that was active.

But even with that simplified code, none of the GPS code seems to run when the Canbus part of the code is present.

If I comment out ALL the following or remove it from the sketch then the GPS routine runs:

 {
  //Canbus.ecu_req(ENGINE_RPM, canbuff);    
  sLCD.print(COMMAND, BYTE);                  
  sLCD.print(LINE2, BYTE);                     
  sLCD.print("Temp");                         

  //digitalWrite(LED3, HIGH);
  
  //Canbus.ecu_req(VEHICLE_SPEED, canbuff); 
  sLCD.print(COMMAND,BYTE);
  sLCD.print(LINE2 + 10,BYTE);                     
  sLCD.print("Speed");
      
  //Canbus.ecu_req(ENGINE_COOLANT_TEMP, canbuff);
  sLCD.print(COMMAND, BYTE);
  sLCD.print(LINE3, BYTE);                                   
  sLCD.print("RPM");
   
  //Canbus.ecu_req(VEHICLE_SPEED, canbuff); 
  sLCD.print(COMMAND,BYTE);
  sLCD.print(LINE3 + 10,BYTE);                     
  sLCD.print("Throttle");
     
  //digitalWrite(LED3, LOW); 
  delay(50);
  }

Then the GPS data appears on the LCD?

Exactly where the problem lies will probably require using Serial.print to print out trace statements to see if the Canbus library functions, the GPS library functions, and the LCD library functions are being called.

You have the source for the libraries, and you can use Serial.print in them, too.

An interesting dilemna. I wish I had the hardware to be able to replicate the problem.

Thanks for your help, I’ll have a delve!

Hmm, after re-adding all the Serial.print commands so I could see if the GPS was active etc and moving the CANBUS section of the code around I have now got it all working.

In the end, all I needed to do was move the CANBUS code below this section:

Serial.print(longitude/1000000, DEC); Serial.print('\°', BYTE); Serial.print(' ');
     sLCD.print(longitude/1000000, DEC); sLCD.print(0xDF, BYTE); sLCD.print(' ');
     
    Serial.print((longitude/10000)%100, DEC); Serial.print('\''); Serial.print(' ');
  sLCD.print((longitude/10000)%100, DEC); sLCD.print('\''); //sLCD.print(' ');
  
    Serial.print((longitude%10000)*6/1000, DEC); Serial.print('.');
    sLCD.print((longitude%10000)*6/1000, DEC); sLCD.print('.');
    
    Serial.print(((longitude%10000)*6/10)%100, DEC); Serial.println('"');
   sLCD.print(((longitude%10000)*6/10)%100, DEC); sLCD.print('"');

I'm still not clear as to why it works now and not before but it works and that's the main thing for now!

I'm still not clear as to why it works now and not before but it works and that's the main thing for now!

It's basically because the first NewSoftSerial method call for each instance is a throw away that simply changes the active instance. Since your code didn't have any throwaway-able calls, important ones were thrown away.

By moving all the Canbus data collection together, and the GPS data collection together, and the LCD code together, the active instance switching had less noticeable impact. The GPS and Canbus libraries seem to have throwaway calls built in.

This is not to imply that any of the calls are not needed, but the GPS sketch, for instance, did not include a call to NewSoftSerial::available(). Instead, it just called NewSoftSerial::read() in a while loop, returning only when the correct terminating character was encountered. The first call to GPS.read() is a throwaway, in that it never returned valid data, because the GPS instance was not the current instance.

I think that the Canbus library is working the same way.

I'd suspect that the first command sent to the LCD is not being sent correctly, but it might not matter. You could ensure that all the LCD.print stuff is properly performed, by calling LCD.available() first, which will always return 0, since the LCD is not sending you any data. The call would serve, though, to switch the current NewSoftSerial instance.