PowerBroker2 / SerialTransfer

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

Feature Request: Can this work for the Portenta #67

Closed hpssjellis closed 3 years ago

hpssjellis commented 3 years ago

I have read that the library does not support the Nano33Ble, is that an MBED thing? Could the library possibly support the Portenta ?

If not do you have any suggestions for parsing variables between Portenta's using UART?

Note: txData and rxData both compile version 2.10 MBED core on the Nano33Ble and the Portenta. Tomorrow I will check if they actually send data.

PowerBroker2 commented 3 years ago

That was only for the SPI part of the lib, which is currently disabled. The UART portions of the lib should be supported for all Arduinos AFAIK.

hpssjellis commented 3 years ago

Thanks for the reply @PowerBroker2 I am actually trying to get the 2 cores of the Portenta talking faster than the Normal Arduino RPC method for transferring variables. your tx rx datum examples look perfect. (I connect tx and rx pins together), but I get this in the serial monitor.

ERROR: PAYLOAD_ERROR
ERROR: PAYLOAD_ERROR
ERROR: PAYLOAD_ERROR
ERROR: PAYLOAD_ERROR

Any suggestions? Other than, lots of the MBED Portenta code doesn't work yet.

Here is the arduino forum topic complaining about the RPC speeds. https://forum.arduino.cc/t/passing-variable-from-cm7-to-cm4-using-rpc/692026/7

I am jerteach on that forum.

PowerBroker2 commented 3 years ago

PAYLOAD_ERROR happens when the received packet payload length value is not between 1 and 254 (inclusive). Here's where it happens in the code:

https://github.com/PowerBroker2/SerialTransfer/blob/3367a6f2056ef2496906ff4bcb2c93f43720fd49/src/Packet.cpp#L165-L185

Can you post your TX and RX sketches plus a complete debug printout (including timestamps) on the RX side?

hpssjellis commented 3 years ago

Thank you @PowerBroker2 for looking at this. The Portenta software has been a long haul. I will put this example on my library of Portenta examples if it can eventually work.

Not really sure how to make debug print to the Serial monitor, but here is my output and timestamps: I used a 3 second delay for Tx

14:17:09.572 -> ERROR: CRC_ERROR
14:17:12.579 -> ERROR: PAYLOAD_ERROR
14:17:15.633 -> ERROR: PAYLOAD_ERROR
14:17:18.692 -> ERROR: PAYLOAD_ERROR
14:17:33.858 -> ERROR: CRC_ERROR
14:17:36.861 -> ERROR: PAYLOAD_ERROR
14:17:39.884 -> ERROR: PAYLOAD_ERROR
14:17:55.073 -> ERROR: CRC_ERROR
14:17:58.101 -> ERROR: PAYLOAD_ERROR
14:18:01.150 -> ERROR: PAYLOAD_ERROR
14:18:04.192 -> ERROR: PAYLOAD_ERROR
14:18:07.240 -> ERROR: PAYLOAD_ERROR
14:18:22.366 -> ERROR: CRC_ERROR

The M7 core boots the M4 core.


#ifdef CORE_CM7  // Needed to only flash to the M7 core

#include "SerialTransfer.h"

SerialTransfer myTransfer;

struct STRUCT {
  char z;
  float y;
} testStruct;

void myHi()
{
  Serial.println("hi");
}

const functionPtr callbackArr[] = { myHi };

void setup()
{
  bootM4();   // boot the M4 inner Core
  Serial.begin(115200);
  Serial1.begin(115200);

  ///////////////////////////////////////////////////////////////// Config Parameters
  configST myConfig;
  myConfig.debug        = true;
  myConfig.callbacks    = callbackArr;
  myConfig.callbacksLen = sizeof(callbackArr) / sizeof(functionPtr);
  /////////////////////////////////////////////////////////////////

  myTransfer.begin(Serial1, myConfig);
}

void loop()
{
  if(myTransfer.available())
  {
    myTransfer.rxObj(testStruct);
    Serial.print(testStruct.z);
    Serial.println(testStruct.y);
  }
}

#endif   // done all M7 main core code

#ifdef CORE_CM4  // Needed to only flash to the inner M4 core

#include "SerialTransfer.h"

SerialTransfer myTransfer;

struct STRUCT {
  char z;
  float y;
} testStruct;

void setup()
{
  //Serial.begin(115200);  // M4 core does not have monitor access
  Serial1.begin(115200);
  myTransfer.begin(Serial1);

  testStruct.z = '$';
  testStruct.y = 4.5;
}

void loop()
{
  myTransfer.sendDatum(testStruct);
  delay(3000);
}

#endif   // done all M4 core code
PowerBroker2 commented 3 years ago

To simplify things, don't setup or use SerialTransfer callbacks. Instead, reference the examples here: TX and RX.

I'm assuming you're testing serial connection between the two cores via the same UART port. I'm not sure if the core libraries and UART hardware can support this. Perhaps you should test the connection between two different serial ports or even between one Arduino and another just to make sure you know how to use the library first before delving into the cross-core communication.

