Problem with Software Serial talking bi-bidirectional with Sabertooth controller

I am having difficulty with the software serial port. I have spent nearly a full day on this issue, so I feel it is now time to go to the community for help.

I am attempting to use an Arduino Uno to talk with the Sabertooth 2x32 motor controller. I want to be able to use the hardware serial so I can use the Serial Monitor , so I am using the Software Serial ports to talk with the Sabertooth..

I have successfully created a simple program that sends commands to the Sabertooth over the Software Serial.

Now I am attempting to query the motor amperage and then forward the text response from the Sabertooth to the hardware serial. This way I can read it in the Arduino's serial monitor on my Windows machine.

Here is the code, with the line that generates the error highlighted.

#include <SoftwareSerial.h>                     //Load the sofware serial library
SoftwareSerial SaberSoftSerial(11, 9);          //Define software serial

void setup() {
  SaberSoftSerial.begin(9600);                  //set baud rate for software serial
  
  serial.begin(57600);                          //start the hardware serial port
    while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }
  
  serial.println("just testing");                // <<<<<<<<<<<<< IT IS OK WITH THIS LINE
}

void loop() {


  SaberSoftSerial.println("M1:1000");        // Send a command out Software Serial to Sabertooth. 
  delay(1000);

  SaberSoftSerial.println("A1:get");          // Request amperage on motor 1 via software serial.

  // read the text that the Sabertooth motor controller sends, and forward it to hardware serial.
if (SaberSoftSerial.available()) {
  serial.print(SaberSoftSerial.read);       // THIS LINE GENERATES ERROR, SAYING 'serial' was not declared in this scope.

  serial.println(" amps");
  }
  
}

I believe my hardware serial port is already setup and has those related commands in the proper ares of the program. So, why am I getting the "Not defined in this scope" message? Is there some sort of conflict with the hardware serial?

serial.begin(57600); //start the hardware serial port
Need Serial.begin here, capital S. Fix in other places you call "serial.something" as well.

This is not needed at all in an Uno? No native USB support (like a 32U4 in Leonardo of Micro)
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}

First, you need to look at Serial Input Basics on the Useful Links page. The problem is that serial communications take time... a long time, from the Arduino's 16MHz perspective. An eternity!

So you have to let your loop runrunrun while the characters gradually go out and the response gradually returns:

#include <NeoSWSerial.h>

NeoSWSerial SaberSoftSerial(11, 9);

void setup() {

  SaberSoftSerial.begin(9600);
  
  Serial.begin(57600);
  while (!Serial)
    ;
  
  Serial.println( F("just testing") ); // F macro on double-quoted string prints saves RAM!
}

void loop() {

  SaberSoftSerial.println( F("M1:1000"));  // Set motor 1 speed?
  delay(1000);

  SaberSoftSerial.println("A1:get");       // Request amperage on motor 1

  // Wait for a response
  uint32_t startTime = millis();
  
  while (millis() - startTime < 500) {     // up to 0.5 seconds

    // Did one character finally come back?
    if (SaberSoftSerial.available()) {

      // Yes, get it.
      char c = SaberSoftSerial.read();

      // Is this the end of the response line?
      if (c == '\n')
        break; // Yes, go print the units

      // Ignore any CR characters
      if (c != '\r') {
        // Echo everything else
        Serial.print( c );
      }
    }
  }
  Serial.println( F(" amps") );
  
}

This also uses the "Blink without delay" technique to have a timeout on reading the response. Eventually, you will need to use some flags ("state" variables) so you can get rid of these delays. Right now, the loop is very slow and waits for each thing to happen sequentially. It stays in that while loop until the response '\n' is received or 0.5 seconds elapses. This "blocking" may eventually keep you from doing other things.

I would also recommend NeoSWSerial instead of SoftwareSerial. SoftwareSerial blocks interrupts for long periods of time, which will interfere with other parts of your sketch, or with other libraries. Also, SoftwareSerial cannot transmit and receive at the same time, which could become a problem later.

If you could use pins 8 & 9 instead, AltSoftSerial would be even better!

Cheers,
/dev

Thank you both for the replies. I see that the capitalization of terms matters a lot. Unfortunately, the capitalization rules seem inconsistent. Sometimes all lower-case, and sometimes initial capital.

But, I had given up on the Software serial and now have purchased a Mega, with its 4 serial ports, in hopes it would simplify things for this relative novice. But, I am stuck again.

There is an example on this web page, that demonstrates the use of two hardware serial ports. In my case, I wired the main serial TX to the serial1 RX, and the serial1 TX to the main serail RX. So, I would have though it would simply echo back to the serial monitor, whatever I send via the serial monitor.

/*
Multiple Serial test

Receives from the main serial port, sends to the others.
Receives from serial port 1, sends to the main serial (Serial 0).

The circuit:

  • Any serial device attached to Serial port 1
  • Serial monitor open on Serial port 0:

created by Tom Igoe & Jed Roach
This example code is in the public domain.

*/

void setup() {
// initialize both serial ports:
Serial.begin(9600);
Serial1.begin(9600);
}

void loop() {
// read from port 1, send to port 0:
if (Serial1.available()) {
int inByte = Serial1.read();
Serial.write(inByte);
}

// read from port 0, send to port 1:
if (Serial.available()) {
int inByte = Serial.read();
Serial1.write(inByte);
}
}

