PowerBroker2 / SerialTransfer

Arduino library to transfer dynamic, packetized data fast and reliably via Serial, I2C, or SPI
MIT License
423 stars 68 forks source link

Corruption at > 9600 over SoftwareSerial #61

Closed pauljeffress closed 3 years ago

pauljeffress commented 3 years ago

Hi there, I'm another big fan of your library here, but just having an issue with it over Serial, and in particular SoftwareSerial at one end of my link. I have an Adafruit FeatherM0 HW Serial port connected to a Sparkfun Artemis Global Tracker SoftwareSerial port with SerialTransfer (3.0.8) running over the top. The serial link works fine at 9600, at anything above the data TX'ed from the Artemis is slightly corrupted, such that the Feather is not able to reassemble a packet. I can see this via an FTDI Buddy I have looking at the TX pin on the Artemis. Traffic in the other direction is fine. I am also making sure I am not TX/RX concurrently on the Artemis as I understand that is a limitation of SoftwareSerial. I ran a basic test just pumping out chars from the Artemis (SoftwareSerial) to the Feather at 115200 no problem, everything received intact. The issue seems to be only when I use SerialTransfer and configure the link for > 9600. Best I can tell, when I take the link up to 19200, the initial char that I think should be 0x7E, looks like it is 0xFC on the wire? Likewise the 3rd byte looks like it should be 0xFF (when I look at it at 9600) but is 0xFA when sending at 19200. Everything else in the packet looks low from that point on, i.e. same in 9600 transmission as 19200 transmission. If I turn the speed up to say 38400, corruption appears in more of the packet.

This is the struct I am sending from the Artemis to the Feather;

struct STRUCT { // my datum to tx/rx over SerialTransfer
  char c1;
  char c2;
  char c3;
  char c4;
} STDatumTX, STDatumRX;

for the purposes of this testing I am statically setting it as follows;

  STDatumTX.c1 = 'A';
  STDatumTX.c2 = 't';
  STDatumTX.c3 = 'o';
  STDatumTX.c4 = 'F';

Screen Shot 2021-03-21 at 1 33 31 pm In the above screenshot the system is running at 19200 for most of that hex capture, and you can see an example in the red box of bad packet. Then a fair way down I switched to 9600 and you can see an example good packet in green.

Also, I have enabled debugging via STdriverF2A.begin(SerialtoAGT, true, Serial); on the Feather and STdriverF2A.begin(SerialtoAGT, true, Serial); on the Artemis, but neither spits out any output ?

Hopefully its something simple I am doing wrong?

pauljeffress commented 3 years ago

Full copy of code on Feather side

#include "global_vars.h"        // My main header file for this project itself

/**********************************/
/* Global Variables               */
/**********************************/

#define SENDPERIODSECONDS 17  // how often to send the dummy packets

/*
 * Create new Serial ports
 */
// Create SerialtoAGT
Uart SerialtoAGT (&sercom1, 11, 10, SERCOM_RX_PAD_0, UART_TX_PAD_2);
void SERCOM1_Handler()
{
  SerialtoAGT.IrqHandler();
}

// SerialTransfer initialisation
SerialTransfer STdriverF2A;  // create a SerialTransfer entity for the Feather to AGT connection.

struct STRUCT { // my datum to tx/rx over SerialTransfer
  char c1;
//  uint16_t i;
//  float f;
  char c2;
  char c3;
  char c4;
} STDatumTX, STDatumRX;

uint8_t sendALog = 0;  // used to determine when to send a log packet for the FDR.

int lastsend = 0; // used to determine when to send a dummy packet

bool feather_cant_tx_flag;
/**********************************/
/*  setup()                       */
/**********************************/
void setup() {
  // Initialise IDE Serial Monitor port
  Serial.begin(115200);
  delay(3000);  // wait for serial to be ready so we don't miss first bits of output to Serial Monitor
  Serial.println("Sketch Starting...");

  setupPins();  // set up the GPIO's that are used for FNICLISTEN etc.

  ledInitFlasher();

  // Initialise the Serial that connects this Feather to the AGT
  SerialtoAGT.begin(9600);  // AGT end is using SoftwareSerial, so go slower speed.
  // Reassign pins on the internal SAMD pinmux, to connect to my SERCOMs. They may have defaulted to other peripherals.
  // Assign pins 10 & 11 SERCOM functionality
  pinPeripheral(10, PIO_SERCOM);    // the 'PIO_SERCOM' should be 'PIO_SERCOM_ALT' if we are trying to use the 'alternate' pins for this.
  pinPeripheral(11, PIO_SERCOM);    // same as above comment.

  // Initialise FNIC SerialTransfer drivers
  STdriverF2A.begin(SerialtoAGT, true, Serial);

  // prep datum for first use
  STDatumTX.c1 = 'F';
//  STDatumTX.i = 100;
//  STDatumTX.f = 100.1;
//  STDatumTX.c2 = 'A';
  STDatumTX.c2 = 't';
  STDatumTX.c3 = 'o';
  STDatumTX.c4 = 'A';

  feather_cant_tx_flag = false;

} // END - setup()

