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
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
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
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.
HardwareSerial *mySerial = &Serial2;
This declares that
mySerial "points" to the memory address of
Serial2. There is just one version of
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
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
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.