Again, perhaps removing the callback code will fix it...

hpssjellis commented 3 years ago

Good idea about trying 2 Arduino with Serial1. I will get back to you.

hpssjellis commented 3 years ago

Works between 2 Portenta's

15:17:06.279 -> $4.50 | hello 15:17:06.773 -> $4.50 | hello 15:17:07.267 -> $4.50 | hello 15:17:07.761 -> $4.50 | hello 15:17:08.300 -> $4.50 | hello

I will try the inner core again.

hpssjellis commented 3 years ago

Note: I have done lots of Serial1 communication between the 2 cores, so that should not be the issue.

Note I have also connected Pin 13 RX and 14 TX like I have done before.

Using the M4 to M7 cores and the data examples. With a delay of 5 seconds

15:22:38.153 -> ERROR: CRC_ERROR
15:22:53.418 -> ERROR: CRC_ERROR
15:23:08.683 -> ERROR: CRC_ERROR
15:23:18.873 -> ERROR: CRC_ERROR
15:23:34.185 -> ERROR: CRC_ERROR

here is this code:


#ifdef CORE_CM7  

#include "SerialTransfer.h"

SerialTransfer myTransfer;

struct STRUCT {
  char z;
  float y;
} testStruct;

char arr[6];

void setup()
{
  bootM4();
  Serial.begin(115200);
  Serial1.begin(9600);  

  ///////////////////////////////////////////////////////////////// Config Parameters
  configST myConfig;
  myConfig.debug        = true;
  /////////////////////////////////////////////////////////////////

  myTransfer.begin(Serial1, myConfig);
}

void loop()
{
  if(myTransfer.available())
  {
    // use this variable to keep track of how many
    // bytes we've processed from the receive buffer
    uint16_t recSize = 0;

    recSize = myTransfer.rxObj(testStruct, recSize);
    Serial.print(testStruct.z);
    Serial.print(testStruct.y);
    Serial.print(" | ");

    recSize = myTransfer.rxObj(arr, recSize);
    Serial.println(arr);
  }
}

#endif

#ifdef CORE_CM4  

#include "SerialTransfer.h"

SerialTransfer myTransfer;

struct STRUCT {
  char z;
  float y;
} testStruct;

char arr[] = "hello";

void setup()
{
 // Serial.begin(115200);
  Serial1.begin(9600);
  myTransfer.begin(Serial1);

  testStruct.z = '$';
  testStruct.y = 4.5;
}

void loop()
{
  // use this variable to keep track of how many
  // bytes we're stuffing in the transmit buffer
  uint16_t sendSize = 0;

  ///////////////////////////////////////// Stuff buffer with struct
  sendSize = myTransfer.txObj(testStruct, sendSize);

  ///////////////////////////////////////// Stuff buffer with array
  sendSize = myTransfer.txObj(arr, sendSize);

  ///////////////////////////////////////// Send buffer
  myTransfer.sendData(sendSize);
  delay(5000);
}

#endif
PowerBroker2 commented 3 years ago

Remove the following:

  ///////////////////////////////////////////////////////////////// Config Parameters
  configST myConfig;
  myConfig.debug        = true;
  /////////////////////////////////////////////////////////////////

  myTransfer.begin(Serial1, myConfig);

and replace it simply with:

  myTransfer.begin(Serial1);
PowerBroker2 commented 3 years ago

You might also have to go into Packet.cpp and start adding extra Serial debug statements to see what is going on and where it's failing.

hpssjellis commented 3 years ago

I will, The Portenta has been a headache, so I would assume no issue with your code. Interesting printout now.


15:31:30.724 -> ERROR: STALE PACKET
15:31:35.820 -> ERROR: STALE PACKET
15:31:40.936 -> ERROR: STALE PACKET
15:31:45.956 -> ERROR: STOP_BYTE_ERROR
15:31:51.113 -> ERROR: STALE PACKET
15:31:56.184 -> ERROR: PAYLOAD_ERROR
15:32:01.258 -> ERROR: PAYLOAD_ERROR
15:32:06.373 -> ERROR: STOP_BYTE_ERROR
15:32:11.489 -> ERROR: STALE PACKET
15:32:16.625 -> ERROR: STALE PACKET
15:32:21.704 -> ERROR: STALE PACKET
15:32:26.748 -> ERROR: PAYLOAD_ERROR
15:32:31.872 -> ERROR: PAYLOAD_ERROR
15:32:37.013 -> ERROR: STALE PACKET

#ifdef CORE_CM7  

#include "SerialTransfer.h"

SerialTransfer myTransfer;

struct STRUCT {
  char z;
  float y;
} testStruct;

char arr[6];

void setup()
{
  bootM4();
  Serial.begin(115200);
  Serial1.begin(9600);  

  myTransfer.begin(Serial1);
}