/**********************************/
/*  loop()                        */
/**********************************/
void loop() {

  // If we are not currently allowing the AGT to TX, and hence holding ourselves
  // back from TX'ing to it, then lets check if its asking.
  if (!feather_cant_tx_flag){ // i.e. we are NOT currently sensing the AGT wants to TX
    // Is the AGT signalling it wants to TX to me?
    feather_cant_tx_flag = digitalRead(AGT_WANTS_TO_TX_PIN);
    // If it is, then respond and let it know it can, and block myself from TX'in until its done.
    if (feather_cant_tx_flag){  // AGT wants to TX to me
      // respond by Asserting FEATHER_READY_TO_RX_PIN
      Serial.println("Asserting FEATHER_READY_TO_RX_PIN to let AGT know it can safely TX");
      digitalWrite(FEATHER_READY_TO_RX_PIN, HIGH);
    }
  }    
  else{ // i.e we are currently allowing the AGT to TX, so lets check if it still wants to?
    feather_cant_tx_flag = digitalRead(AGT_WANTS_TO_TX_PIN);
    if (feather_cant_tx_flag){  // AGT_WANTS_TO_TX is still Asserted, so we need to continue to allow the AGT to TX 
    }
    else{ // AGT_WANTS_TO_TX has been dropped, so we no longer need to allow the AGT to TX
      // respond by Dropping FEATHER_READY_TO_RX_PIN
      Serial.println("Dropping FEATHER_READY_TO_RX_PIN to let AGT know it can NOT safely TX");
      digitalWrite(FEATHER_READY_TO_RX_PIN, LOW);
    }
  }
  // For the rest of loop() we need to check status of feather_cant_tx_flag before we ever TX to the AGT.

  // Check if the AGT has sent us anything
  // Use SerialTransfer receive code to get anything from our peer.
  if(STdriverF2A.available())
  {
    STdriverF2A.rxObj(STDatumRX);
    Serial.println("Received a Datum");
    Serial.println("<<<<<<<<<<<<<<<<");
    Serial.print("c1=");Serial.println(STDatumRX.c1);
//    Serial.print("i =");Serial.println(STDatumRX.i);
//    Serial.print("f =");Serial.println(STDatumRX.f);
    Serial.print("c2=");Serial.println(STDatumRX.c2); 
    Serial.print("c3=");Serial.println(STDatumRX.c3); 
    Serial.print("c4=");Serial.println(STDatumRX.c4);    
  } 

  // Send Dummy Datum
  // First make sure we are currently allowed to TX to the AGT.
  if (!feather_cant_tx_flag){
    if (millis() > (lastsend + (1000*SENDPERIODSECONDS))) // is it time to send another dummy packet?
    {
    lastsend = millis(); // timestamp this send.
    ledInitFlasher();

    // insert our dummy data
//    STDatumTX.i++;
//    STDatumTX.f += 1.1;

    Serial.println("Sending a Datum");
    Serial.println(">>>>>>>>>>>>>>>");
    Serial.print("c1=");Serial.println(STDatumTX.c1);
//    Serial.print("i =");Serial.println(STDatumTX.i);
//    Serial.print("f =");Serial.println(STDatumTX.f);
    Serial.print("c2=");Serial.println(STDatumTX.c2); 
    Serial.print("c3=");Serial.println(STDatumTX.c3); 
    Serial.print("c4=");Serial.println(STDatumTX.c4); 

    // Send the Datum to peer
    STdriverF2A.sendDatum(STDatumTX);

    } // END of send dummy packet
  } // END of can we TX IF 

} // END - loop()
pauljeffress commented 3 years ago

Full copy of code on Artemis side;

#include "global_vars.h"        // My main header file for this project itself

/**********************************/
/* Global Variables               */
/**********************************/

