Problem using NewSoftSerial

I've got a strange problem communicating between an Arduino Uno ATMEGA328P and PICAXE08M. I'm trying to figure out where the problem, hope you can help. I suspect NewSoftSerial because I'm creating two NewSoftSerial instances each with a different baud rate.

For debugging I've been doing the following, which works...I'm sending commands to the Arduino from my laptop, the Arduino then sends a command to the PICAXE which send a response via serial out back to the laptop.

Laptop <---> Arduino Uno ---> PICAXE ---> Laptop

Example:
send command 'GetVersion' to Arudio...Arudio sends 'V' to PICAXE, PICAXE send the string '001+' to Laptop. I've verified this works correctly and reliably. If the PICAXE receives a bad command from the Arduino it replies with the string '' where XXX is the decimal value of the character it received.

It stops working when I physically connect the PICAXE's Tx line to the Arduino's Rx.

Laptop <---> Arduino Uno <---> PICAXE

In this configuration the Arudio always receives the string '<254>' from the PICAXE. This shows the Arduino can receive from the PICAXE (because the received string is correctly framed) but is no longer sending correctly. Once complicating factor is the baud rates for Tx and Rx are different. From the Arduino's perspective Tx is 4800 and Rx is 9600. I'm suspecting the interrupts from two different baud rates on the Arduino aren't playing nice. Any ideas?

#include <NewSoftSerial.h>

static unsigned int PA_txBaudRate       = 4800;
static unsigned int PA_rxBaudRate       = 9600;

#define rxPin1 4
#define txPin1 5
#define rxPin2 12
#define txPin2 13

// set up a new serial port
NewSoftSerial mySerial1 = NewSoftSerial(rxPin1, txPin1, true);
NewSoftSerial mySerial2 = NewSoftSerial(rxPin2, txPin2, true);
byte pinState = 0;

char
PICAXE08M_MC_Init()
{
  pinMode(rxPin1, INPUT);
  pinMode(txPin1, OUTPUT);
  pinMode(rxPin2, INPUT);
  pinMode(txPin2, OUTPUT);
  mySerial1.begin(PA_rxBaudRate);
  mySerial2.begin(PA_txBaudRate);
  return 1;
}

static void
PICAXE08M_MC_Print(const char* str)
{
  char c = 0;
  // Pulse the txPin high to generate an interrupt on the PICAXE
  digitalWrite(txPin2, HIGH);
  digitalWrite(txPin2, LOW);

  // Wait for the PICAXE to be ready to read the data
  delay(8);
  int len = strlen(str);
  for(int ii=0; ii<len; ii++) {
    c = *(str+ii);
    mySerial2.print(c);
    if(ii + 1 < len) {
      delay(2);
    }
  }
}

int
PICAXE08M_MC_GetVersion(const char* inData, int dataLength, char* outBuffer, int outBufLength)
{
  uint8_t cnt = 0;
  const int VERSION_LEN = 3;
  char c = 0;
  int retry = 0;

  snprintf(outBuffer, 4, "123\0");
  PICAXE08M_MC_Print("V");
  while (cnt < VERSION_LEN && retry < 500) {
    // read the incoming byte:
    if(mySerial1.available() > 0) {
      outBuffer[cnt] = mySerial1.read();
      cnt++;
    }
    else {
      retry++;
      delay(1);
    }
  }
  retry = 0;
  while(retry < 100) {
    if(mySerial1.available() > 0) {
      c = mySerial1.read();
      if(c == '+') {
        outBuffer[cnt++] = c;
        return cnt;
      }
      outBuffer[cnt++] = c;
    }
    else {
      retry++;
      delay(1);
    }
  }
  outBuffer[cnt++] = 'E';
  outBuffer[cnt++] = 'r';
  outBuffer[cnt++] = 'r';
  outBuffer[cnt++] = 'o';
  outBuffer[cnt++] = 'r';
  return cnt;
}

The code you posted is incomplete so I don't have a whole overview . Is this Arduino code or PICAXE code?

You are fiddling with the transmit pins that are used by NewsoftSerial, which will corrupt the correct transmission.

static void
PICAXE08M_MC_Print(const char* str)
{
  char c = 0;
  // Pulse the txPin high to generate an interrupt on the PICAXE
  digitalWrite(txPin2, HIGH);  << HERE
  digitalWrite(txPin2, LOW);  << AND HERE
 // Pulse the txPin high to generate an interrupt on the PICAXE
  digitalWrite(txPin2, HIGH);
  digitalWrite(txPin2, LOW);

Serial lines normally idle HIGH, this implies that yours is idling LOW. I gather NSS can work both ways, but is that correct?

Also, are you transmitting to the Picaxe at 4800 and receiving at 9600? If so why?

Also outBuffer is not null terminated, I hope you aren't using it as a string.


Rob

@robtillaart

The posted code is for the Arduino, it's complete as far as the serial communication with the PICAXE goes. The rest of my code deals with other stuff and is quite large. Pulsing the txPin high is needed for the PICAXE to wake up and get ready to receive serial data. There is no outgoing serial data from the Arduino when *_Print() is called, so nothing to be corrupted.

@Graynomad

NewSoftSerial says it works with inverted serial lines, and I've verified that Tx works when the PICAXE's serial out goes to my laptop. I've verified that NSS receives inverted too because the Arduino receives the PICAXE's error messages correctly '<254>'. So, from the Arduino's perspective, Tx works when there is no Rx. But not when Rx is connected. Strange.

I have the asymmetrical baud rates because the PICAXE's Serial Out pin uses the sertxd command and 'The baud rate is fixed at 4800,n,8,1 (9600,n,8,1 on X2 parts)'. And the part 08M doesn't support the serrxd command, so I have to use the serin command which maxes out at 4800.

The functions return the byte count because they received character's aren't null terminated, I've just been calling them a string for convenience, outBuffer is really a character buffer.

For HW I've got a common ground and pull downs on the Tx lines so they low when not driven.

Could it be anything other than NSS?

sertxd command and 'The baud rate is fixed at 4800,n,8,1

so I have to use the serin command which maxes out at 4800.

You're using an 08 so doesn't this mean 4800 both ways?

Have you verified that the string passed to PICAXE08M_MC_Print is correct?

Is 2mS enough for the 08 to process the character and 8mS enough to get it's attention? (I would think so but you never know)


Rob

Another thought, this

 digitalWrite(txPin2, HIGH);
  digitalWrite(txPin2, LOW);

will produce a pretty small pulse, the Picaxe "interrupts" are really polled aren't they, will the 08 recognize it? What about trying a small delay() between the HIGH and LOW to make sure.


Rob

Thanks for all your help, it now works.

Adding a delay(1) between the HIGH/LOW strobe fixed the problem. It threw me for a while because the first time through the *_Print() statement with the delay() does not help, however all subsequent *_Print()s do work.

digitalWrite(txPin2, HIGH);
delay(1);
digitalWrite(txPin2, LOW);