You can have high-speed debugging by utilizing either I2C or SPI to send to a second Arduino. That second Arduino can be devoted to receiving debugging information and passing it on (via serial to your PC).
I describe the process here.
The general idea is to add some stuff to the DuT (device under test) to output via SPI/I2C.
Example code:
// make true to debug, false to not
#define DEBUG true
#include <SPI.h>
// conditional debugging
#if DEBUG
#define beginDebug() do { SPI.begin (); SPI.setClockDivider(SPI_CLOCK_DIV8); } while (0)
#define Trace(x) SPIdebug.print (x)
#define Trace2(x,y) SPIdebug.print (x,y)
#define Traceln(x) SPIdebug.println (x)
#define Traceln2(x,y) SPIdebug.println (x,y)
#define TraceFunc() do { SPIdebug.print (F("In function: ")); SPIdebug.println (__PRETTY_FUNCTION__); } while (0)
class tSPIdebug : public Print
{
public:
virtual size_t write (const byte c)
{
digitalWrite(SS, LOW);
SPI.transfer (c);
digitalWrite(SS, HIGH);
return 1;
} // end of tSPIdebug::write
}; // end of tSPIdebug
// an instance of the SPIdebug object
tSPIdebug SPIdebug;
#else
#define beginDebug() ((void) 0)
#define Trace(x) ((void) 0)
#define Trace2(x,y) ((void) 0)
#define Traceln(x) ((void) 0)
#define Traceln2(x,y) ((void) 0)
#define TraceFunc() ((void) 0)
#endif // DEBUG
long counter;
unsigned long start;
void setup() {
start = micros ();
beginDebug ();
Traceln (F("Commenced device-under-test debugging!"));
TraceFunc (); // show current function name
} // end of setup
void foo ()
{
TraceFunc (); // show current function name
}
void loop()
{
counter++;
if (counter == 100000)
{
Traceln (F("100000 reached."));
Trace (F("took "));
Traceln (micros () - start);
counter = 0;
foo ();
} // end of if
} // end of loop
The other Arduino (which receives the debugging information) simply echoes it to Serial:
char buf [1000];
volatile int inpoint, outpoint;
void setup (void)
{
Serial.begin (115200); // debugging
Serial.println ();
Serial.println (F("Commencing debugging session ..."));
Serial.println ();
// have to send on master in, *slave out*
pinMode(MISO, OUTPUT);
// turn on SPI in slave mode
SPCR |= bit (SPE);
// now turn on interrupts
SPCR |= bit (SPIE);
} // end of setup
// SPI interrupt routine
ISR (SPI_STC_vect)
{
byte c = SPDR; // grab byte from SPI Data Register
int next = inpoint + 1; // next insert point
// wrap-around at end of buffer
if (next >= sizeof buf)
next = 0;
if (next == outpoint) // caught up with removal point?
return; // give up
// insert at insertion point
buf [inpoint] = c;
inpoint = next; // advance to next
} // end of interrupt routine SPI_STC_vect
void loop (void)
{
// insertion and removal point the same, nothing there
noInterrupts (); // atomic test of a 16-bit variable
if (outpoint == inpoint)
{
interrupts ();
return;
}
interrupts ();
// display anything found in the circular buffer
Serial.print (buf [outpoint]);
noInterrupts ();
if (++outpoint >= sizeof buf)
outpoint = 0; // wrap around
interrupts ();
} // end of loop
SPI is quite fast, so the overhead of doing the debugging should be slight. The receiving end can batch up the debugging information (up to a point which is about 1000 characters) and then send it to your PC via Serial.
I made a library out of the relevant code. The library is available from:
http://gammon.com.au/Arduino/SPI_Debugging.zip
Example using the library:
// make true to debug, false to not
#define DEBUG true
#include <SPI.h>
#include <SPI_Debugging.h>
long counter;
unsigned long start;
void setup() {
start = micros ();
beginDebug ();
Traceln (F("Commenced device-under-test debugging!"));
TraceFunc (); // show current function name
} // end of setup
void foo ()
{
TraceFunc (); // show current function name
}
void loop()
{
counter++;
if (counter == 100000)
{
Traceln (F("100000 reached."));
Trace (F("took "));
Traceln (micros () - start);
counter = 0;
foo ();
} // end of if
} // end of loop
There is a similar library for I2C debugging:
http://gammon.com.au/Arduino/I2C_Debugging.zip
Example of doing I2C debugging:
// make true to debug, false to not
#define DEBUG true
const byte SLAVE_ADDRESS = 100; // which address debugging goes to
#include <Wire.h>
#include <I2C_Debugging.h>
long counter;
unsigned long start;
void setup() {
start = micros ();
beginDebug ();
Traceln (F("Commenced device-under-test debugging!"));
TraceFunc (); // show current function name
} // end of setup
void foo ()
{
TraceFunc (); // show current function name
}
void loop()
{
counter++;
if (counter == 100000)
{
Traceln (F("100000 reached."));
Trace (F("took "));
Traceln (micros () - start);
counter = 0;
foo ();
} // end of if
} // end of loop
All of the examples illustrate using a #define
to turn the debugging output on or off, so you can leave the debugging prints there in case things go wrong, but with debugging disabled they won't have any penalty.
listen()
. You can't do anything at all whilst writing on a SoftwareSerial port. That includes listening on another SoftwareSerial port. So anything that arrives on the first port whilst you are writing earlier data out of the second port will become corrupted or not arrive at all.