Skip to content

Commit 1b6f6d6

Browse files
committed
Content update post-revision
1 parent 9f414f5 commit 1b6f6d6

File tree

2 files changed

+293
-13
lines changed

2 files changed

+293
-13
lines changed

content/hardware/07.opta/opta-family/opta/tutorials/08.memory-partitioning/content.md

Lines changed: 293 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,20 @@ hardware:
1818

1919
## Overview
2020

21-
The **Opta™** is a secure, easy-to-use micro Programmable Logic Controller (PLC) with Industrial Internet of Things (IIoT) capabilities. The **Portenta Machine Control** is a centralized, energy-efficient industrial control unit capable of operating equipment and machinery. Both devices can be programmed with the [Arduino PLC IDE](https://www.arduino.cc/pro/software-plc-ide/), a tool that simplifies programming the device through any of the five programming languages defined by the IEC 61131-3 standard.
21+
The **Opta™** is a secure, easy-to-use micro Programmable Logic Controller (PLC) with Industrial Internet of Things (IIoT) capabilities. The **Portenta Machine Control** is a centralized, energy-efficient industrial control unit capable of operating equipment and machinery.
22+
23+
Both devices can be programmed with the [Arduino PLC IDE](https://www.arduino.cc/pro/software-plc-ide/), a tool that simplifies programming the device through any of the five programming languages defined by the IEC 61131-3 standard.
2224

2325
The Opta™ and the Portenta Machine Control can also be programmed with other tools from the Arduino ecosystem, such as the [Arduino IDE](https://www.arduino.cc/en/software) and the Arduino programming language. Suppose we are using Opta™ with the Arduino PLC IDE and want to start programming the device using the Arduino IDE and the rest of the Arduino ecosystem tools. In that case, we need to partition the device's memory to enable all available features. This applies to the Portenta Machine Control as well.
2426

25-
In this tutorial, we will use an Opta™ and learn how to partition the memory to enable all its functionalities to be programmed using the Arduino IDE and the Arduino programming language.
27+
![The Memory Map for Opta™ & Portenta Machine Control](assets/memory-partitioning-introduction.png)
2628

27-
![The Opta™ memory map](assets/memory-partitioning-introduction.png)
29+
In this tutorial, we will use an Opta™ and learn how to partition the memory to enable all its functionalities to be programmed using the Arduino IDE and the Arduino programming language.
2830

2931
## Goals
3032

31-
- Learn how to partition the memory of an Opta™ device to be used with the Arduino IDE and other ecosystem tools
32-
- Program an Opta™ device using the Arduino IDE and the Arduino programming language.
33+
- Learn how to partition the memory of an Opta™ device and a Portenta Machine Control to be used with the Arduino IDE and other ecosystem tools
34+
- Program an Opta™ device and a Portenta Machine Control using the Arduino IDE and the Arduino programming language.
3335

3436
## Hardware and Software Requirements
3537

@@ -52,21 +54,21 @@ In the context of the Opta™, partitioning the memory allows the device to acco
5254

5355
Now, we will guide the process of partitioning an Opta™'s memory to enable the full range of its functionalities to be programmed using the Arduino IDE and the Arduino programming language.
5456

55-
## Instructions
57+
## Setting Up the Arduino IDE
5658

57-
### Setting Up the Arduino IDE
59+
This tutorial requires the latest version of the Arduino IDE, which can be downloaded [here](https://www.arduino.cc/en/software).
5860

59-
This tutorial requires the latest version of the Arduino IDE, which can be downloaded [here](https://www.arduino.cc/en/software). In the Arduino IDE, you need to install the core for Opta™ devices. To do this, go to **Tools > Board > Boards Manager** or click the **Boards Manager** icon in the left tab of the IDE. In the **Boards Manager** tab, search for `opta` and install the latest `Arduino Mbed OS Opta Boards` version.
61+
In the Arduino IDE, you need to install the core for *Opta™* devices. To do this, go to **Tools > Board > Boards Manager** or click the **Boards Manager** icon in the left tab of the IDE. In the **Boards Manager** tab, search for `opta` and install the latest `Arduino Mbed OS Opta Boards` version.
6062

6163
![Installing the Opta™ core in the Arduino IDE bootloader](assets/arduino-ide-1.png)
6264

63-
***For the Portenta Machine Control, in the __Boards Manager__ tab, search for `portenta` and install the latest `Arduino Mbed OS Portenta Boards` core version.***
65+
For the *Portenta Machine Control*, in the __Boards Manager__ tab, search for `portenta` and install the latest `Arduino Mbed OS Portenta Boards` core version.
6466

6567
We are now ready to compile and upload Arduino sketches to an Opta™ device using the Arduino IDE.
6668

6769
***Memory partitioning of an Opta™ or a Portenta Machine Control should use the latest core and libraries to ensure the system is up-to-date with the intended default configuration.***
6870

69-
### Partitioning Memory of an Opta™ Device
71+
## Partitioning Memory of an Opta™ Device
7072

7173
To partition the memory of an Opta™ device, you need an Arduino sketch that includes memory partition operations and a certificate file. These files can be downloaded directly from the [Software Requirements section](#software-requirements) or [here](assets/memory_partitioning.zip). Ensure both files are stored in the same directory, as shown in the following layout:
7274

@@ -358,13 +360,291 @@ After a while, you should see information on the progress of the flashing proces
358360

359361
If everything went as intended, you should see a success message in the Serial Monitor. Now, we are ready to start using the full capabilities of the Opta™ with the Arduino IDE.
360362

361-
**The process is similar for the Portenta Machine Control.** Once the same memory partition code uploads and finishes its operation, it should show similar results in the Arduino IDE's Serial Monitor as the Opta™. It will be ready to start using the full functionalities of the Portenta Machine Control with the Arduino IDE.
363+
## Partitioning Memory of a Portenta Machine Control
364+
365+
**The memory partitioning process for the Portenta Machine Control follows the same procedure as the Opta™ device.** You will need the latest `Arduino Mbed OS Portenta Boards` core version. If you do not have the latest version, please refer to the [Setting Up the Arduino IDE](#setting-up-the-arduino-ide) section before proceeding.
366+
367+
You will need an Arduino sketch with memory partition operations and a certificate file. These files can be downloaded from the [Software Requirements section](#software-requirements) or directly [here](assets/memory_partitioning.zip). Ensure both files are stored in the same directory, as shown below:
368+
369+
```
370+
memory_partitioning
371+
├── certificates.h
372+
└── memory_partitioning.ino
373+
```
374+
375+
***The complete sketch and the certificate files can be downloaded [__here__](assets/memory_partitioning.zip). __Please store both files in the same directory__.***
376+
377+
Compile and upload the following code to partition the memory of a Portenta Machine Control:
378+
379+
```arduino
380+
// Include necessary libraries for working
381+
#include <BlockDevice.h>
382+
#include <FATFileSystem.h>
383+
#include <LittleFileSystem.h>
384+
#include <MBRBlockDevice.h>
385+
#include "wiced_resource.h"
386+
#include "certificates.h"
387+
388+
// Ensure that the M7 core is being used instead of the M4 core
389+
#ifndef CORE_CM7
390+
#error Update the WiFi firmware by uploading the sketch to the M7 core instead of the M4 core.
391+
#endif
392+
393+
using namespace mbed;
394+
395+
// Create instances of block devices and filesystems for the QSPI Flash memory
396+
BlockDevice* root;
397+
MBRBlockDevice* wifi_data;
398+
MBRBlockDevice* ota_data;
399+
FATFileSystem wifi_data_fs("wlan");
400+
FATFileSystem ota_data_fs("fs");
401+
402+
void setup() {
403+
// Set the built-in LED pin as an output and turn it off
404+
pinMode(LED_BUILTIN, OUTPUT);
405+
digitalWrite(LED_BUILTIN, LOW);
406+
407+
// Initialize serial communication and wait up to 2.5 seconds for a connection
408+
Serial.begin(115200);
409+
for (auto startNow = millis() + 2500; !Serial && millis() < startNow; delay(500))
410+
;
411+
412+
// Blink the built-in LED 10 times as a visual indicator that the process is starting
413+
for (auto i = 0u; i < 10; i++) {
414+
digitalWrite(LED_BUILTIN, HIGH);
415+
delay(25);
416+
digitalWrite(LED_BUILTIN, LOW);
417+
delay(50);
418+
}
419+
420+
// Initialize and erase the QSPI flash memory.
421+
Serial.println("Erasing the QSPIF");
422+
root = BlockDevice::get_default_instance();
423+
auto err = root->init();
424+
if (err != 0) {
425+
Serial.print("Error Initializing the QSPIF: ");
426+
Serial.println(err);
427+
while (true) {
428+
digitalWrite(LED_BUILTIN, HIGH);
429+
delay(50);
430+
digitalWrite(LED_BUILTIN, LOW);
431+
delay(150);
432+
}
433+
}
434+
435+
// Create partitions for Wi-Fi firmware, OTA updates, and certificate storage
436+
// Get device geometry.
437+
const auto erase_size = root->get_erase_size();
438+
const auto size = root->size();
439+
const auto eraseSectors = size / erase_size;
440+
441+
for (auto i = 0u; i < eraseSectors; i++) {
442+
err = root->erase(i * erase_size, erase_size);
443+
if (i % 64 == 0) {
444+
digitalWrite(LED_BUILTIN, HIGH);
445+
delay(25);
446+
digitalWrite(LED_BUILTIN, LOW);
447+
}
448+
if (err != 0) {
449+
Serial.print("Error erasing sector ");
450+
Serial.println(i);
451+
Serial.print(" [");
452+
Serial.print(i * erase_size);
453+
Serial.print(" - ");
454+
Serial.print(float{ i } / float{ eraseSectors } * 100);
455+
Serial.print("%] -> ");
456+
Serial.print(err ? "KO" : "OK");
457+
Serial.println();
458+
for (auto i = 0u; i < 2; i++) {
459+
digitalWrite(LED_BUILTIN, HIGH);
460+
delay(50);
461+
digitalWrite(LED_BUILTIN, LOW);
462+
delay(150);
463+
}
464+
}
465+
}
466+
467+
Serial.println("Done");
468+
for (auto i = 0u; i < 5; i++) {
469+
digitalWrite(LED_BUILTIN, HIGH);
470+
delay(25);
471+
digitalWrite(LED_BUILTIN, LOW);
472+
delay(50);
473+
}
474+
475+
// Format the partitions and create filesystem instances
476+
// WiFi Firmware and TLS TA certificates: 1 MB
477+
// Arduino OTA: 13 MB
478+
MBRBlockDevice::partition(root, 1, 0x0B, 0 * 1024 * 1024, 1 * 1024 * 1024);
479+
MBRBlockDevice::partition(root, 3, 0x0B, 14 * 1024 * 1024, 14 * 1024 * 1024);
480+
MBRBlockDevice::partition(root, 2, 0x0B, 1024 * 1024, 14 * 1024 * 1024);
481+
482+
// Create the filesystem references
483+
wifi_data = new MBRBlockDevice(root, 1);
484+
ota_data = new MBRBlockDevice(root, 2);
485+
486+
// Write Wi-Fi firmware and certificate data to the appropriate partitions
487+
Serial.print("Formatting WiFi partition... ");
488+
err = wifi_data_fs.reformat(wifi_data);
489+
if (err != 0) {
490+
Serial.println("Error formatting WiFi partition");
491+
while (true) {
492+
digitalWrite(LED_BUILTIN, HIGH);
493+
delay(50);
494+
digitalWrite(LED_BUILTIN, LOW);
495+
delay(150);
496+
}
497+
}
498+
499+
Serial.println("done.");
500+
Serial.print("Formatting OTA partition...");
501+
err = ota_data_fs.reformat(ota_data);
502+
if (err != 0) {
503+
Serial.println("Error formatting OTA partition");
504+
while (true) {
505+
digitalWrite(LED_BUILTIN, HIGH);
506+
delay(50);
507+
digitalWrite(LED_BUILTIN, LOW);
508+
delay(150);
509+
}
510+
}
511+
512+
Serial.println("done.");
513+
for (auto i = 0u; i < 10; i++) {
514+
digitalWrite(LED_BUILTIN, HIGH);
515+
delay(25);
516+
digitalWrite(LED_BUILTIN, LOW);
517+
delay(50);
518+
}
519+
520+
Serial.println("QSPI Flash Storage Ready.");
521+
522+
// Flash the memory-mapped Wi-Fi firmware and certificates
523+
extern const unsigned char wifi_firmware_image_data[];
524+
extern const resource_hnd_t wifi_firmware_image;
525+
FILE* fp = fopen("/wlan/4343WA1.BIN", "wb");
526+
const int file_size = 421098;
527+
int chunck_size = 1024;
528+
int byte_count = 0;
529+
530+
Serial.println("Flashing /wlan/4343WA1.BIN file");
531+
printProgress(byte_count, file_size, 10, true);
532+
while (byte_count < file_size) {
533+
if (byte_count + chunck_size > file_size)
534+
chunck_size = file_size - byte_count;
535+
int ret = fwrite(&wifi_firmware_image_data[byte_count], chunck_size, 1, fp);
536+
if (ret != 1) {
537+
Serial.println("Error writing firmware data");
538+
break;
539+
}
540+
byte_count += chunck_size;
541+
printProgress(byte_count, file_size, 10, false);
542+
}
543+
fclose(fp);
544+
545+
chunck_size = 1024;
546+
byte_count = 0;
547+
const uint32_t offset = 15 * 1024 * 1024 + 1024 * 512;
548+
549+
Serial.println("Flashing memory mapped firmware");
550+
printProgress(byte_count, file_size, 10, true);
551+
while (byte_count < file_size) {
552+
if (byte_count + chunck_size > file_size)
553+
chunck_size = file_size - byte_count;
554+
int ret = root->program(wifi_firmware_image_data, offset + byte_count, chunck_size);
555+
if (ret != 0) {
556+
Serial.println("Error writing firmware data");
557+
break;
558+
}
559+
byte_count += chunck_size;
560+
printProgress(byte_count, file_size, 10, false);
561+
}
562+
563+
chunck_size = 128;
564+
byte_count = 0;
565+
fp = fopen("/wlan/cacert.pem", "wb");
566+
567+
Serial.println("Flashing certificates");
568+
printProgress(byte_count, cacert_pem_len, 10, true);
569+
while (byte_count < cacert_pem_len) {
570+
if (byte_count + chunck_size > cacert_pem_len)
571+
chunck_size = cacert_pem_len - byte_count;
572+
int ret = fwrite(&cacert_pem[byte_count], chunck_size, 1, fp);
573+
if (ret != 1) {
574+
Serial.println("Error writing certificates");
575+
break;
576+
}
577+
byte_count += chunck_size;
578+
printProgress(byte_count, cacert_pem_len, 10, false);
579+
}
580+
fclose(fp);
581+
582+
fp = fopen("/wlan/cacert.pem", "rb");
583+
char buffer[128];
584+
int ret = fread(buffer, 1, 128, fp);
585+
Serial.write(buffer, ret);
586+
while (ret == 128) {
587+
ret = fread(buffer, 1, 128, fp);
588+
Serial.write(buffer, ret);
589+
}
590+
fclose(fp);
591+
592+
Serial.println("\nFirmware and certificates updated!");
593+
Serial.println("It's now safe to reboot or disconnect your board.");
594+
}
595+
596+
void loop() {
597+
// Empty loop function, main task is performed in the setup function
598+
}
599+
600+
/**
601+
Get the size of a file
602+
603+
@param bootloader fp (FP)
604+
@return files size
605+
*/
606+
long getFileSize(FILE* fp) {
607+
fseek(fp, 0, SEEK_END);
608+
int size = ftell(fp);
609+
fseek(fp, 0, SEEK_SET);
610+
611+
return size;
612+
}
613+
614+
/**
615+
Display the progress of the flashing process
616+
617+
@params offset (uint32_t), size (uint32_t), threshold (uint32_t) and reset (bool)
618+
@return none
619+
*/
620+
void printProgress(uint32_t offset, uint32_t size, uint32_t threshold, bool reset) {
621+
static int percent_done = 0;
622+
if (reset == true) {
623+
percent_done = 0;
624+
Serial.println("Flashed " + String(percent_done) + "%");
625+
} else {
626+
uint32_t percent_done_new = offset * 100 / size;
627+
if (percent_done_new >= percent_done + threshold) {
628+
percent_done = percent_done_new;
629+
Serial.println("Flashed " + String(percent_done) + "%");
630+
}
631+
}
632+
}
633+
```
634+
635+
***If you encounter an error during the compilation process, **please ensure both the script and certificate files are in the same folder**. The certificate file is crucial for the memory partitioning process. The complete sketch and the certificate files can be downloaded [here](assets/memory_partitioning.zip).***
636+
637+
Once the memory partition code uploads, wait for it to complete its process. It should display similar results in the Arduino IDE's Serial Monitor as the Opta™.
638+
639+
![Flashing process feedback of the Opta™ shown in the Arduino IDE's Serial Monitor](assets/arduino-ide-3.png)
640+
641+
You will then be ready to use the full functionalities of the Portenta Machine Control with the Arduino IDE.
362642

363643
## Conclusion
364644

365-
In this tutorial, we showed how to partition the memory of an Opta™ device, enabling its full range of functionalities to be programmed using Arduino ecosystem tools such as the Arduino IDE and the Arduino programming language. We walked through initializing and erasing the QSPI Flash memory, partitioning and formatting the memory, and writing the Wi-Fi firmware and certificate data onto the device's memory.
645+
In this tutorial, we showed how to partition the memory of an Opta™ device and a Portenta Machine Control, enabling its full range of functionalities to be programmed using Arduino ecosystem tools such as the Arduino IDE and the Arduino programming language.
366646

367-
We also showed that the same process can be applied to the Portenta Machine Control to achieve similar results.
647+
We walked through initializing and erasing the QSPI Flash memory, partitioning and formatting the memory, and writing the Wi-Fi firmware and certificate data onto the device's memory.
368648

369649
## Next Steps
370650

0 commit comments

Comments
 (0)