Arduino Zero - SoftwareSerial library

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.

The pins you mention read differently in the page reference you linked. I will try to understand what is written there and experiment a bit :wink:

Hi Miq1,

The pins you mention read differently in the page reference you linked.

If you look at the bottom of the linked page, you'll find an example that exactly matches your requirement, namely using sercom2 on pins D3 and D4. Here's the code from adafruit's page:

    #include <Arduino.h>   // required before wiring_private.h
    #include "wiring_private.h" // pinPeripheral() function
     
    Uart Serial2 (&sercom2, 3, 4, SERCOM_RX_PAD_1, UART_TX_PAD_0);
    void SERCOM2_Handler()
    {
      Serial2.IrqHandler();
    }
     
    void setup() {
      Serial.begin(115200);
     
      Serial2.begin(115200);
      
      // Assign pins 3 & 4 SERCOM functionality
      pinPeripheral(3, PIO_SERCOM_ALT);
      pinPeripheral(4, PIO_SERCOM_ALT);
    }
     
    uint8_t i=0;
    void loop() {
      Serial.print(i);
      Serial2.write(i++);
      if (Serial2.available()) {
        Serial.print(" -> 0x"); Serial.print(Serial2.read(), HEX);
      }
      Serial.println();
      
      delay(10);
     
    }

The same code can be used as a template to configure sercom4 on D2 and D5.

Hi MartinL,

thanks, it worked with sercom2 on D3/D4. For sercom4 on D2/D5, would it be:

Uart Serial4 (&sercom4, 2, 5, SERCOM_RX_PAD_1, UART_TX_PAD_0);
    void SERCOM4_Handler()
    {
      Serial4.IrqHandler();
    }

setup()
{
...
 pinPeripheral(2, PIO_SERCOM_ALT);
 pinPeripheral(5, PIO_SERCOM_ALT);
...

then?

Hi Miq1,

You just need to reverse the pins D2=TX, D5=RX and change the PAD definitions. I've also changed Serial4 to Serial3, although this doesn't really matter:

Uart Serial3 (&sercom4, 5, 2, SERCOM_RX_PAD_3, UART_TX_PAD_2);
    void SERCOM4_Handler()
    {
      Serial3.IrqHandler();
    }

setup(){}

Hi MartinL,

looks like I am blind - with the code below, the D3/D4 UART ceased working, the Nextion display connected to it shows no reaction anymore. But why?

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

...

// Instantiate the Nextion-Serial and Serial2 class
Uart CellOne(&sercom4,  5,  2, SERCOM_RX_PAD_3, UART_TX_PAD_2);  
Uart Nextion(&sercom2,  3,  4, SERCOM_RX_PAD_1, UART_TX_PAD_0);    

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

void SERCOM4_Handler()    // Interrupt handler for SERCOM4
{
  CellOne.IrqHandler();
}
     
void setup() {

...

// Assign pins 3 & 4 for SERCOM2 functionality
  pinPeripheral(3, PIO_SERCOM_ALT);
  pinPeripheral(4, PIO_SERCOM_ALT);  
// Assign pins 2 & 5 for SERCOM4 functionality
  pinPeripheral(2, PIO_SERCOM_ALT);
  pinPeripheral(5, PIO_SERCOM_ALT);  

 ...

  Nextion.begin(9600);

  initNextion("Log"); // no effect :-(

  flashNextion("Nextion init done"); // neither effect

Oh, by the way: Serial1 (D0/D1) is in use as well and works.

Hi Miq1,

It's because you're calling the pinPeripheral() function before the Serialx.begin(). Call the begin() function for the serial ports first, then it should work OK.

You are right! Thanks a lot!

I never would have thought that assigning pins must be done after the initialization :astonished:

I am also looking for SoftwareSerial on Zero.
The reason is support for 9N1... There are forks of SoftwareSerial that implement it.

Can it be achieved with SERCOMs?

Hi mzayats,

Can it be achieved with SERCOMs?

Yes it can. It's detailed in Adafruit's tutorial: Creating a new Serial | Using ATSAMD21 SERCOM for more SPI, I2C and Serial ports | Adafruit Learning System.

MartinL:
Hi mzayats,

Yes it can. It's detailed in Adafruit's tutorial: Creating a new Serial | Using ATSAMD21 SERCOM for more SPI, I2C and Serial ports | Adafruit Learning System.

I'm not sure it is possible as is. if you take a look at HardwareSerial.h, you'll see that the 9N1 config doesn't exist.

Hi AloyseTech,

I'm not sure it is possible as is. if you take a look at HardwareSerial.h, you'll see that the 9N1 config doesn't exist.

You're right, I didn't notice that 9 bits worth of data isn't an option in hardware serial.

Although, it might be possible to disable the UART, set the number of bits to 9 and then restart. I haven't tested it though:

Serial1.begin(115200);                                          // Set-up Serial1 at 115200bps
SERCOM0->USART.CTRLA.reg &= ~SERCOM_USART_CTRLA_ENABLE;         // Disable the UART
while(SERCOM0->USART.SYNCBUSY.bit.ENABLE);                      // Wait for synchronization
SERCOM0->USART.CTRLB.reg |= SERCOM_USART_CTRLB_CHSIZE(1);       // Set CHSIZE to 1 = 9 bits per character 
SERCOM0->USART.CTRLA.reg |= SERCOM_USART_CTRLA_ENABLE;          // Re-enable the UART
while(SERCOM0->USART.SYNCBUSY.bit.ENABLE);                      // Wait for synchronization

yes, my understanding is that for 9N1, I would have to patch the libraries - those patches exist, but would force me to go back to 1.5.x or I will have to port the patches over. SoftwareSerial is available, just has the dependency on AVR.. Any plans to support ARM targets?

Hi mzayats,

I believe that a Software Serial library exists for Arduino.org's M0 PRO, (essentially the same as an Arduino Zero). It was included in Arduino.org's IDE from version 1.7.9 onwards: https://github.com/arduino-org/Arduino/releases.

Not sure if it supports 9 bit data, but might be worth a look?

Indeed - https://github.com/arduino-org/Arduino/tree/d110edbbfdf48d1b6ffc116ed1e58e742be4cf6e/hardware/arduino/samd/libraries/SoftwareSerial

Thanks!

Hey guys, So using this arduino.org library I can use software serial on a M0?

I am trying to compile a sketch on a adafruit feather M0, I dont understand enough to be able to port a software serial to an actual hardware serial.

Can can I go about using this arduino.org software serial library?

Thank!