The Opta™ is a secure, easy-to-use micro Programmable Logic Controller (PLC) with Industrial Internet of Things (IIoT) capabilities. It can be programmed with the Arduino PLC IDE, a tool that simplifies programming the device through any of the five programming languages defined by the IEC 61131-3 standard.
The Opta™ can also be programmed with other tools from the Arduino ecosystem, such as the Arduino IDE and the Arduino programming language. In case we are using Opta™ with the Arduino PLC IDE and we want to start programming the device using the Arduino IDE and the rest of the Arduino ecosystem tools, we need to partition the memory of the device to enable all of the available features.
In this tutorial, we will learn how to partition the memory of an Opta™ device to enable all of its functionalities to be programmed using the Arduino IDE and the Arduino programming language.
Memory partitioning involves dividing the available memory of a device into separate regions or partitions, each serving a specific purpose. This process is used for managing memory efficiently and ensuring that different programs or functionalities of the device can coexist without interfering each other. In the context of the Opta™, partitioning the memory allows the device to accommodate different functionalities, such as its Wi-Fi® module firmware, Over-The-Air (OTA) updates functionality, and certificates storage, while still providing memory space for user programs developed in the Arduino IDE and the Arduino programming language.
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.
This tutorial requires the latest version of the Arduino IDE; we can download it here. In the Arduino IDE, we need to install the core for Opta™ devices; this can be done by navigating to Tools > Board > Boards Manager or clicking 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.
Now we are ready to start compiling and uploading Arduino sketches to an Opta™ device using the Arduino IDE.
The sketch below will guide us through partitioning the memory of an Opta™ device. The complete sketch and the certificates file can be downloaded here. Remember to store both files in the same folder.
1/**2 Opta memory partitioning3 Name: opta_memory_partitioning.ino4 Purpose: Partition the memory of an Opta device5
6 @author Arduino PRO team7 @version 1.0 04/04/228*/9
10// Include necessary libraries for working11#include <BlockDevice.h>12#include <FATFileSystem.h>13#include <LittleFileSystem.h>14#include <MBRBlockDevice.h>15#include "wiced_resource.h"16#include "certificates.h"17
18// Ensure that the M7 core is being used instead of the M4 core19#ifndef CORE_CM720#error Update the WiFi firmware by uploading the sketch to the M7 core instead of the M4 core.21#endif22
23using namespace mbed;24
25// Create instances of block devices and filesystems for the QSPI Flash memory26BlockDevice* root;27MBRBlockDevice* wifi_data;28MBRBlockDevice* ota_data;29FATFileSystem wifi_data_fs("wlan");30FATFileSystem ota_data_fs("fs");31
32void setup() {33 // Set the built-in LED pin as an output and turn it off34 pinMode(LED_BUILTIN, OUTPUT);35 digitalWrite(LED_BUILTIN, LOW);36
37 // Initialize serial communication and wait up to 2.5 seconds for a connection38 Serial.begin(115200);39 for (auto startNow = millis() + 2500; !Serial && millis() < startNow; delay(500))40 ;41
42 // Blink the built-in LED 10 times as a visual indicator that the process is starting43 for (auto i = 0u; i < 10; i++) {44 digitalWrite(LED_BUILTIN, HIGH);45 delay(25);46 digitalWrite(LED_BUILTIN, LOW);47 delay(50);48 }49
50 // Initialize and erase the QSPI flash memory.51 Serial.println("Erasing the QSPIF");52 root = BlockDevice::get_default_instance();53 auto err = root->init();54 if (err != 0) {55 Serial.print("Error Initializing the QSPIF: ");56 Serial.println(err);57 while (true) {58 digitalWrite(LED_BUILTIN, HIGH);59 delay(50);60 digitalWrite(LED_BUILTIN, LOW);61 delay(150);62 }63 }64
65 // Create partitions for Wi-Fi firmware, OTA updates, and certificate storage66 // Get device geometry.67 const auto erase_size = root->get_erase_size();68 const auto size = root->size();69 const auto eraseSectors = size / erase_size;70
71 for (auto i = 0u; i < eraseSectors; i++) {72 err = root->erase(i * erase_size, erase_size);73 if (i % 64 == 0) {74 digitalWrite(LED_BUILTIN, HIGH);75 delay(25);76 digitalWrite(LED_BUILTIN, LOW);77 }78 if (err != 0) {79 Serial.print("Error erasing sector ");80 Serial.println(i);81 Serial.print(" [");82 Serial.print(i * erase_size);83 Serial.print(" - ");84 Serial.print(float{ i } / float{ eraseSectors } * 100);85 Serial.print("%] -> ");86 Serial.print(err ? "KO" : "OK");87 Serial.println();88 for (auto i = 0u; i < 2; i++) {89 digitalWrite(LED_BUILTIN, HIGH);90 delay(50);91 digitalWrite(LED_BUILTIN, LOW);92 delay(150);93 }94 }95 }96
97 Serial.println("Done");98 for (auto i = 0u; i < 5; i++) {99 digitalWrite(LED_BUILTIN, HIGH);100 delay(25);101 digitalWrite(LED_BUILTIN, LOW);102 delay(50);103 }104
105 // Format the partitions and create filesystem instances106 // WiFi Firmware and TLS TA certificates: 1 MB107 // Arduino OTA: 13 MB108 MBRBlockDevice::partition(root, 1, 0x0B, 0 * 1024 * 1024, 1 * 1024 * 1024);109 MBRBlockDevice::partition(root, 3, 0x0B, 14 * 1024 * 1024, 14 * 1024 * 1024);110 MBRBlockDevice::partition(root, 2, 0x0B, 1024 * 1024, 14 * 1024 * 1024);111
112 // Create the filesystem references113 wifi_data = new MBRBlockDevice(root, 1);114 ota_data = new MBRBlockDevice(root, 2);115
116 // Write Wi-Fi firmware and certificate data to the appropriate partitions117 Serial.print("Formatting WiFi partition... ");118 err = wifi_data_fs.reformat(wifi_data);119 if (err != 0) {120 Serial.println("Error formatting WiFi partition");121 while (true) {122 digitalWrite(LED_BUILTIN, HIGH);123 delay(50);124 digitalWrite(LED_BUILTIN, LOW);125 delay(150);126 }127 }128
129 Serial.println("done.");130 Serial.print("Formatting OTA partition...");131 err = ota_data_fs.reformat(ota_data);132 if (err != 0) {133 Serial.println("Error formatting OTA partition");134 while (true) {135 digitalWrite(LED_BUILTIN, HIGH);136 delay(50);137 digitalWrite(LED_BUILTIN, LOW);138 delay(150);139 }140 }141
142 Serial.println("done.");143 for (auto i = 0u; i < 10; i++) {144 digitalWrite(LED_BUILTIN, HIGH);145 delay(25);146 digitalWrite(LED_BUILTIN, LOW);147 delay(50);148 }149
150 Serial.println("QSPI Flash Storage Ready.");151
152 // Flash the memory-mapped Wi-Fi firmware and certificates153 extern const unsigned char wifi_firmware_image_data[];154 extern const resource_hnd_t wifi_firmware_image;155 FILE* fp = fopen("/wlan/4343WA1.BIN", "wb");156 const int file_size = 421098;157 int chunck_size = 1024;158 int byte_count = 0;159
160 Serial.println("Flashing /wlan/4343WA1.BIN file");161 printProgress(byte_count, file_size, 10, true);162 while (byte_count < file_size) {163 if (byte_count + chunck_size > file_size)164 chunck_size = file_size - byte_count;165 int ret = fwrite(&wifi_firmware_image_data[byte_count], chunck_size, 1, fp);166 if (ret != 1) {167 Serial.println("Error writing firmware data");168 break;169 }170 byte_count += chunck_size;171 printProgress(byte_count, file_size, 10, false);172 }173 fclose(fp);174
175 chunck_size = 1024;176 byte_count = 0;177 const uint32_t offset = 15 * 1024 * 1024 + 1024 * 512;178
179 Serial.println("Flashing memory mapped firmware");180 printProgress(byte_count, file_size, 10, true);181 while (byte_count < file_size) {182 if (byte_count + chunck_size > file_size)183 chunck_size = file_size - byte_count;184 int ret = root->program(wifi_firmware_image_data, offset + byte_count, chunck_size);185 if (ret != 0) {186 Serial.println("Error writing firmware data");187 break;188 }189 byte_count += chunck_size;190 printProgress(byte_count, file_size, 10, false);191 }192
193 chunck_size = 128;194 byte_count = 0;195 fp = fopen("/wlan/cacert.pem", "wb");196
197 Serial.println("Flashing certificates");198 printProgress(byte_count, cacert_pem_len, 10, true);199 while (byte_count < cacert_pem_len) {200 if (byte_count + chunck_size > cacert_pem_len)201 chunck_size = cacert_pem_len - byte_count;202 int ret = fwrite(&cacert_pem[byte_count], chunck_size, 1, fp);203 if (ret != 1) {204 Serial.println("Error writing certificates");205 break;206 }207 byte_count += chunck_size;208 printProgress(byte_count, cacert_pem_len, 10, false);209 }210 fclose(fp);211
212 fp = fopen("/wlan/cacert.pem", "rb");213 char buffer[128];214 int ret = fread(buffer, 1, 128, fp);215 Serial.write(buffer, ret);216 while (ret == 128) {217 ret = fread(buffer, 1, 128, fp);218 Serial.write(buffer, ret);219 }220 fclose(fp);221
222 Serial.println("\nFirmware and certificates updated!");223 Serial.println("It's now safe to reboot or disconnect your board.");224}225
226void loop() {227 // Empty loop function, main task is performed in the setup function228}229
230/**231 Get the size of a file232 233 @param bootloader fp (FP)234 @return files size235*/236long getFileSize(FILE* fp) {237 fseek(fp, 0, SEEK_END);238 int size = ftell(fp);239 fseek(fp, 0, SEEK_SET);240
241 return size;242}243
244/**245 Display the progress of the flashing process246 247 @params offset (uint32_t), size (uint32_t), threshold (uint32_t) and reset (bool)248 @return none249*/250void printProgress(uint32_t offset, uint32_t size, uint32_t threshold, bool reset) {251 static int percent_done = 0;252 if (reset == true) {253 percent_done = 0;254 Serial.println("Flashed " + String(percent_done) + "%");255 } else {256 uint32_t percent_done_new = offset * 100 / size;257 if (percent_done_new >= percent_done + threshold) {258 percent_done = percent_done_new;259 Serial.println("Flashed " + String(percent_done) + "%");260 }261 }262}
The sketch shown above performs four main tasks:
Initialize and erase the QSPI Flash memory: The sketch initializes the QSPI Flash memory of the Opta™ device and erases its content to prepare the memory for new firmware and data. One of the built-in LEDs of the device is used to indicate the progress of the memory-erasing process.
Create partitions and format them in the QSPI Flash memory: The sketch creates and formats partitions in the QSPI Flash memory for the Wi-Fi firmware, Over-The-Air (OTA) updates functionality, and certificates storage.
Write Wi-Fi firmware and certificates data: The sketch writes the Wi-Fi firmware and certificate data to the appropriate partitions in the QSPI Flash memory. It also flashes the memory-mapped Wi-Fi firmware and certificates.
Display progress in the Arduino IDE Serial Monitor: The sketch provides a visual indication of the progress of the flashing process using one of the built-in LEDs of the Opta™ device and displays messages through the Arduino IDE Serial Monitor to inform the user about the current status of the flashing process.
To upload the code, click the Verify button to compile the sketch and check for errors; then click the Upload button to program the device with the sketch.
After a while, you should see in the Serial Monitor information on the progress of the flashing process, as shown in the image below.
If everything is correct, 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.
In this tutorial, we successfully partitioned 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.
As you move forward, you can explore the extensive Arduino ecosystem, leverage various libraries and hardware add-ons, and create powerful, connected, and secure industrial solutions using the Opta™. Check out our getting started with the Opta™ tutorial to learn more about its hardware and software features.