arduino-libraries / Arduino_Portenta_OTA

OTA on the Arduino Portenta.
15 stars 4 forks source link

Trouble trying to OTA update M4 core #42

Open IgnacioSJ opened 10 months ago

IgnacioSJ commented 10 months ago

Hi! I was trying to update M4 core with this library using an Arduino GIGA and it seems that it doesn´t work for me, maybe I'm not doing it in the right way. I want to upload the following sketch (that works fine if I upload it through USB) to both cores:

#include <RPC.h>
String currentCPU() {
  if (HAL_GetCurrentCPUID() == CM7_CPUID) {
    return "M7";
  } else {
    return "M4";
  }
}
void setup() {
  if(currentCPU() == "M7") {
    Serial.begin(9600);
    while(!Serial);
    RPC.begin();
  }
  if(currentCPU() == "M4") {
    RPC.begin();
  }
}
void loop() {
  if(currentCPU() == "M7") {
    String buffer = "";
    while (RPC.available()) {
      buffer += (char)RPC.read();
    }
    if (buffer.length() > 0) {
      Serial.print(buffer);
    }
  }
  if(currentCPU() == "M4") {
    delay(1000);
    RPC.println("Message to M7 from M4");
  }
}

I have done all the steps without any problem and it seems that M7 downloads the code and updates fine. That's not the case for M4. If I upload "OTA on M4" code on M7, it uploads only for M7 but if I upload it on M4, it seems to be not working because it doesn't reset (I can't see Serial monitor because Serial.println() only works for M7, so I deleted the part that asks for Serial to read a response). What I have to do to upload the same sketch into both cores?

Thanks!

pennam commented 10 months ago

Hi @IgnacioSJ how do you build your M4 OTA file ? Have you seen this PR https://github.com/arduino-libraries/Arduino_Portenta_OTA/pull/40

It's been a while i've done it, and i had totally forgot about it but it should work ...

IgnacioSJ commented 10 months ago

Hi @pennam, I build only 1 OTA file (bin into lzss and lzss into ota using GIGA parameter) for both cores because the sketch is the same, function currentCPU() tells each core what it has to do. I used PR #40 on M7 and works fine but if I use it on M4 it seems to do nothing.

pennam commented 10 months ago

Hi @IgnacioSJ it cannot work using one single OTA file using PR #40 you have to OTA M4 and M7 separately using two different OTA file, one including the M7 binary and the other the M4 binary

IgnacioSJ commented 10 months ago

OK, so I split the code into 2 binary files and build them to OTA files. Next thing I'm supposed to do is to upload #40 on M4 and wait for the OTA update of the code, right? Then, if I'm not wrong, #40 won't work because of Serial waiting for a response and M4 doesn't allow Serial, only M7 and it will be waiting for a response that it won't receive. This makes me think that I have to upload #40 on M7 instead of M4.

pennam commented 10 months ago

PR #40 contains an example sketch OTA_Qspi_Flash_M4.ino that you can load on the M7 and it will update with an OTA your M4 core. This is because the file being downloaded is an M4 binary file.

If you want to make your own M4 OTA file you can follow the instructions in that example:

Sendos-JuanPG commented 10 months ago

Could I update a sketch to both cores separately OTA using ethernet shield with Arduino GIGA?

Sendos-JuanPG commented 10 months ago

I have tried successfully with WiFi, but with ethernet connection using ethernet shield v2 (w5500) I get the following error:

Arduino_Portenta_OTA_QSPI::download failed with error code -3003

facchinm commented 9 months ago

@Sendos-JuanPG the function download() works only with MbedClient classes at the moment, so Ethernet Shield (w/ w5500) is excluded at the moment. You can try subclassing the OTA class with something like this (there's a TODO so take extra care when dealing with this sketch :slightly_smiling_face: )


/*
   This example demonstrates how to use to update the firmware of the Arduino Portenta H7 using
   a firmware image stored on the QSPI.

   Steps:
     1) Create a sketch for the Portenta H7 and verify
        that it both compiles and works on a board.
     2) In the IDE select: Sketch -> Export compiled Binary.
     3) Create an OTA update file utilising the tools 'lzss.py' and 'bin2ota.py' stored in
        https://github.com/arduino-libraries/ArduinoIoTCloud/tree/master/extras/tools .
        A) ./lzss.py --encode SKETCH.bin SKETCH.lzss
        B) ./bin2ota.py PORTENTA_H7_M7 SKETCH.lzss SKETCH.ota
     4) Upload the OTA file to a network reachable location, e.g. OTA_Usage_Portenta.ino.PORTENTA_H7_M7.ota
        has been uploaded to: http://downloads.arduino.cc/ota/OTA_Usage_Portenta.ino.PORTENTA_H7_M7.ota
     5) Perform an OTA update via steps outlined below.
*/

/******************************************************************************
   INCLUDE
 ******************************************************************************/

#include <Arduino_Portenta_OTA.h>

#include <Ethernet.h>
#include <ArduinoHttpClient.h>

static char const OTA_FILE_LOCATION[] = "http://10.130.22.225/ota/OTA_Usage_Portenta.ino.GIGA.ota";
extern const char * UPDATE_FILE_NAME_LZSS;

class Arduino_Portenta_OTA_QSPI_HttpClient : public Arduino_Portenta_OTA_QSPI {
  public:
    Arduino_Portenta_OTA_QSPI_HttpClient(StorageTypePortenta x, int y) : Arduino_Portenta_OTA_QSPI(x, y) {}
    int download(const char * url, bool const is_https) {
      // TODO: split url into its parts
      EthernetClient e;
      HttpClient client(e, "10.130.22.225", 80);
      client.get("/ota/OTA_Usage_Portenta.ino.GIGA.ota");
      int statusCode = client.responseStatusCode();
      client.skipResponseHeaders();
      int bodyLen = client.contentLength();
      FILE* f = fopen(UPDATE_FILE_NAME_LZSS, "w");
      while (client.available()) {
        char _c = client.read();
        fwrite(&_c, 1, 1, f);
      }
      fclose(f);
      return bodyLen;
    }
};

/******************************************************************************
   CONSTANT
 ******************************************************************************/

/******************************************************************************
   SETUP/LOOP
 ******************************************************************************/
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };

void setup()
{
  Serial.begin(115200);
  while (!Serial) {}

  Ethernet.init(10);

  while (Ethernet.linkStatus() == LinkOFF)
  {
    Serial.println("Attempting to connect to the network ...");
    Ethernet.begin(mac);
  }
  Serial.println("Connected");

  Arduino_Portenta_OTA_QSPI_HttpClient ota(QSPI_FLASH_FATFS_MBR, 2);
  Arduino_Portenta_OTA::Error ota_err = Arduino_Portenta_OTA::Error::None;

  if (!ota.isOtaCapable())
  {
    Serial.println("Higher version bootloader required to perform OTA.");
    Serial.println("Please update the bootloader.");
    Serial.println("File -> Examples -> STM32H747_System -> STM32H747_manageBootloader");
    return;
  }

  Serial.println("Initializing OTA storage");
  if ((ota_err = ota.begin()) != Arduino_Portenta_OTA::Error::None)
  {
    Serial.print  ("Arduino_Portenta_OTA::begin() failed with error code ");
    Serial.println((int)ota_err);
    return;
  }

  Serial.println("Starting download to QSPI ...");
  int const ota_download = ota.download(OTA_FILE_LOCATION, false /* is_https */);
  if (ota_download <= 0)
  {
    Serial.print  ("Arduino_Portenta_OTA_QSPI::download failed with error code ");
    Serial.println(ota_download);
    return;
  }
  Serial.print  (ota_download);
  Serial.println(" bytes stored.");

  Serial.println("Decompressing LZSS compressed file ...");
  int const ota_decompress = ota.decompress();
  if (ota_decompress < 0)
  {
    Serial.print("Arduino_Portenta_OTA_QSPI::decompress() failed with error code");
    Serial.println(ota_decompress);
    return;
  }
  Serial.print(ota_decompress);
  Serial.println(" bytes decompressed.");

  Serial.println("Storing parameters for firmware update in bootloader accessible non-volatile memory ...");
  if ((ota_err = ota.update()) != Arduino_Portenta_OTA::Error::None)
  {
    Serial.print  ("ota.update() failed with error code ");
    Serial.println((int)ota_err);
    return;
  }

  Serial.println("Performing a reset after which the bootloader will update the firmware.");
  Serial.println("Hint: Board LED will blink Red-Blue-Green.");
  delay(1000); /* Make sure the serial message gets out before the reset. */
  ota.reset();
}

void loop()
{

}```
IgnacioSJ commented 9 months ago

Hi @facchinm! I tried your code and the error message has changed. Now it's like: Arduino_Portenta_OTA_QSPI::download failed with error code -1 We are trying to do OTA updates using Github repositories which are "https". It seems that Ethernet library is not compatible with "https" URLs (I tried with 443 port). Do you know how to deal with "https" with Ethernet? Some people have recommended me SSLClient library but I don't know how to use it with your sketch. Thank you!