Arduino Zero - SoftwareSerial library

I need to use the softwareSerial library, and isn't available for the arduino zero. Does anyone know of an alternative that is compatible with the Zero?

I'm looking for the same.

Any solution or suggestions?

Why do you want a Software UART ?

Not only are there 6 SERCOM channels, you can re-arrange quite a few of the hardware pins.

Explain what you want to do. It is easy to bit-bash an Serial TX. Doing RX in software is fiddly.

David.

There are 6 Serial Communication (Sercom) channels on the SAMD21, each capable of operating as either a USART, I2C or SPI port.

Serial1 (on Sercom0) is obviously on digital pins 0 (Rx) and 1 (Tx).

Sercom 1 and 2 used to be free, but I believe that the Zero's latest "variant.cpp" file has now been modified to allow Serial2 and 3 to be used. So you should just be able to call on Serial2.begin() or Serial3.begin() in your sketch.

Serial2 (on Sercom1) should be on digital pins 12 (Rx) and 10 (Tx).

Serial3 (on Sercom2) should be on digital pins 5 (Rx) and 2 (Tx) on the Zero, or 5 (Rx) and 4 (Tx) on the M0 Pro.

If "variant.cpp" file hasn't been modified, the thread here shows you how to get the extra serial ports working.

Sercom 3 and 4 are reserved for Arduino's I2C Wire and SPI libraries. Sercom5 is reserved for serial communication with the EDBG debugging chip.

1 Like

As I look at variants.cpp on github, (today) I only see Serial1 and Serial declared. Is that the right place or the only place to look?

Yes, "variant.cpp"/"variant.h" are the places to look.

If it's not there, it's possible to get Serial2 working on D12 (Rx) and D10 (Tx) without modification to the "variant" files, using the following code in your Arduino sketch. This code example simply echos back characters sent from the Arduino IDE console:

// Serial2 pin and pad definitions (in Arduino files Variant.h & Variant.cpp)
#define PIN_SERIAL2_RX       (34ul)               // Pin description number for PIO_SERCOM on D12
#define PIN_SERIAL2_TX       (36ul)               // Pin description number for PIO_SERCOM on D10
#define PAD_SERIAL2_TX       (UART_TX_PAD_2)      // SERCOM pad 2
#define PAD_SERIAL2_RX       (SERCOM_RX_PAD_3)    // SERCOM pad 3

// Instantiate the Serial2 class
Uart Serial2(&sercom1, PIN_SERIAL2_RX, PIN_SERIAL2_TX, PAD_SERIAL2_RX, PAD_SERIAL2_TX);

void setup()
{
  Serial2.begin(115200);          // Begin Serial2
}

void loop()
{ 
  if (Serial2.available())        // Check if incoming data is available
  {
    byte byteRead = Serial2.read();    // Read the most recent byte 
    Serial2.write(byteRead);      // Echo the byte back out on the serial port
  }
}

void SERCOM1_Handler()    // Interrupt handler for SERCOM1
{
  Serial2.IrqHandler();
}

Implementing Serial3 however, requires a modification to the "variant.cpp" file. Just dd the two following lines to the end of the "g_APinDescription" array in the file ..Computer\AppData\Roaming\Arduino15\packages\arduino\hardware\samd\1.6.1\variants\arduino_zero\variant.cpp:

