SoftwareSerial issues on tinkercad

Hi there!

I’m new to Arduino and I am trying to learn/implement the Software Serial library, but I am stuck. I only have one physical board at the moment so I have been experimenting on Tinkercad with connecting two together, but it doesn’t seem to work. I can’t seem to send any data whatsoever from the Master device to the Slave device.

In have them connect as in the image attached.

I have lost most of today trying to work out what could be wrong, reading and re-reading the Software serial references, but I am totally stumped! Am I committing some noob rookie error or failing to understand something at a basic level??

Any advice would be greatly appreciated.

Many Thanks

Michael

/**************************************
 __  __    _    ____ _____ _____ ____  
|  \/  |  / \  / ___|_   _| ____|  _ \ 
| |\/| | / _ \ \___ \ | | |  _| | |_) |
| |  | |/ ___ \ ___) || | | |___|  _ < 
|_|  |_/_/   \_\____/ |_| |_____|_| \_\

***************************************/

#include <SoftwareSerial.h>

// define the transmit & receive pins
// for the SoftwareSerial port.
const byte rxPin = 2;
const byte txPin = 3;

// a new SoftwareSerial port instance.
SoftwareSerial masterPort0(rxPin, txPin);

void setup()
{ 
  // open comms on the hardware serial port.
  Serial.begin(9600);
  
  // set the pin mode of the SoftSerial port pins.
  pinMode(rxPin, INPUT);
  pinMode(txPin, OUTPUT);
  
  // begin comms on the SoftwareSerial port.
  masterPort0.begin(9600);
}

void loop(){
  
  masterPort0.println(10, DEC);
  Serial.println(10, DEC);
  delay(50);
}
/**************************************
 ____  _        ___     _______ 
/ ___|| |      / \ \   / / ____|
\___ \| |     / _ \ \ / /|  _|  
 ___) | |___ / ___ \ V / | |___ 
|____/|_____/_/   \_\_/  |_____|
                                
***************************************/

#include <SoftwareSerial.h>

// define the transmit & receive pins
// for the SoftwareSerial port.
const byte rxPin = 2;
const byte txPin = 3;

// a var to store the incoming data.
int incomingByte;

// a new SoftwareSerial port instance.
SoftwareSerial slavePort0(rxPin, txPin);

void setup(){ 
  
  // begin comms on the hardware serial port.
  Serial.begin(9600);
  
  // set the pin mode of the SoftSerial port pins.
  pinMode(rxPin, INPUT);
  pinMode(txPin, OUTPUT);
  
  // begin comms on the SoftwareSerial port.
  slavePort0.begin(9600);
}

void loop(){
  
 // listen on the SoftwareSerial port.
 slavePort0.listen();
  
  // reply only when you receive data:
  if(slavePort0.available() > 0) {
    // read the incoming byte:
    incomingByte = slavePort0.read();

    // say what you got:
    Serial.print("I received: ");
    Serial.println(incomingByte, DEC);
  }
}

master.ino (864 Bytes)

slave.ino (1.13 KB)

STICKLEBRICK's GIF.gif:

Connect the GND pin of one board to the GND pin of the other board.

Hi Pert,

Thanks for your reply (and not totally berating me!). Yes I did figure that out eventually.... Rookie error indeed!

The tutorial I was following had a textual description of the connections needed, but no mention of ground. Maybe it just assumed that this would be obvious to everyone & anyone.

I just need to figure out why, when I send data from the master to the slave and echo it out of the slave's hardware serial port, the data doesn't match the data sent from the master...

I definitely forgot to connect the grounds once myself.

I do notice that the ground connection is sometimes not mentioned. I think the pros assume that's a given and are more focused on the more tricky signal connections.

STICKLEBRICK:
I just need to figure out why, when I send data from the master to the slave and echo it out of the slave's hardware serial port, the data doesn't match the data sent from the master...

This line in your master code:

STICKLEBRICK:

Serial.println(10, DEC);

Sends the string "10\r\n". That is four bytes. Each byte is the ASCII code for the character:
https://www.asciitable.com/

This line in your slave code:

STICKLEBRICK:

    incomingByte = slavePort0.read();

Reads one byte of incoming data.
This line:

STICKLEBRICK:

Serial.println(incomingByte, DEC);

