Giga Wifi and QSPIF user space - works

Newbie here... thought I would share how I was finally able to get some QSPI user space without overwriting WiFi firmware on the Giga

  1. used example QSPIFormat.ino from IDE examples STM32H747_System. I used partition scheme #2 that sets up a partition for user data

  2. Used QSPIFReadPartitions.ino example again from STM32H747_System to verify the partitions. My result was:

================================
Partition: 3
Bootable: No
Type: 0x00
================================
Partition: 4
Bootable: No
Type: 0x00

No more partitions are present.

Total Space [KBytes]:         16384
Allocated Space [KBytes]:     1020
Unallocated Space [KBytes]:   15364

YMMV

  1. ran WiFiFirmwareUpdater.ino from STM32H747_System examples to install the Wifi and guess also OTA firmware.

Trying to get a sketch to use the user data partition was pretty tricky and took a few days and bit of luck, but was finally able to get it done with the following sketch. I'm not a programmer and just know the very basics, but using GPT along with Code Copilot trial and error method managed to make it work. Here's the code that should pretty much run standalone with a couple of temperature sensors.

#ifndef FLASHSTORAGE_H
#define FLASHSTORAGE_H

#include <LittleFileSystem.h>
#include <QSPIFBlockDevice.h>
#include <stdint.h>
#include "DebugLogger.h"

// Partition configuration
#define USER_PARTITION_OFFSET (6 * 1024 * 1024) // 6MB offset
#define USER_PARTITION_SIZE   (8 * 1024 * 1024) // 8MB size

bool readFile(const char* path, char* buffer, size_t bufferSize);
bool writeFile(const char* path, const char* data);
bool writeSensorData(const char* sensorName, float value);

// Initialize QSPI block device
static QSPIFBlockDevice qspiDevice;

// Custom block device for user partition
class PartitionBlockDevice : public mbed::BlockDevice {
public:
    PartitionBlockDevice(mbed::BlockDevice* bd, mbed::bd_addr_t offset, mbed::bd_size_t size)
        : _bd(bd), _offset(offset), _size(size) {}

    int init() override { return _bd->init(); }
    int deinit() override { return _bd->deinit(); }

    int read(void* buffer, mbed::bd_addr_t addr, mbed::bd_size_t size) override {
        if (addr + size > _size) return -1;
        return _bd->read(buffer, addr + _offset, size);
    }

    int program(const void* buffer, mbed::bd_addr_t addr, mbed::bd_size_t size) override {
        if (addr + size > _size) return -1;
        return _bd->program(buffer, addr + _offset, size);
    }

    int erase(mbed::bd_addr_t addr, mbed::bd_size_t size) override {
        if (addr + size > _size) return -1;
        return _bd->erase(addr + _offset, size);
    }

    mbed::bd_size_t size() const override { return _size; }
    mbed::bd_size_t get_read_size() const override { return _bd->get_read_size(); }
    mbed::bd_size_t get_program_size() const override { return _bd->get_program_size(); }
    mbed::bd_size_t get_erase_size() const override { return _bd->get_erase_size(); }
    const char* get_type() const override { return _bd->get_type(); }

private:
    mbed::BlockDevice* _bd;
    mbed::bd_addr_t _offset;
    mbed::bd_size_t _size;
};

static PartitionBlockDevice userPartition(&qspiDevice, USER_PARTITION_OFFSET, USER_PARTITION_SIZE);
static mbed::LittleFileSystem userFS("fs");

bool initializeFlashStorage() {
    logProcess("FlashStorage", "Mounting the filesystem...", LOG_INFO);
    int mountError = userFS.mount(&userPartition);
    if (mountError) {
        logProcess("FlashStorage", "Mount failed. Attempting to reformat...", LOG_WARNING);
        int reformatError = userFS.reformat(&userPartition);
        if (reformatError) {
            logProcess("FlashStorage", "Reformatting failed.", LOG_ERROR);
            return false;
        }
    }
    logProcess("FlashStorage", "Flash storage initialized successfully.", LOG_INFO);
    return true;
}

bool readFile(const char* path, char* buffer, size_t bufferSize) {
    FILE* file = fopen(path, "r");
    if (!file) {
        logProcess("FlashStorage", "Failed to open file for reading.", LOG_ERROR);
        return false;
    }
    size_t bytesRead = fread(buffer, 1, bufferSize - 1, file);
    fclose(file);
    buffer[bytesRead] = '\0';
    return true;
}

bool writeFile(const char* path, const char* data) {
    FILE* file = fopen(path, "w");
    if (!file) {
        logProcess("FlashStorage", "Failed to open file for writing.", LOG_ERROR);
        return false;
    }
    fwrite(data, 1, strlen(data), file);
    fclose(file);
    return true;
}

bool writeSensorData(const char* sensorName, float value) {
    String path;
    if (strcmp(sensorName, "BoilerTemp") == 0) {
        path = "/fs/BoilerTemp.txt";
    } else if (strcmp(sensorName, "ReturnTemp") == 0) {
        path = "/fs/ReturnTemp.txt";
    } else if (strcmp(sensorName, "FlueTemp") == 0) {
        path = "/fs/FlueTemp.txt";
    } else {
        logProcess("FlashStorage", "Unknown sensor name.", LOG_ERROR);
        return false;
    }
    char buffer[16];
    snprintf(buffer, sizeof(buffer), "%.2f", value);
    return writeFile(path.c_str(), buffer);
}

