Using Hardware Serial on Arduino Zero

Hi,

I am wanting to use the Adafruit Fona GSM module with the Arduino Zero board to send data via the MQTT protocol. I have it working on an Arduino Uno but because of the extended features of the Arduino Zero I want to use this instead. The problem is that the Arduino Zero does not support the SoftwareSerial library. I’m assuming that I need to use a hardware serial port instead but I don’t know how to do this? What would I have to change in my code to make this possible as I have tried researching online and can’t make any sense of it.

The main code;

/***************************************************
  
/********* NOTE: MUST uncomment debug line in Adafruit_MQTT.h (#define MQTT_DEBUG line 37) *********/

/* Edit config.h file for Mobile and MQTT broker config */

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

#include <Adafruit_FONA.h>
#include "config.h"
#include <Adafruit_SleepyDog.h>
#include <SoftwareSerial.h>
#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT_FONA.h"

/*************************** FONA Pins ***********************************/

// Default pins for Feather 32u4 FONA
#define FONA_RX  2
#define FONA_TX  3
#define FONA_RST 4
SoftwareSerial fonaSS = SoftwareSerial(FONA_TX, FONA_RX);

Adafruit_FONA fona = Adafruit_FONA(FONA_RST);

/************************* WiFi Access Point *********************************/


/************ Global State (you don't need to change this!) ******************/

// Setup the FONA MQTT class by passing in the FONA class and MQTT server and login details.
Adafruit_MQTT_FONA mqtt(&fona, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY);

// You don't need to change anything below this line!
#define halt(s) { Serial.println(F( s )); while(1);  }

// FONAconnect is a helper function that sets up the FONA and connects to the GPRS network. See the fonahelper.cpp tab above for the source!
boolean FONAconnect(const __FlashStringHelper *apn, const __FlashStringHelper *username, const __FlashStringHelper *password);

/****************************** Feeds ***************************************/

// Setup a feed called 'photocell' for publishing.
// Notice MQTT paths for AIO follow the form: <username>/feeds/<feedname>
Adafruit_MQTT_Publish button1 = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/button1");
//Adafruit_MQTT_Publish button1 = Adafruit_MQTT_Publish(&mqtt, "/input/" AIO_USERNAME "/" AIO_KEY);

// Setup a feed called 'onoff' for subscribing to changes.
Adafruit_MQTT_Subscribe onoffbutton = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/onoff");

/*************************** Sketch Code ************************************/

// How many transmission failures in a row we're willing to be ok with before reset
uint8_t txfailures = 0;
#define MAXTXFAILURES 3

void setup() {
  while (!Serial);

  // Watchdog is optional!
  //Watchdog.enable(8000);
  
  Serial.begin(115200);

  Serial.println(F("Adafruit FONA MQTT demo"));

  mqtt.subscribe(&onoffbutton);

  Watchdog.reset();
  delay(5000);  // wait a few seconds to stabilize connection
  Watchdog.reset();
  
  // Initialise the FONA module
  while (! FONAconnect(F(FONA_APN), F(FONA_USERNAME), F(FONA_PASSWORD))) {
    Serial.println("Retrying FONA");
  }

  Serial.println(F("Connected to Cellular!"));

  fonaDiagnostics();  // display various FONA diagnostics (may wish to log some of these?)

  Watchdog.reset();
  delay(5000);  // wait a few seconds to stabilize connection
  Watchdog.reset();


}

uint32_t x=0;

void loop() {
  // Make sure to reset watchdog every loop iteration!
  Watchdog.reset();

  // Ensure the connection to the MQTT server is alive (this will make the first
  // connection and automatically reconnect when disconnected).  See the MQTT_connect
  // function definition further below.
  MQTT_connect();
 
  Watchdog.reset();
  // Now we can publish stuff!
  Serial.print(F("\nSending photocell val "));
  Serial.print(x);
  Serial.print("...");
  if (! button1.publish(x++)) {
    Serial.println(F("Failed"));
    txfailures++;
  } else {
    Serial.println(F("OK!"));
    txfailures = 0;
  }

  Watchdog.reset();  
  // this is our 'wait for incoming subscription packets' busy subloop
  Adafruit_MQTT_Subscribe *subscription;
  while ((subscription = mqtt.readSubscription(5000))) {
    if (subscription == &onoffbutton) {
      Serial.print(F("Got: "));
      Serial.println((char *)onoffbutton.lastread);
    }
  }

  // ping the server to keep the mqtt connection alive, only needed if we're not publishing
  //if(! mqtt.ping()) {
  //  Serial.println(F("MQTT Ping failed."));
  //}

}

// Function to connect and reconnect as necessary to the MQTT server.
// Should be called in the loop function and it will take care if connecting.
void MQTT_connect() {
  int8_t ret;

  // Stop if already connected.
  if (mqtt.connected()) {
    return;
  }

  Serial.print("Connecting to MQTT... ");

  while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
    Serial.println(mqtt.connectErrorString(ret));
    Serial.println("Retrying MQTT connection in 5 seconds...");
    mqtt.disconnect();
    delay(5000);  // wait 5 seconds
  }
  Serial.println("MQTT Connected!");
}