#define SENDPERIODSECONDS 10  // how often to send the dummy packets

// Create Serial2 by SoftwareSerial. 
SoftwareSerial SerialVirt(RXD2, TXD2); //RX, TX pins.

// SerialTransfer initialisation
SerialTransfer STdriverA2F;  // create a SerialTransfer entity for the AGT to Feather connection.

struct STRUCT { // my datum to tx/rx over SerialTransfer
  char c1;
//  uint16_t i;
//  float f;
  char c2;
  char c3;
  char c4;
} STDatumTX, STDatumRX;

int lastsend = 0; // used to determine when to send a dummy packet

/**********************************/
/*  setup()                       */
/**********************************/
void setup() {
  // Initialise IDE Serial Monitor port
  Serial.begin(115200);
  delay(3000);  // wait for serial to be ready so we don't miss first bits of output to Serial Monitor
  Serial.println("Sketch Starting...");

  setupPins();  // set up the GPIO's that are used for FNICLISTEN etc.

  ledInitFlasher();

  // Initialise the Serial that connects this AGT to the Feather
  SerialVirt.begin(9600);  // This port is using software serial, so go slower speed.

  // Initialise FNIC SerialTransfer drivers
  STdriverA2F.begin(SerialVirt, true, Serial);

  // prep datum for first use
  STDatumTX.c1 = 'A';
//  STDatumTX.i = 100;
//  STDatumTX.f = 100.1;
//  STDatumTX.c2 = 'A';
  STDatumTX.c2 = 't';
  STDatumTX.c3 = 'o';
  STDatumTX.c4 = 'F';

} // END - setup()

/**********************************/
/*  loop()                        */
/**********************************/
void loop() {

  // Check if the Feather has sent us anything
  // Use SerialTransfer receive code to get anything from our peer.
  if(STdriverA2F.available())
  {
    STdriverA2F.rxObj(STDatumRX);
    Serial.println("Received a Datum");
    Serial.println("<<<<<<<<<<<<<<<<");
    Serial.print("c1=");Serial.println(STDatumRX.c1);
//    Serial.print("i =");Serial.println(STDatumRX.i);
//    Serial.print("f =");Serial.println(STDatumRX.f);
    Serial.print("c2=");Serial.println(STDatumRX.c2); 
    Serial.print("c3=");Serial.println(STDatumRX.c3); 
    Serial.print("c4=");Serial.println(STDatumRX.c4);    
  } 

  // Send Dummy Datum
  if (millis() > (lastsend + (1000*SENDPERIODSECONDS))) // is it time to send another dummy packet?
  {
  lastsend = millis(); // timestamp this send.
  ledInitFlasher();

  Serial.println("Asserting AGT_WANTS_TO_TX_PIN to ask Feather if we can safely TX");
  // Ask Feather if we can TX to it (e.g. it's agreeing not to send anything to us for now)
  digitalWrite(AGT_WANTS_TO_TX_PIN, HIGH);
  int waitcount = AGT_WAIT_FOR_FEATHER; // load the timer
  while (!digitalRead(FEATHER_READY_TO_RX_PIN) && ( waitcount > 0)){ //
    delay(10);  // wait for 10mS before checking FEATHER_READY_TO_RX_PIN again.
    Serial.print("w");
    waitcount--;
  }
  Serial.println();
  // to get to here, either the pin went high or we exhausted the timer.
  if (digitalRead(FEATHER_READY_TO_RX_PIN)){  // Feather is ready for us to TX
    Serial.println("Sensed that Feather has correctly Asserted FEATHER_READY_TO_RX_PIN");
    Serial.println("So transmitting data to Feather");

    // insert our dummy data
//    STDatumTX.i++;
//    STDatumTX.f += 1.1;
    Serial.println("Sending a Datum");
    Serial.println(">>>>>>>>>>>>>>>");
    Serial.print("c1=");Serial.println(STDatumTX.c1);
//    Serial.print("i =");Serial.println(STDatumTX.i);
//    Serial.print("f =");Serial.println(STDatumTX.f);
    Serial.print("c2=");Serial.println(STDatumTX.c2); 
    Serial.print("c3=");Serial.println(STDatumTX.c3); 
    Serial.print("c4=");Serial.println(STDatumTX.c4); 
    // Send the Datum to peer
    STdriverA2F.sendDatum(STDatumTX);   

    Serial.println("Dropping AGT_WANTS_TO_TX_PIN to let Feather know we are done TX'ing");
    // Let Feather know we have finished TX'ing.
    digitalWrite(AGT_WANTS_TO_TX_PIN, LOW);
  }
  else{ // Feather did not respond in time, timer ran out :(
    Serial.println("!!!! Feather did not respond in time, timer ran out");
    Serial.println("Dropping AGT_WANTS_TO_TX_PIN to clean up our side");
    // Clean up our side
    digitalWrite(AGT_WANTS_TO_TX_PIN, LOW);
  }

  // Check/Ensure that the Feather drops FEATHER_READY_TO_RX_PIN as we have dropped AGT_WANTS_TO_TX_PIN
  // Note - strictly speaking I should only check for this is in the above code I actually saw it 
  // get Asserted in the first place, but to keep code flow simple, I'll just check here regardless.
  Serial.println("Check/Ensure that the Feather drops FEATHER_READY_TO_RX_PIN as we have dropped AGT_WANTS_TO_TX_PIN");
  waitcount = AGT_WAIT_FOR_FEATHER; // load the timer
  while (digitalRead(FEATHER_READY_TO_RX_PIN) && ( waitcount > 0)){ //
    delay(10);  // wait for 10mS before checking FEATHER_READY_TO_RX_PIN again.
    waitcount--;
  }
  // to get to here, either the pin went low or we exhausted the timer.
  if (!digitalRead(FEATHER_READY_TO_RX_PIN)){  // Feather is ready for us to TX
    Serial.println("Sensed that Feather has correctly Dropped FEATHER_READY_TO_RX_PIN");
  }
  else{ // Feather did not respond in time, timer ran out :(
    Serial.println("!!!! Feather did not respond (by dropping FEATHER_READY_TO_RX_PIN in time) and timer ran out");
  }

  } // END of send dummy packet

} // END - loop()
pauljeffress commented 3 years ago