It seems to compile and load onto the Mega. But, I don't get anything back into the serial monitor when I type and send something. I do see that the Serial receive light on the Mega indicates that the serial monitor did send the text I typed, but the receive light does not flash.

One test I did was to add a line in the main loop;

Serial.println("test");

It did fill the serial monitor with "test". So, I know I can both send and receive serial data on the serial monitor. Am I mis-understanding what the example program does?

-Joe

Am I mis-understanding what the example program does?

Yes. It is intended to provide a "bridge" for some other device, like a bluetooth or GPS. This turns your mega into an expensive USB-to-TTL serial adapter for connecting the other device to your PC. It's handy for seeing exactly what the device is sending, or for sending commands to the device from the Serial Monitor window.

It is not intended for a hard-wired loopback test, using the Serial Monitor window to send to the Mega. The problem is that, with your jumpers, you really have two things connected to the Mega RX: Serial1 TX and the Serial Monitor TX (via USB cable to the on-board USB-to-TTL adapter, an ATmega16U2 chip).

If the Serial Monitor sends a character, the Mega receives it and immediately writes it on Serial1. But this is the same wire that the Serial Monitor is using to send the second character. They will collide and Serial RX can't make anything out of the mess.

Try sending just one character with the Serial Monitor, and make sure the "Newline" choice (lower-right corner) is set to "No line ending". When there is no second character, the Serial1 write of the first character will be correctly received by Serial... which writes it to Serial1 again. You have basically wired an infinite loop for just one character.

Cheers,
/dev

Thank you for taking the time to talk with me on this. I have been frustrated with it over several days, and can't seem to make any headway.

First, I probably should explain why I was using this simple serial program on the Mega I just purchased. I have not been successful in bi-directional talking with my Sabertooth motor controller. So, I figured I should just disconnect from the Sabertooth, and see if I can get a better understanding of serial communications itself by making things as simple as possible.

I just tried your test of just sending one character, with the "No Line Ending" option set. (It has always been this way). But, there is nothing echoed back.

It seems I am not understanding how the USB and main serial connection are handled. I know they somehow are associated with each other, so that pins 0 and 1 are the serial port and are also the USB port.

But, somehow the example is supposed to permit the user to send/receive serial data from/to the monitor to the external serial device. I have a high degree of confidence in the example, since it is provided on the arduino.cc web site, and so gets vetted by many users. I am not using an external device at the moment. I did try it with the Sabertooth, with no results.

If the Serial Monitor sends a character, the Mega receives it and immediately writes it on Serial1. But this is the same wire that the Serial Monitor is using to send the second character. They will collide and Serial RX can't make anything out of the mess.

I am not trying to be argumentative, as I suspect that you are right, and I am wrong in my understanding. But, to explain my current thinking... Why will they collide, if the output of one is sent to the input of the other?

I can see how an external device may not be likely to simultaneously send data while it is also receiving .. but it might.

-Joe

If you look at your Mega board, you will see a small, square black chip near the USB connector. This ATmega16U2 chip is a separate processor that handles the nasty USB protocol (on some of its pins) and talks to the ATmega2560 (on some other pins). The ATmega2560 is the largest black square chip near the center of the Arduino board.

When you write a program in the IDE and upload it, it goes over the USB to the 16U2. The 16U2 takes that sketch information and "writes" it into the 2560. Now your sketch is inside the "Arduino". The 2560 then starts running your program.

The same kind of thing happens when you run the Serial Monitor. When you "Send" characters from the window, they go out of the PC on the USB cable, wrapped in the USB protocol "envelopes". The 16U2 receives these packets, and starts writing the enclosed characters to its TX pin. Essentially, it forwards a few bytes from the PC to the 2560.

The key is that the 16U2 TX pin is connected to the 2560 RX pin 0. But you have connected the 2560 Serial1 pin 18 to RX pin 0. Now there are two things that can talk on the same receiving pin.

The reverse is also true: The 16U2 RX pin is connected to the 2560 TX pin 1. When you call Serial.print, characters are transmitted on pin 1, and it is received by the 16U2. The 16U2 then wraps it in a USB protocol packet and sends it to the PC over the USB, where the MS-Windows application "Serial Monitor" reads the character. That app then shows it in the window for you to see. So if you connect a receiver (like Serial1 RX pin 19) to Serial TX pin 1, there will be two things listening: the 16U2 and the receiver (Serial1 in your case).

Multiple listeners (the latter case) are not a big deal. Multiple talkers (the former case) usually cause problems.

You usually don't have to understand that the 16U2 is a separate processor. Your sketches are not loaded into that one; they are forwarded to the 2560.

Cheers,
/dev

When you write a program in the IDE and upload it, it goes over the USB to the 16U2. The 16U2 takes that sketch information and "writes" it into the 2560.

No, it merely passes the serial data it receives along to the 2560 Rx pin - the bootloader in the 2560 writes the sketch into flash memory.
The 16U2 also passes back any to the PC any serial data the 2560 Tx sends out.
As you said, many listeners on the Mega's Tx pin are fine, many talkers on the Mega's Rx pin are likely to be a problem.