Hardware Serial GPS , IDE difference 1.0.5 , 1.6

I have been using the Adafruit GPS with both the TinyGPS+ library and the Adafruit GPS library. I have it connected properly to a Mega on Serial2. I have successfully used two examples , FullExample (TinyGPS+) and Parsing(Adafruit), and they work great, while I am using the 1.0.5 IDE. However, if I try and run this exact same code under 1.6, the Mega never seems to get any data from the GPS. Both examples require about the same modification for use with hardware serial on a Mega, and it is basicaly commenting out the software serial and adding a line like this: HardwareSerial mySerial = Serial2; .

Is there something obvious that changed in dealing with the hardware serial form 1.0.5 to 1.6.x that would cause this behavior?

I didn't post all the code to those examples since they are available, but I will upon request.

You would normally just use

Serial2.begin(baudRate);

Then

Serial2.print(something);

when using Serial2.

What happens if you get rid of the unnecessary references to HardwareSerial and mySerial ?

MakoMaker: Both examples require about the same modification for use with hardware serial on a Mega, and it is basicaly commenting out the software serial and adding a line like this:

HardwareSerial mySerial = Serial2;

Is there something obvious that changed in dealing with the hardware serial form 1.0.5 to 1.6.x that would cause this behavior?

I can't say if something changed in the IDE, but I can say that, in general, copying an object like that is considered bad C++ practice, unless it's what you really intend and the author of the class supports it. Without going too far into C++ conventions, I will mention that many class header files are written in a way that prevents that line of code from ever compiling.

From a pure C++ perspective, I would have guessed that this the problem. Your code makes a second, fully-capable instance of the class HardwareSerial. For example, Serial2 is another instance of class HardwareSerial that is hooked to the second UART. It handles all the interrupts and buffering for that UART.

You don't really intend for both mySerial and Serial2 to handle interrupts and buffering. Actually, the interrupt is only handled by Serial2. Although there may be a difference between IDE versions, I'm not sure that should have ever worked in the previous version(s).

Let's look at three different ways you can have a mySerial.

1) Duplicated:

HardwareSerial mySerial = Serial2;

In C++ terms, this is a "shallow copy". If you look at the header file for class HardwareSerial, you would see this big scary constructor:

HardwareSerial(ring_buffer *rx_buffer, ring_buffer *tx_buffer,
      volatile uint8_t *ubrrh, volatile uint8_t *ubrrl,
      volatile uint8_t *ucsra, volatile uint8_t *ucsrb,
      volatile uint8_t *ucsrc, volatile uint8_t *udr,
      uint8_t rxen, uint8_t txen, uint8_t rxcie, uint8_t udrie, uint8_t u2x);

You say, "What the heck are all those arguments?" Exactly! Making an instance of HardwareSerial is very detailed, and this is how Serial2, Serial3, Serial4 are hooked to the right hardware bits.

The C++ compiler looks at your code and says, "I'll just memcpy everything that Serial2 has into mySerial. You're welcome." Unfortunately, this doesn't always work out, especially when pointers are involved.

For example, the String class has pointers to character arrays, and it is very careful to do this kind of copy in a "deeper" way. It has special code for allocating a copy of the array, so that both strings end up with their own data. Changing one string later will not affect the other string. The HardwareSerial class is not so careful.

2) Pointers:

HardwareSerial *mySerial = &Serial2;

This declares that mySerial "points" to the memory address of Serial2. There is just one version of Serial2, and mySerial knows where it's located. But to use mySerial, you have to use the "->" operator instead of ".":

mySerial.begin( 9600 );   // NO!  Won't compile
mySerial->begin( 9600 );  // ok.

The arrow operator tells the compiler that the Serial2 object is pointed to by mySerial. Use that address to call begin.

3) References:

HardwareSerial &mySerial = Serial2;

That one, sneaky ampersand essentially says that mySerial is an alias for Serial2. Internally, it's still a pointer, but you don't have to use the arrow; you still use the dot:

mySerial.begin( 9600 );   // Yep, just like it was Serial2.begin(9600)
mySerial->begin( 9600 );  // NO! Not a pointer!

All your code could use mySerial interchangeably with Serial2.


So, I suspect that you meant to have an alias for Serial2 (choice 3), not a whole 'nother copy (choice 1). And you could use a pointer to do what you want (choice 2), but you'd have to use the arrow operator everywhere.

Like HeliBob says, you would normally use Serial2 everywhere, avoiding the whole copy/pointer/reference question. But if you don't like how it reads, or you're tired of doing a search&replace for different serial ports, or you're passing the serial port into a function, then pointers or references are a good solution. One line can be changed at the top of your .INO, and you're using a different port everywhere.

Cheers, /dev

Your code makes a second, fully-capable instance of the class Serial.

No, it makes an instance of the HardwareSerial class - the same class that Serial is an instance of.

Fixt. Thanks, Paul!

Bob, /dev, and Paul,

Thank so much. I now better understand what's going on. It does appear that the examples in the libraries are not properly constructed to work with the 1.6 and forward, but with your eplanations I was able to modify an example from both libraries to work properly.

I eliminated the extra Hardware Serial objects and replaced them with direct references to Serial2 and it came online.

I banged my head on this code for a while and never could get it, but once you guys explained it like that, it clicked and the fix was clear and straightforward. Thanks for contributing to this forum in the way that you do.