void loop()
{
  if(myTransfer.available())
  {
    // use this variable to keep track of how many
    // bytes we've processed from the receive buffer
    uint16_t recSize = 0;

    recSize = myTransfer.rxObj(testStruct, recSize);
    Serial.print(testStruct.z);
    Serial.print(testStruct.y);
    Serial.print(" | ");

    recSize = myTransfer.rxObj(arr, recSize);
    Serial.println(arr);
  }
}

#endif

#ifdef CORE_CM4  

#include "SerialTransfer.h"

SerialTransfer myTransfer;

struct STRUCT {
  char z;
  float y;
} testStruct;

char arr[] = "hello";

void setup()
{
 // Serial.begin(115200);
  Serial1.begin(9600);
  myTransfer.begin(Serial1);

  testStruct.z = '$';
  testStruct.y = 4.5;
}

void loop()
{
  // use this variable to keep track of how many
  // bytes we're stuffing in the transmit buffer
  uint16_t sendSize = 0;

  ///////////////////////////////////////// Stuff buffer with struct
  sendSize = myTransfer.txObj(testStruct, sendSize);

  ///////////////////////////////////////// Stuff buffer with array
  sendSize = myTransfer.txObj(arr, sendSize);

  ///////////////////////////////////////// Send buffer
  myTransfer.sendData(sendSize);
  delay(5000);
}

#endif
PowerBroker2 commented 3 years ago

Stale packets occur when a given packet isn't fully received within the timeout period. For instance, if I begin to receive bytes of a valid packet, but half way the data stream dries up and no bytes are received for 2 seconds (randomly chosen time period to make the point), we can safely assume the rest of the packet was lost due to dropped/disconnected signal.

See here:

https://github.com/PowerBroker2/SerialTransfer/blob/3367a6f2056ef2496906ff4bcb2c93f43720fd49/src/Packet.cpp#L121-L134

PowerBroker2 commented 3 years ago

In the available() member function within SerialTransfer.cpp, add debug statements showing the exact byte value for all bytes received. Add more debug statements in the sendData() function that shows the exact byte value for all bytes sent. Thie complete set of debug statements will tell you if the data is being corrupted.

hpssjellis commented 3 years ago

So the plot thickens. I can send serialTransfer between 2 different boards both running on the M4 core.

I can send from one M4 core to another board M7 core. So all is looking good there. Still testing one board sending from M4 to M7. Thanks for your suggestions.

hpssjellis commented 3 years ago

Just found out that the Portenta Serial1 does not do 115200 only 9600. Not sure if that is relevant.

PowerBroker2 commented 3 years ago

Yes, that is very relevant - change the Serial1 config to 9600 and things should work a lot better if what you say is true

hpssjellis commented 3 years ago

@PowerBroker2 sort of solved the issue. On the Portenta it has 4 UART ports, shared by both cores. If I use different ports, UART0 and URT1 using the Breakout Board, then SerialTransfer works great and very fast. Here is my working code, using A1 for an analog reading and connecting UART0 TX to UART1 RX.

#ifdef CORE_CM7  

#include "SerialTransfer.h"

UART myUART0(PA_0,  PI_9,  NC, NC); //TX, RX, RTS, CTC  NOTE: NC means not connected
UART myUART1(PA_9,  PA_10, NC, NC);

SerialTransfer myTransfer;

struct STRUCT {
  char z;
  float y;
} testStruct;

char arr[6];

void setup()
{
  bootM4();
  Serial.begin(115200);
  myUART1.begin(9600);  

  myTransfer.begin(myUART1);
}

void loop()
{
  if(myTransfer.available())
  {
    // use this variable to keep track of how many
    // bytes we've processed from the receive buffer
    uint16_t recSize = 0;

    recSize = myTransfer.rxObj(testStruct, recSize);
    Serial.print(testStruct.z);
    Serial.print(testStruct.y);
    Serial.print(" | ");

    recSize = myTransfer.rxObj(arr, recSize);
    Serial.println(arr);
  }
}

#endif

#ifdef CORE_CM4  

#include "SerialTransfer.h"

UART myUART0(PA_0,  PI_9,  NC, NC); //TX, RX, RTS, CTC  NOTE: NC means not connected
UART myUART1(PA_9,  PA_10, NC, NC);

SerialTransfer myTransfer;

struct STRUCT {
  char z;
  float y;
} testStruct;

char arr[] = "hello";

void setup()
{
 // Serial.begin(115200);
  myUART0.begin(115200);
  myTransfer.begin(myUART0);

}

void loop()
{
  // use this variable to keep track of how many
  // bytes we're stuffing in the transmit buffer
  uint16_t sendSize = 0;

  testStruct.z = '$';
  testStruct.y = (float)analogRead(A1);

  sendSize = myTransfer.txObj(testStruct, sendSize);

  sendSize = myTransfer.txObj(arr, sendSize);

  myTransfer.sendData(sendSize);
  delay(2000);
  //delayMicroseconds(300); // unsigned int max 65535
}

#endif

Doesn't really solve the problem because who wants to use some enormous breakout board in a final product, but at least it works.