also here is global_vars.h from the Artemis. I am not using all of this SRNP stuff anymore, so not much of this is used but still hanging around.

/*
 * global_vars.h
 * 
 * Overall header file for this project
 * 
 */

#ifndef GLOBAL_VARS_H
#define GLOBAL_VARS_H

/* types for global vars */
#include <Arduino.h>  // Placed here by PlatformIO 
#include <Wire.h> // PlatformIO needed me to explicitly include this before SerialTransfer.h
#include <SPI.h>  // PlatformIO needed me to explicitly include this before SerialTransfer.h
#include <SoftwareSerial.h> // needed by some HOSTs (i.e. AGT) that don't have enough HW Serial ports.
#include <SerialTransfer.h> // Note: This library will complain about SPDR in the SPITransfer.cpp/h files at compile time.
                            //       Its a known problem, search my Evernotes. The solution as I am not using SPI under
                            //       SerialTransfer is to rename two of the source files in the SerilTransfer library so
                            //       they are not compiled.  The two files are;
                            //       * SPITransfer.h and SPITransfer.cpp
                            //       they live in .pio/libdeps/..../SerialTransfer/src/
// if compiling on ArduinoIDE the below two .h files will be in the same folder as the master .ino, so 
// we must use "" not < > around the file names.  The "" tell the compiler to look in this directory for
// the files rather than a specific include directory as far as I know.
#include "srnp.h"
#include "srn_custom_host.h"

/* define any enums */
//enum statemachine { AWAKE, HOUSEKEEPING, INITMYSELF, PRESLEEP, SLEEP, POSTSLEEP, CHECKFORPACKETS, ACTIONPACKETS, CHECKONHOST, ACTIONHOST, REPORT };

/* extern my global vars */
//extern enum statemachine state;
extern SerialTransfer STdriverFNIC;
extern SrnpPacket sendPacket, receivePacket;
extern uint8_t rollingMagicNumber;

/* function pre defines */
void setupPins();
SrnpPacket emptyPacket();
void printPacket(SrnpPacket pac);
void ledInitFlasher();
SrnpPacket generateFDRPacket(char Fstr[SRNP_DATA_MAX_SIZE - 1]);
#endif   
PowerBroker2 commented 3 years ago

SoftwareSerial is notoriously slow and unstable at high bauds, so I don't expect it to work much above 9600 baud anyway. I would suggest using hardware UART ports instead. As far as I can see, the issue is independent of the library (baud changes don't affect how the library operates at all), so I'm closing the issue for now. If you do find a bug, feel free to reopen