// 44..45 - SERCOM2
  { PORTA, 14, PIO_SERCOM, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, // SERCOM2/PAD[2]
  { PORTA, 15, PIO_SERCOM, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, // SERCOM2/PAD[3]

Then to get Serial3 working on D5 (Rx) and D2 (Tx) on the Zero (or D5 (Rx) and D4 (Tx) on the M0 Pro), use the following code:

// Serial3 pin and pad definitions (in Arduino files Variant.h & modified Variant.cpp)
#define PIN_SERIAL3_RX       (45ul)               // Pin description number for PIO_SERCOM on D5
#define PIN_SERIAL3_TX       (44ul)               // Pin description number for PIO_SERCOM on D2
#define PAD_SERIAL3_TX       (UART_TX_PAD_2)      // SERCOM pad 2
#define PAD_SERIAL3_RX       (SERCOM_RX_PAD_3)    // SERCOM pad 3

// Instantiate the Serial3 class
Uart Serial3(&sercom2, PIN_SERIAL3_RX, PIN_SERIAL3_TX, PAD_SERIAL3_RX, PAD_SERIAL3_TX);

void setup()
{
  Serial3.begin(115200);          // Begin Serial3
}

void loop()
{ 
  if (Serial3.available())        // Check if incoming data is available
  {
    byte byteRead = Serial3.read();    // Read the most recent byte 
    Serial3.write(byteRead);      // Echo the byte back out on the serial port
  }
}

void SERCOM2_Handler()    // Interrupt handler for SERCOM2
{
  Serial3.IrqHandler();
}

Or alternatively, just replace your "variant.cpp" and "variant.h" files (in the directory specified above) with these modified ones (attached). This allows you to call on Serial2 and Serial3 in your Arduino sketch directly without any additional code:

variant.h (6.88 KB)

variant.cpp (18.9 KB)

A problem we've encountered with Teensy 3.x involves 3rd party libraries with dependency on SoftwareSerial.

No matter how many real hardware serial ports you have (Teensy has 3 available), you can't use any of them if your project depends on some library that requires a pointer or a C++ reference to an object of type "SoftwareSerial".

Ideally, libraries should use Stream pointers or references, so they could be used interchangeably with HardwareSerial, SoftwareSerial and EthernetClient or other non-serial data streams. Some libraries do, but many do not. Some libraries don't even provide a function or constructor to set the stream, so you have to edit the library code. Some libraries have overloaded functions to take HardwareSerial or SoftwareSerial, but not any other Stream-based objects. Some only work with SoftwareSerial and have no provision to use a real serial port!

A lesser problem involves libraries that could take a HardwareSerial or Stream inherited object, in their constructor or begin function or whatever way they're configured, but they only provide examples using SoftwareSerial. Usually such code is only ever tested with Arduino Uno. Even if it could work, the lack of any examples or documentation, with all apparent paths showing it only works with SoftwareSerial on Uno, is a real stumbling block for beginners.

For Teensy, a "solution" I implemented involves a dummy SoftwareSerial library. It only works if you configure for two pins which are one of the real hardware serial ports. Internally, it just calls the hardware serial functions. That's a tiny amount of extra overhead, but of course far more efficient than bit bashing serial. Because it's actually defined as the SoftwareSerial object, you can use software that has a hard-coded dependency on SoftwareSerial. I've never tested it on any other boards, but if you grab the code, it'll probably work on Zero with only a little fiddling to change the pin numbers and hardware serial port names.

Thank you MartinL. Your solution worked out first try for me. I'm back up an running because you took the time to post this simple solution.

Justin

Hi MartinL

MartinL:
Or alternatively, just replace your "variant.cpp" and "variant.h" files (in the directory specified above) with these modified ones (attached). This allows you to call on Serial2 and Serial3 in your Arduino sketch directly without any additional code:

Which directory? Please explain.

Hi glovisol,

The currently Arduino core code sets up the Zero to create two serial ports. Serial1 on digtial pins 0 and 1, as well as Serial that connects to the programming port (via the Atmel EDBG chip). However, it's possible to create additional hardware serial ports by configuring the spare serial communication or Sercom modules on the SAMD21. The Sercom modules are flexible in that they can be configured to be an I2C, SPI port or a USART.

The "variant.cpp" and "variant.h" files define the Zero's pin allocation and amongst other thinks sets up Serial1 and Serial. It's possible to edit these files to include the configuration for Serial2 and Serial3, thereby removing the configuration code from your sketch. In hindsight though, it's probably best to leave most of it in your sketch as the variant files are overwritten everytime you update the core to the latest version (1.6.4). Serial3 does require a small change appended to the pin definitions in "variant.cpp" though.

Variant.cpp can be currently found in the following directory (at least on my Windows machine): C:\Users\Computer\AppData\Local\Arduino15\packages\arduino\hardware\samd\1.6.4\variants\arduino_zero\variant.cpp.

Hi MartinL

Thank you for the detailed info. I take it you are using Windows XP. I have a very similar path on the XP machines, but up to now it has been impossible for me to find the variant files in Windows 7, but I will try again tomorrow. I forgot how to open up Documents & settings which is locked up, but will recall...

In XP it is (as always better):

C:\Documents and Settings\Administrator\Impostazioni Locali\Dati Applicazioni\Arduino 15\packages\arduino\hardware\samd\1.6.4\variants\arduino_zero\variant.cpp.

and

C:\Documents and Settings\Administrator\Impostazioni Locali\Dati Applicazioni\Arduino 15\packages\arduino\hardware\samd\1.6.4\variants\arduino_zero\variant.h

Kind regards,

glovisol

So i tried the example from adafruit to output tx on 10 and rx on 11, this works fine, now when i try to switch to pins 6TX and 7RX with sercom3 enable, pad2 tx, pad3 for rx, i am not getting anything output from tx. not sure why not.

#include <Arduino.h> // required before wiring_private.h
#include "wiring_private.h" // pinPeripheral() function

Uart Serial2 (&sercom3, 7, 6, SERCOM_RX_PAD_3, UART_TX_PAD_2);
void SERCOM3_Handler()
{
Serial2.IrqHandler();
}

void setup() {

Serial2.begin(115200);
// Assign pins 10 & 11 SERCOM functionality
pinPeripheral(6, PIO_SERCOM);
pinPeripheral(7, PIO_SERCOM);
}

void loop() {

Serial2.println
("going");
delay(100);
}

Hi ChrisChris,

Try changing the PIO_SERCOM to PIO_SERCOM_ALT.

Sercom3 for these pins is on the alternative sercom peripherial channel.

Excellent!!!! That Works, Hoping that it would i already have a printed board with those pins dedicated as uart, thank god I actually put them on the correct pad for tx and rx.

Hi MartinL
Thanks for your tipps.
I work with Arduino Zero and the Adafruit shield "Adafruit Ultimate GPS+Logging Shield".

Using RX:D12 and TX:D10 and setting th switch to "Soft Serial" works fine

// Serial2 pin and pad definitions (in Arduino files Variant.h & Variant.cpp)
#define PIN_SERIAL2_RX (34ul) // Pin description number for PIO_SERCOM on D12
#define PIN_SERIAL2_TX (36ul) // Pin description number for PIO_SERCOM on D10
#define PAD_SERIAL2_TX (UART_TX_PAD_2) // SERCOM pad 2
#define PAD_SERIAL2_RX (SERCOM_RX_PAD_3) // SERCOM pad 3

// Instantiate the Serial2 class
Uart Serial2(&sercom1, PIN_SERIAL2_RX, PIN_SERIAL2_TX, PAD_SERIAL2_RX, PAD_SERIAL2_TX);

I strongly asume, setting the switch on this shield to "Soft Serial" will connect D7 from Arduino Zero directly to RX of the shield and D8 to TX. Unfortunately I didn't find a schematics to verify my assumption, but it looks like on the surface of the shield. Do you have a link to the schematics?

As consequence I would like to alter your approche to use D7 and D8, because otherwise D7 and D8 are no longer usable for the Arduino Zero due to possbple electrical shurtcuts.

I could not find out what (number ul) to use. Pleas give me a tipp.

Thank you

Best regards
Urs

Hi Urs,

I had a look a Adafruit's shield compatibility guide: https://cdn-learn.adafruit.com/downloads/pdf/adafruit-shield-compatibility.pdf

The Zero isn't mentioned, but as it mentions the Due, I assume that the GPS Data Logger Shield is 3.3V compatible.

This guide states that this shield uses D0 and D1 for hardware serial, or D7 and D8 for software serial. Unfortunately, D7 and D8 on the Zero don't share the same Serial Communications (SERCOM) port, so configuring them as a hardware serial port isn't possible. However, the Zero's "Serial1" port is on D0 and D1, so your best bet is to move the switch to hardware serial and in Adafruit's example code redefine "mySerial" to "Serial1":

//SoftwareSerial mySerial(8, 7);
#define mySerial Serial1
Adafruit_GPS GPS(&mySerial);

Another issue (if you're using it) is going to be your SPI, micro SD card data logger. The Arduino SPI library for the Zero doesn't use digtal pins D10-D13 like the Uno, but instead uses the 6-pin ICSP header. Fortunately it's possible to configure the Zero to use the same pins as the Uno, by following Adafruit's SERCOM guide here: Creating a new SPI | Using ATSAMD21 SERCOM for more SPI, I2C and Serial ports | Adafruit Learning System

It's for their Feather M0 board, but it's similar to the Zero, (they use the same processor).

By the way, here's the schematic: Adafruit Learning System

Hi Urs,

Looking at the schematic, it appears as though Adafruit have reversed their Rx and Tx pins that go to digital pins 0 and 1. Reversing these pins and loading the Uno with a blank sketch allows a direct connection from the GPS to your PC. Using this method the GPS's NMEA data can be displayed on the Arduino IDE's console. Unfortunately, this won't work with the Zero.

To get the board to work with Serial1 on the Zero's D0 and D1 pins, your going to have to use the "Direct Connection with Jumpers on Leonardo" method:

This requires you to select "Software Serial" on the switch and directly connect the GPS's Tx to the Zero's Rx (D0) and the GPS's Rx to the Zero's Tx (D1) using jumper wires. Doing this however means that you can't use D7 or D8.

Hi Martin
Thank you for this investigation and your answer.
Best Regards
Urs

Hi all,

I have to pick up this thread as I think you may be the right audience for my issue.

I would like to use pins 2/3 for Serial2 and 4/5 for Serial3 on the Zero for easier cabling. Is that possible without modifying the variant files? Just with local code in my sketch?

Any help is appreciated :slight_smile:

Hi Miq1,

If you're using an Arduino.cc's Zero then D2/D5 and D3/D4 make TX/RX sercom pairs, whereas on an Arduino.org's M0/M0 Pro it's D2/D3 and D4/D5. This is because on Arduino.cc's Zero, pins D2 and D4 are swapped.

The Adafruit article: Creating a new Serial | Using ATSAMD21 SERCOM for more SPI, I2C and Serial ports | Adafruit Learning System, shows how it's possible to access the sercom ports using the pinPeripheral() function. This function gives the pins sercom functionality without having to modify the variant files.

Assuming that you're using the Arduino.cc's Zero, then D2 and D5 use sercom2, or if you're prepared to forgo the SPI port on sercom4. Pins D3 and D4 use sercom0 or sercom2, however sercom0 is already used by D0 and D1.