Prints the decimal value of the ASCII data. The decimal representation of the character '1' is 49. So the expected behavior of this code is to print:

I received: 49
I received: 48
I received: 13
I received: 10

If you want to send the actual value 10, rather than its text representation, then you should use write() instead of println().

OP may find it useful to read the pdf of this link on UART based Serial Data Communication using Arduino.

pert:
This line in your master code:Sends the string "10\r\n". That is four bytes. Each byte is the ASCII code for the character:

1. I could never figure out why is this "10\r\n" not interpreted as 6 character -- '1', '0', '', 'r', '', and 'n'. It is always sent as four async frames for these four characters: '1', '0', CR, and Newline (0x31, 0x30, 0x0D, 0x0A).

2. I have also difficulty to understand/explain --
Given:

byte x1 = 0x41;
char x2 = 0x41;

Serial.print(x1); //shows: 65
Serial.print(x2)  //shows: A

Both x1 and x2 contain the same numerical number 0x41; but, the above two print() methods show different visual things. How to explain it?

GolamMostafa:
1. I could never figure out why is this “10rn” not interpreted as 6 character – ‘1’, ‘0’, ‘’, ‘r’, ‘’, and ‘n’. It is always sent as four async frames for these four characters: ‘1’, ‘0’, CR, and Newline (0x31, 0x30, 0x0D, 0x0A).

because any character preceded by a \ is an escape sequence:

even though you must type two keystrokes to write it, this is a single character.

If you did want to sent that string verbatim and achieve your expected result of 6 characters, you would need to escape the escape character:
“10\r\n”

GolamMostafa:
2. I have also difficulty to understand/explain –
Given:

byte x1 = 0x41;

char x2 = 0x41;

Serial.print(x1); //shows: 65
Serial.print(x2)  //shows: A




Both x1 and x2 contain the same numerical number 0x41; but, the above two print() methods show different visual things. How to explain it?

Because print() is overloaded. Each parameter type is handled differently in order to always print the text representation of the parameter. Here’s the overload of print() used for the byte type parameter (byte is a typedef for unsigned char on the AVR boards):

size_t Print::print(unsigned char b, int base)
{
  return print((unsigned long) b, base);
}

Then here’s the unsigned long overload:

size_t Print::print(unsigned long n, int base)
{
  if (base == 0) return write(n);
  else return printNumber(n, base);
}

Next to printNumber():

size_t Print::printNumber(unsigned long n, uint8_t base)
{
  char buf[8 * sizeof(long) + 1]; // Assumes 8-bit chars plus zero byte.
  char *str = &buf[sizeof(buf) - 1];

  *str = '\0';

  // prevent crash if called with base == 1
  if (base < 2) base = 10;

  do {
    char c = n % base;
    n /= base;

    *--str = c < 10 ? c + '0' : c + 'A' - 10;
  } while(n);

  return write(str);
}

And finally to the string overload of write():

/* default implementation: may be overridden */
size_t Print::write(const uint8_t *buffer, size_t size)
{
  size_t n = 0;
  while (size--) {
    if (write(*buffer++)) n++;
    else break;
  }
  return n;
}

The char overload of write() is implemented differently by each library that inherits from the Print class.

Since the default format of the integer overloads of print() is DEC (decimal), it prints the text representation of the base 10 representation of 0x41. This is done because it’s assumed that if the user passes a number to print(), they expect a number to be printed.

The char overload of print() prints the ASCII character representation of 0x41 because it’s assumed that if the user passes a char to print(), they expect a character to be printed.

write() dispenses with all this conversion and just sends the data as is. So if you used Serial.write() instead of Serial.print() in your example code, it would have resulted in your expected result of both being the same.

Since

Cool :slight_smile: !+

If I have understood correctly --

When the data is passed with the 'byte keyword', the print() is converted to the following two frames of write() methods to show 65 on the Serial Monitor:

byte x1 = 0x41;
Serial.print(x1);
==> Serial.print(x1, DEC);  //because of byte (unsigned char) keyword

==>
Serial.write(0x36);    //shows 6
Serial.write(0x35);    //shows 5

//-----------------------------------------------
When the data is passed with the 'char keyword', the print() is converted to the following single frame of write() method to show A on the Serial Monitor:

char x2 = 0x41;
Serial.print(x2);

==>
Serial.write(0x41);    //shows A