void fonaDiagnostics() {
 uint8_t type;
 type = fona.type();
 Serial.print(F("FONA type "));
 switch (type) {
 case FONA800L: Serial.println(F("FONA 800L")); break;
 case FONA800H: Serial.println(F("FONA 800H")); break;
 case FONA808_V1: Serial.println(F("FONA 808 (v1)")); break;
 case FONA808_V2: Serial.println(F("FONA 808 (v2)")); break;
 case FONA3G_A: Serial.println(F("FONA 3G (American)")); break;
 case FONA3G_E: Serial.println(F("FONA 3G (European)")); break;
 default: Serial.println(F("???")); break;
 }

 // Print module IMEI number.
 char imei[15] = { 0 }; // MUST use a 16 character buffer for IMEI!
 uint8_t imeiLen = fona.getIMEI(imei);
 if (imeiLen > 0) {
 Serial.print("Module IMEI: "); Serial.println(imei);
 }

 // Optionally configure HTTP gets to follow redirects over SSL.
 // Default is not to follow SSL redirects, however if you uncomment
 // the following line then redirects over SSL will be followed.
 //fona.setHTTPSRedirect(true);

 // this is a large buffer for replies
 // 

 // read the ADC
 uint16_t adc;
 if (!fona.getADCVoltage(&adc)) {  Serial.println(F("Failed to read ADC")); }
 else { Serial.print(F("ADC = ")); Serial.print(adc); Serial.println(F(" mV")); }

 // read the battery voltage and percentage
 uint16_t vbat;
 if (!fona.getBattVoltage(&vbat)) { Serial.println(F("Failed to read Batt")); }
 else { Serial.print(F("VBat = ")); Serial.print(vbat); Serial.println(F(" mV")); }
 if (!fona.getBattPercent(&vbat)) { Serial.println(F("Failed to read Batt")); }
 else { Serial.print(F("VPct = ")); Serial.print(vbat); Serial.println(F("%")); }

 // read the time
 fona.enableNetworkTimeSync(1);
 fona.enableRTC(1);
 char buffer[23];
 fona.getTime(buffer, 23);  // make sure replybuffer is at least 23 bytes!
 Serial.print(F("Time = ")); Serial.println(buffer);

 // read the CCID
 char replybuffer[21];
 fona.getSIMCCID(replybuffer);  // make sure replybuffer is at least 21 bytes!
 Serial.print(F("SIM CCID = ")); Serial.println(replybuffer);

 // read the RSSI
 uint8_t n = fona.getRSSI();
 int8_t r;
 Serial.print(F("RSSI = ")); Serial.print(n); Serial.print(": ");
 if (n == 0) r = -115;
 if (n == 1) r = -111;
 if (n == 31) r = -52;
 if ((n >= 2) && (n <= 30)) {  r = map(n, 2, 30, -110, -54); }
 Serial.print(r); Serial.println(F(" dBm"));
}

Well, on the Fona guide it is stated :

At this time we don't have support for Hardware Serial to talk to the FONA

I think you'll have to replace every SoftwareSerial reference inside the library to use Serial1 instead...

@Stevie-G ,
may be you can find some hints also here:

Has anyone made any progress on this? Just started a similar project with the Adafruit FONA and a Sparkfun SAMD21 Mini breakout.

The Adafruit FONA library .cpp file defines its .begin() function as so:

boolean Adafruit_FONA::begin(Stream &port) {
  mySerial = &port;

  ...

  DEBUG_PRINTLN(F("Attempting to open comm with ATs"));
  // give 7 seconds to reboot
  int16_t timeout = 7000;

  while (timeout > 0) {
    while (mySerial->available()) mySerial->read();
    if (sendCheckReply(F("AT"), ok_reply))
      break;
    while (mySerial->available()) mySerial->read();
    if (sendCheckReply(F("AT"), F("AT"))) 
      break;
    delay(500);
    timeout-=500;
  }
...

And continues throughout the library to use mySerial to read and write to the serial port. Why would it matter if I passed to .begin() a hardware serial port or a software serial port? A quick search on "mySerial" within the .cpp file shows that all the calls to mySerial are functions inherited from Stream, which both Software Serial and Hardware Serial should also have. No?

Yes, Serial and Serial1 objects on the Arduino Zero are objects from the Uart class, that inherit from HardwareSerial, which itsel inherit from the Stream class.

Replacing
fona.begin(*fonaSerial)
by
fona.begin(Serial1)
and removing everything about softwareSerial compiles fine. I don't know if it works though, because I don't have a FONA board myself.

Following AloyseTech's advice, I got it working. To use the Sparkfun SAMD21 Mini Breakout with the Adafruit FONA 808 I had to initialize the FONA like so:

if (!fona.begin(Serial1)) {
	SerialUSB.println(F("Couldn't find FONA"));
	while (1);
}

However, this is not all you need to do to get it to work. Using a logic analyzer and some print statements, I noticed I was not able to progress past the "DEBUG_PRINTLN" macro in the fona.begin() function, so I did a find and replace on "DEBUG_PRINT" with "//DEBUG_PRINT" which takes care of both macros. After this, it worked.

Additionally, you can redefine the Serial port used in FONAPlatStd.h line 46 (default is Serial, which I did not have initialized as I was using SerialUSB and Serial1) or turn FONA debugging off the entirely by commenting out line 31 (#define ADAFRUIT_FONA_DEBUG) of FONAConfig.h.

Anyway, just figured I'd post my findings in case anyone else stumbles upon this thread in the future. Cheers.