#endif // FLASHSTORAGE_H

// Checksum: F12A9

Here's the DebugLogger.h

#ifndef DEBUGLOGGER_H
#define DEBUGLOGGER_H

#include <Arduino.h>

// Log Levels
#define LOG_INFO    1
#define LOG_WARNING 2
#define LOG_ERROR   3
#define LOG_DEBUG   4

void logProcess(const String &tag, const String &message, int level) {
    String logLevel;
    switch (level) {
        case LOG_INFO:
            logLevel = "INFO";
            break;
        case LOG_WARNING:
            logLevel = "WARNING";
            break;
        case LOG_ERROR:
            logLevel = "ERROR";
            break;
        case LOG_DEBUG:
            logLevel = "DEBUG";
            break;
        default:
            logLevel = "UNKNOWN";
            break;
    }

    unsigned long timestamp = millis();
    String logMessage = "[" + String(timestamp) + " ms][" + logLevel + "] " + tag + ": " + message;

    Serial.println(logMessage);
}

#endif // DEBUGLOGGER_H

// Checksum: A5B7E

Results in serial output:

02:39:53.488 -> [905 ms][INFO] HeatingSystem: Initializing Flash Storage...

02:39:53.488 -> [906 ms][INFO] FlashStorage: Mounting the filesystem...

02:39:53.488 -> [923 ms][INFO] FlashStorage: Flash storage initialized successfully.

02:39:53.488 -> [923 ms][INFO] HeatingSystem: Initializing Pin Definitions...

02:39:53.488 -> [924 ms][INFO] PinDefinitions: Pins initialized successfully.

02:39:58.492 -> [5923 ms][INFO] HeatingSystem: Initializing Wi-Fi...

02:39:58.492 -> [5923 ms][INFO] WiFiConnection: Scanning for available networks...

02:40:00.273 -> [7712 ms][INFO] WiFiConnection: Available Networks:

02:40:00.273 -> [7713 ms][INFO] WiFiConnection: [0] SSID: Wireless , RSSI: -36 dBm, Channel: 6

02:40:00.273 -> [7713 ms][INFO] WiFiConnection: [1] SSID: VodafoneBrianVoda, RSSI: -50 dBm, Channel: 2

02:40:00.273 -> [7713 ms][INFO] WiFiConnection: [2] SSID: Wireless , RSSI: -74 dBm, Channel: 6

02:40:00.273 -> [7714 ms][INFO] WiFiConnection: [3] SSID: VPN<->KDH, RSSI: -35 dBm, Channel: 6

02:40:00.273 -> [7714 ms][INFO] WiFiConnection: [4] SSID: Wireless , RSSI: -54 dBm, Channel: 6

02:40:00.273 -> [7714 ms][INFO] WiFiConnection: [5] SSID: VPN<->KDH, RSSI: -54 dBm, Channel: 6

02:40:00.273 -> [7715 ms][INFO] WiFiConnection: [6] SSID: Wireless , RSSI: -76 dBm, Channel: 6

02:40:00.273 -> [7715 ms][INFO] WiFiConnection: [7] SSID: VPN<->KDH, RSSI: -74 dBm, Channel: 6

02:40:00.273 -> [7715 ms][INFO] WiFiConnection: [8] SSID: Starlink, RSSI: -61 dBm, Channel: 1

02:40:04.547 -> [11999 ms][INFO] WiFiConnection: Connected to Wi-Fi!

02:40:04.547 -> [12000 ms][INFO] WiFiConnection: SSID: Wireless

02:40:04.547 -> [12000 ms][INFO] WiFiConnection: IP Address: 10.1.14.80

02:40:04.587 -> [12003 ms][INFO] WiFiConnection: Signal strength (RSSI): -42 dBm

02:40:09.551 -> [17002 ms][INFO] HeatingSystem: Initializing Sensors...

02:40:09.551 -> [17002 ms][INFO] SensorManager: Initializing OneWire sensors...

02:40:09.682 -> [17114 ms][INFO] SensorManager: 2 OneWire sensor(s) detected.

02:40:10.700 -> [18134 ms][INFO] SensorManager: Boiler Temp: 19.88 °C

02:40:10.700 -> [18134 ms][INFO] SensorManager: Return Temp: 19.94 °C

02:40:11.193 -> [18634 ms][INFO] SensorManager: Initializing MAX31855 thermocouple sensor...

02:40:11.389 -> [18834 ms][INFO] SensorManager: Flue Temp: 16.75 °C

02:40:16.398 -> [23834 ms][INFO] HeatingSystem: System initialization complete.

02:40:17.419 -> [24855 ms][DEBUG] SensorManager: Boiler Temp: 19.88 °C

02:40:17.419 -> [24855 ms][DEBUG] SensorManager: Return Temp: 19.88 °C

02:40:17.618 -> [25059 ms][DEBUG] SensorManager: Flue Temp: 17.25 °C

02:40:17.717 -> [25156 ms][INFO] SensorManager: Updating sensor readings to FlashStorage at 25156 ms

If someone out there with more experience and a Giga thniks it might be a good idea and wants to chip in and help better document all this for other noobs like myself out ther still scratching heads would be great. I would be interested if others can replicate and confirm it works for them as well.

Cheers!
Brian

PS to the mods: I looked all over for something similar and could not find it all in one place. Feel free to move this post, modify or delete if redundant. I'm new to the forum here as well :slight_smile: I did use the code button....