Trying to setup a shared directory with files to be included in other projects

… and pulling my hair out. In fact, I spent all day on this :frowning:

My projects are in /home/maxg/Workspaces/PlatformIO/Projects. My current project is in HotWaterTermperatures. I have a a shared directory for files I want to include in other projects, as below.

# [2025-04-21 19:33] maxg@x570 ~/Workspaces/PlatformIO/Projects/Arduino_Shared $ 
ls -la -R /home/maxg/Workspaces/PlatformIO/Projects/Arduino_Shared
/home/maxg/Workspaces/PlatformIO/Projects/Arduino_Shared:
total 20
drwxrwxr-x   4 maxg maxg 4096 Apr 21 19:07 .
drwxrwxrwx 110 maxg maxg 4096 Apr 20 19:20 ..
drwxrwxr-x   2 maxg maxg 4096 Apr 21 19:09 include
-rw-rw-r--   1 maxg maxg  297 Apr 21 19:12 library.json
drwxrwxr-x   2 maxg maxg 4096 Apr 21 19:09 src

/home/maxg/Workspaces/PlatformIO/Projects/Arduino_Shared/include:
total 16
drwxrwxr-x 2 maxg maxg 4096 Apr 21 19:09 .
drwxrwxr-x 4 maxg maxg 4096 Apr 21 19:07 ..
-rw-rw-r-- 1 maxg maxg  310 Apr 19 22:56 debug_utils.h
-rw-rw-r-- 1 maxg maxg  569 Apr 21 18:50 mqtt_utils.h

/home/maxg/Workspaces/PlatformIO/Projects/Arduino_Shared/src:
total 16
drwxrwxr-x 2 maxg maxg 4096 Apr 21 19:09 .
drwxrwxr-x 4 maxg maxg 4096 Apr 21 19:07 ..
-rw-rw-r-- 1 maxg maxg 4251 Apr 21 18:50 mqtt_utils.cpp

The files in Arduino_Shared are currently within another 70 odd projects. I’d like to take these out of /src/main.cpp; and then maintain one copy for all of them in ArduinoShared.

I have tried this a couple of years ago and gave up. While I can hack some code, I only push the button to compile, and am new to header files as well. So attention may need to be focused on my include directives, the use of <> and β€œβ€ and more importantly the platformio.ini. I started out without putting the full path in; e.g., …/…/Arduino_Shared. It seems to make no difference.
Any hints appreciated. Thanks.

It seem I cannot attach files, so here their contents; also to allow someone keen to replicate the project.

debug_utils.h

#ifndef DEBUG_UTILS_H
#define DEBUG_UTILS_H

#include <Arduino.h>

#ifdef DEBUG
#define debug(x, ...) do { Serial.print(x, ##__VA_ARGS__); } while (0)
#define debugln(x, ...) do { Serial.println(x, ##__VA_ARGS__); } while (0)
#else
#define debug(x, ...)
#define debugln(x, ...)
#endif

#endif // DEBUG_UTILS_H

mqtt_utils.cpp

#include <mqtt_utils.h>

// External dependencies
extern EthernetClient ethernet_client;
extern PubSubClient mqtt_client;
extern bool b_is_mqtt_connected_to_broker();


void publish_mac_address()
{
    char mac_string[18] {0};

    snprintf(mac_string, sizeof(mac_string), "%02X:%02X:%02X:%02X:%02X:%02X",
             MAC_ADDRESS[0], MAC_ADDRESS[1], MAC_ADDRESS[2],
             MAC_ADDRESS[3], MAC_ADDRESS[4], MAC_ADDRESS[5]);

    mac_string[17] = '\0';
    publish_message(MQTT_PUB_MAC, mac_string);
}


void publish_ip_address()
{
    char ip_address[16] {0};
    IPAddress ip = (uint32_t)Ethernet.localIP();

    snprintf(ip_address, sizeof(ip_address), "%u.%u.%u.%u",
             ip[0], ip[1], ip[2], ip[3]);

    publish_message(MQTT_PUB_IP, ip_address);
}


void maintain_dhcp_connection()
{
    static const uint32_t DHCP_MAINTAIN_TIMER {600000};
    static uint32_t last_time_dhcp_maintained {0};
    uint8_t dhcp_return_code {1};

    if ((millis() - last_time_dhcp_maintained) > DHCP_MAINTAIN_TIMER)
    {
        last_time_dhcp_maintained = millis();
        dhcp_return_code = Ethernet.maintain();

        switch (dhcp_return_code)
        {
            case 0:
                publish_error(INFO, (char *)"DHCP OK");
                break;
            case 1:
                publish_error(ERROR, (char *)"DHCP renew failed");
                break;
            case 2:
                publish_error(INFO, (char *)"DHCP renew OK");
                break;
            case 3:
                publish_error(ERROR, (char *)"DHCP rebind failed");
                break;
            case 4:
                publish_error(INFO, (char *)"DHCP rebind OK");
                break;
            default:
                break;
        }
    }
}


void publish_message(const char *topic, const char *msg)
{
    if (false == b_is_mqtt_connected_to_broker())
    {
        debugln(F("MQTT: client not connected"));
    }

    const uint8_t BUF_SIZE_TOPIC {80};
    char topic_string[BUF_SIZE_TOPIC] {0};

    topic_string[0] = '\0';
    strncat(topic_string, MQTT_PUB_TOPIC_BASE, BUF_SIZE_TOPIC - strlen(topic_string) - 1);
    strncat(topic_string, topic, BUF_SIZE_TOPIC - strlen(topic_string) - 1);

    mqtt_client.publish(topic_string, msg);
}


void publish_error(uint8_t errorType, char *errorMessage)
{
    const uint8_t BUF_SIZE {80};
    char buf[BUF_SIZE] {0};
    buf[0] = '\0';

    strncpy(buf, MQTT_CONNECT_ID, BUF_SIZE - strlen(buf) - 1);

    switch (errorType)
    {
        case 0:
            strncat(buf, "|INFO|", BUF_SIZE - strlen(buf) - 1);
            break;
        case 1:
            strncat(buf, "|WARN|", BUF_SIZE - strlen(buf) - 1);
            break;
        case 2:
            strncat(buf, "|ERROR|", BUF_SIZE - strlen(buf) - 1);
            break;
        case 3:
            strncat(buf, "|DEBUG|", BUF_SIZE - strlen(buf) - 1);
            break;
        default:
            break;
    }

    strncat(buf, errorMessage, BUF_SIZE - strlen(buf) - 1);
    publish_message(MQTT_PUB_NOTIFICATION, buf);
}


void send_post_boot_data()
{
    const uint8_t MQTT_PUBLISH_DELAY {200};
    delay(MQTT_PUBLISH_DELAY);

    publish_mac_address();
    delay(MQTT_PUBLISH_DELAY);

    publish_ip_address();
    delay(MQTT_PUBLISH_DELAY);

    publish_message(MQTT_PUB_VERSION, CODE_VERSION);
    delay(MQTT_PUBLISH_DELAY);
}


void reconnect_mqtt_client()
{
    const uint32_t MQTT_RECONNECT_INTERVAL {10000};
    static uint32_t last_mqtt_reconnect_attempt = 0;
    static uint16_t reconnect_attempts = 0;

    if (millis() - last_mqtt_reconnect_attempt > MQTT_RECONNECT_INTERVAL)
    {
        last_mqtt_reconnect_attempt = millis();
        reconnect_attempts++;
        debugln(F("MQTT: attempting to reconnect to broker ..."));
        mqtt_client.connect(MQTT_CONNECT_ID);

        if (true == b_is_mqtt_connected_to_broker())
        {
            reconnect_attempts = 0;
            debugln(F("... success."));
        }
        else
        {
            debugln(F("... failed."));
            if (reconnect_attempts % 10 == 0)
            {
                debugln(F("Warning: Prolonged MQTT disconnect"));
                publish_error(WARN, (char *)F("Prolonged MQTT disconnect"));
            }
        }
    }
}

mqtt_utils.h

#ifndef MQTT_UTILS_H
#define MQTT_UTILS_H

#include <Arduino.h>
#include <Ethernet.h>
#include <PubSubClient.h>
#include <debug_utils.h>
#include "config.h"

// Log message level enumeration
enum LogMsgLevel
{
    INFO,
    WARN,
    ERROR,
    DEBUG
};

// Function prototypes
void publish_mac_address();
void publish_ip_address();
void maintain_dhcp_connection();
void publish_message(const char *topic, const char *message);
void publish_error(uint8_t errorType, char *errorMessage);
void send_post_boot_data();
void reconnect_mqtt_client();

#endif // MQTT_UTILS_H

library.json

{
    "name": "ArduinoShared",
    "version": "1.0.0",
    "description": "Shared utilities for Arduino projects",
    "keywords": "mqtt, debug, utilities",
    "authors": [
        {
            "name": "Max Grenkowitz"
        }
    ],
    "frameworks": "arduino",
    "platforms": "atmelavr"
}

config and main are in HotWaterTemperatures/src

config.cpp

/*
    Configuration parameter file
*/
#include "config.h"

const char MQTT_CONNECT_ID[] {"ShedHotWaterSystem"};
const char MQTT_PUB_TOPIC_BASE[] {"ArgyleCourt/Shed/Tower/ShedHotWaterSystem/"};
const char MQTT_PUB_MAC[] {"MAC"};
const char MQTT_PUB_IP[] {"IP"};
const char MQTT_PUB_VERSION[] {"Version"};
const char MQTT_PUB_NOTIFICATION[] {"Notification"};
const char CODE_VERSION[] {"0.1.0"};
uint8_t MAC_ADDRESS[] {0x45, 0x48, 0x57, 0x53, 0x30, 0x31};

config.h

#ifndef CONFIG_H
#define CONFIG_H

#include <Arduino.h>

extern const char MQTT_CONNECT_ID[];
extern const char MQTT_PUB_TOPIC_BASE[];
extern const char MQTT_PUB_MAC[];
extern const char MQTT_PUB_IP[];
extern const char MQTT_PUB_VERSION[];
extern const char MQTT_PUB_NOTIFICATION[];
extern const char CODE_VERSION[];
extern uint8_t MAC_ADDRESS[];

#endif // CONFIG_H

platformio.ini

; PlatformIO Project Configuration File
;
;   Build options: build flags, source filter
;   Upload options: custom upload port, speed and extra flags
;   Library options: dependencies, extra library storages
;   Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html

[env:uno]
platform = atmelavr
board = uno
framework = arduino

[env:uno_r4_minima]
platform = renesas-ra
board = uno_r4_minima
framework = arduino

[env]
lib_deps =
	arduino-libraries/Ethernet@^2.0.0
	knolleary/PubSubClient@^2.8
	paulstoffregen/OneWire@^2.3.8
    milesburton/DallasTemperature@^4.0.4
    file:///home/maxg/Workspaces/PlatformIO/Projects/Arduino_Shared
monitor_speed = 115200
upload_port = /dev/ttyACM1
build_flags =
	-Wall
	-Werror
	-Wunused-variable
    -I /home/maxg/Workspaces/PlatformIO/Projects/Arduino_Shared/include
;    -I ../../Arduino_Shared/include
;src_filter = +<*.cpp> +<../../Arduino_Shared/mqtt_utils.cpp>
;lib_extra_dirs = ../../Arduino_Shared
lib_extra_dirs = /home/maxg/Workspaces/PlatformIO/Projects/Arduino_Shared

and the main.cpp

/*
    Hotwater system controller with Arduino UNO R3 (or R4)

    Copyright 2025 Max G

    @author Max Grenkowitz

    20250419 v0.1.0 MaxG -  externalising repeated code into include files
    20250417 v0.0.2 MaxG -  formatting, naming, added sensor loop;
    20250416 v0.0.1 MaxG -  PoC

*/
/*! --------------- Libraries ----------------------------------------------- */
#include <Arduino.h>                            // basic Arduino definitions
#include <stdbool.h>                            // boolean definition
#include <SPI.h>                                // Serial Peripheral Interface Bus
#include <Ethernet.h>                           // Ethernet library for EtherShield
#include <PubSubClient.h>                       // MQTT client library
#include <OneWire.h>
#include <DallasTemperature.h>
#include "config.h"
#include <debug_utils.h>
#include <mqtt_utils.h>

#define DEBUG
#define BOARD_UNO_R3                            // can be R3 or R4

#ifdef BOARD_UNO_R4
#include "FspTimer.h"
#include <WDT.h>                                // watch dog
FspTimer background_timer;
unsigned long g_timeout_counter = 0;
#else
#include <avr/wdt.h>                            // watch dog
#endif

const uint8_t G_PIN_ETHERNET_SEL {10};          // chip select for EthernetShield
const uint8_t G_PIN_ONE_WIRE_BUS  {2};          // Pin for DS18B20 sensors


// Maximum number of sensors supported
const uint8_t G_MAX_NUM_SENSORS {4};

// Sensor names for MQTT temperature topics
const char* sensor_names[G_MAX_NUM_SENSORS] {"Top", "Bottom", "BoilerSkin", "TowerAir"};

// Minimum temperature change to publish (in Β°C)
const float G_TEMP_CHANGE_THRESHOLD {0.1};

bool g_has_controller_rebooted {true};

// Store previous temperatures to detect changes; preset with DS18B20 invalid temp
float g_previous_temps[G_MAX_NUM_SENSORS]
{
    DEVICE_DISCONNECTED_C,
    DEVICE_DISCONNECTED_C,
    DEVICE_DISCONNECTED_C,
    DEVICE_DISCONNECTED_C
};

uint8_t g_num_of_detected_sensors = 0;


/*! --------------- Instantiate global objects ------------------------------ */
EthernetClient ethernet_client;
PubSubClient mqtt_client(ethernet_client);
OneWire oneWire(G_PIN_ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
LogMsgLevel g_log_msg_level;


// Hardcoded ROM addresses for known sensors
DeviceAddress sensor_addresses[G_MAX_NUM_SENSORS] {
    {0x28, 0x19, 0xC6, 0x85, 0x00, 0x00, 0x00, 0x35}, // Boiler Top
    {0x28, 0xD3, 0x94, 0x85, 0x00, 0x00, 0x00, 0x73}, // Boiler Bottom
    {0x28, 0x9D, 0xB2, 0x85, 0x00, 0x00, 0x00, 0x71}, // Boiler Skin
    {0x28, 0x3A, 0xAB, 0x83, 0x00, 0x00, 0x00, 0x2E}  // Tower Air
};


/*! --------------- Prototypes ---------------------------------------------- */
// modify function(s) as required
bool b_is_mqtt_connected_to_broker();

// Program specific functions
void read_temp_sensors();
void format_temp(float temp, char* output, size_t size);
void get_temp_sensors();

#ifdef BOARD_UNO_R4
void watchdog_timer_callback(timer_callback_args_t __attribute((unused)) *p_args);
bool begin_watchdog_timer(float rate);
#endif


/*! --------------- Function declarations ----------------------------------- */

/*!
 * -----------------------------------------------------------------------------
 * @brief b_is_mqtt_connected_to_broker --- (re)connect to MQTT broker
 * @param[out] MQTT broker connection state
 * -----------------------------------------------------------------------------
 */
bool b_is_mqtt_connected_to_broker()
{

    return mqtt_client.connected();

    // add any broker topic subcriptions here

}   // b_is_mqtt_connected_to_broker()


/*!
 * -----------------------------------------------------------------------------
 * @brief read_temp_sensors --- reads and publishes temperature sensor data
 * -----------------------------------------------------------------------------
 */
void read_temp_sensors()
{
    sensors.requestTemperatures();

    for (uint8_t i = 0; i < g_num_of_detected_sensors; i++)
    {
        float temp = sensors.getTempC(sensor_addresses[i]);

        debug(F("Sensor "));
        debug(i);
        debug(F(" ("));
        debug(sensor_names[i]);
        debug(F("): "));
        debugln(temp);

        const char MQTT_PUB_TEMP_PREFIX[] {"Temp"};

        if (abs(temp - g_previous_temps[i]) >= G_TEMP_CHANGE_THRESHOLD && temp != -127.0)
        {
            char tempStr[8];
            format_temp(temp, tempStr, sizeof(tempStr));

            // Concatenate MQTT_PUB_TEMP_PREFIX with sensor_names[i]
            char topic_string[16];
            snprintf(topic_string, sizeof(topic_string), "%s%s", MQTT_PUB_TEMP_PREFIX, sensor_names[i]);
            publish_message(topic_string, tempStr);

            g_previous_temps[i] = temp;

            debug(F("Published "));
            debug(sensor_names[i]);
            debug(F(": "));
            debugln(tempStr);
        }
    }

}   // read_temp_sensors()


/*!
 * -----------------------------------------------------------------------------
 * @brief format_temp --- trims spaces and formats temperature
 * -----------------------------------------------------------------------------
 */
void format_temp(float temp, char* output, size_t size)
{
    char tempStr[16];

    // Format: 6 chars total, 2 decimal places
    dtostrf(temp, 6, 2, tempStr);

    // Trim leading spaces
    char* ptr = tempStr;

    while (*ptr == ' ')
    {
        ptr++;
    }

    strncpy(output, ptr, size);
    output[size - 1] = '\0';

}   // format_temp()


/*!
 * -----------------------------------------------------------------------------
 * @brief get_temp_sensors --- initialises and configures DS18B20 sensors
 * -----------------------------------------------------------------------------
 */
void get_temp_sensors()
{
    sensors.begin();

    g_num_of_detected_sensors = sensors.getDeviceCount();
    uint8_t valid_sensors = 0;

    if (g_num_of_detected_sensors < 1 || g_num_of_detected_sensors > G_MAX_NUM_SENSORS)
    {
        debug(F("Error: Found "));
        debug(g_num_of_detected_sensors);
        debug(F(" sensors, expected 1 to "));
        debugln(G_MAX_NUM_SENSORS);

        char error_msg[32];
        snprintf(error_msg, sizeof(error_msg), "Invalid sensor count: %d", g_num_of_detected_sensors);
        publish_error(ERROR, error_msg);
        g_num_of_detected_sensors = 0;
        return;
    }

    debug(F("Found "));
    debug(g_num_of_detected_sensors);
    debugln(F(" sensors"));

    // Verify and set resolution for known sensors
    for (uint8_t i = 0; i < g_num_of_detected_sensors; i++)
    {
        // Use hardcoded addresses for known sensors
        if (i < 2 || sensor_addresses[i][0] != 0x00)
        {
            if (false == sensors.isConnected(sensor_addresses[i]))
            {
                debug(F("Error: Sensor "));
                debug(sensor_names[i]);
                debugln(F(" not found"));

                char error_msg[24];
                snprintf(error_msg, sizeof(error_msg), "Sensor %s not found", sensor_names[i]);
                publish_error(2, error_msg);
                continue;
            }

            sensors.setResolution(sensor_addresses[i], 12);
            valid_sensors++;
        }
        else    // Discover new sensors
        {
            DeviceAddress temp_address;

            if (sensors.getAddress(temp_address, i))
            {
                memcpy(sensor_addresses[i], temp_address, 8);
                sensors.setResolution(sensor_addresses[i], 12);

                debug(F("Discovered sensor "));
                debug(sensor_names[i]);
                debug(F(" address: "));

                for (uint8_t j = 0; j < 8; j++)
                {
                    debug("0x");
                    debug(sensor_addresses[i][j], HEX);
                    debug(" ");
                }

                debugln();
                valid_sensors++;
            }
            else
            {
                char error_msg[24];
                snprintf(error_msg, sizeof(error_msg), "No sensor at index %d", i);
                publish_error(ERROR, error_msg);
                debugln(error_msg);

                continue;
            }
        }
    }

    g_num_of_detected_sensors = valid_sensors;

    if (g_num_of_detected_sensors == 0)
    {
        debugln(F("Warning: No valid sensors configured"));
        publish_error(WARN, (char *) "No valid sensors configured");
    }

}   // get_temp_sensors()


#ifdef BOARD_UNO_R4
bool begin_watchdog_timer(float rate)
{
    uint8_t timer_type = GPT_TIMER;
    int8_t tindex = FspTimer::get_available_timer(timer_type);

    if (tindex < 0)
    {
        tindex = FspTimer::get_available_timer(timer_type, true);
    }

    if (tindex < 0)
    {
        return false;
    }

    FspTimer::force_use_of_pwm_reserved_timer();

    if (false == background_timer.begin(TIMER_MODE_PERIODIC, timer_type, tindex, rate, 0.0f, watchdog_timer_callback))
    {
        return false;
    }

    if (false == background_timer.setup_overflow_irq())
    {
        return false;
    }

    if (false == background_timer.open())
    {
        return false;
    }

    if (false == background_timer.start())
    {
        return false;
    }

    return true;
}


void watchdog_timer_callback(timer_callback_args_t __attribute((unused)) *p_args)
{
    WDT.refresh();

    // If more than 5 minutes unresponsive...
    if (millis() > (g_timeout_counter + (1000 * 60 * 5)))
    {
        NVIC_SystemReset();
    }
}
#endif


/*!
 * -----------------------------------------------------------------------------
 * @brief Setup routine -- runs once
 * -----------------------------------------------------------------------------
 */
void setup()
{
    // To ensure the EthernetShield has reset too
    delay(200);

	g_has_controller_rebooted = true;

    #ifdef DEBUG
    const uint32_t BAUD_RATE_HW {115200};
    Serial.begin(BAUD_RATE_HW);
    delay(500);
    #endif

    debug(F("\nReady to query EHWS v"));
    debugln(CODE_VERSION);
    debug("Compiled at: ");
    debug(__DATE__);
    debug(", ");
    debugln(__TIME__);

    debugln(F("Trying DHCP..."));

    Ethernet.init(G_PIN_ETHERNET_SEL);

    while (0 == Ethernet.begin(MAC_ADDRESS))
    {
        debugln(F("Get DHCP failed, retry in 2 sec."));
        delay(2000);
    }

    const uint16_t MQTT_BROKER_PORT {1883};
    const char* MQTT_BROKER_IP {"192.168.1.5"};
    mqtt_client.setServer(MQTT_BROKER_IP, MQTT_BROKER_PORT);

    // Retry MQTT connection until successful
    debugln(F("Trying to connect to MQTT broker..."));

    while (false == mqtt_client.connect(MQTT_CONNECT_ID))
    {
        debugln(F("Broker connection failed, retry in 2 sec."));
        delay(2000);
    }

    debugln(F("Broker connected"));

    debug(F("Client IP..: "));
    debugln(Ethernet.localIP());
    debug(F("Subnet Mask: "));
    debugln(Ethernet.subnetMask());
    debug(F("Gateway IP.: "));
    debugln(Ethernet.gatewayIP());
    debug(F("DNS srvr IP: "));
    debugln(Ethernet.dnsServerIP());
    debug(F("BROKER_IP..: "));
    debugln(MQTT_BROKER_IP);
    debug(F("CONNECT_ID.: "));
    debugln(MQTT_CONNECT_ID);

    get_temp_sensors();

    #ifdef BOARD_UNO_R4
    if (WDT.begin(5000))
    {
        WDT.refresh();
    }
    else
    {
        debugln(F("Error initialising watchdog!"));
    }

    begin_watchdog_timer(1);
    #else
    wdt_enable(WDTO_8S);                        // set 8-second watch dog timer
    #endif

    debugln(F("-> exit setup"));

}   // setup()


/*!
 * -----------------------------------------------------------------------------
 * @brief Loop -- runs continuously
 * -----------------------------------------------------------------------------
 */
void loop()
{
    #ifdef BOARD_UNO_R4
    g_timeout_counter = millis();  // Kick the timeout counter to avoid reset.
    #else
    // Reset watch dog timer; must the first statement in the loop
    wdt_reset();
    #endif

    maintain_dhcp_connection();

    if (false == mqtt_client.connected())
    {
        // Try establishing a connection to the MQTT server
        reconnect_mqtt_client();
    }
    else
    {
        // Maintain MQTT connection and check for incoming messages
        mqtt_client.loop();

        // Start any loop work here

        if (true == g_has_controller_rebooted)
        {
            g_has_controller_rebooted = false;
            debugln(F("--> Arduino rebooted!"));
            publish_error(INFO, (char*) "has rebooted");
            send_post_boot_data();

            // Add the first rouund of data output; otherwise we have to wait
            // for the first read interval to pass.
            read_temp_sensors();
        }


        const uint16_t TEMP_READ_INTERVAL {30000};   // [ms]
        static uint32_t temp_last_read = 0;     // timestamp for last time CAN bus was read

        if (millis() - temp_last_read > TEMP_READ_INTERVAL)
        {
            debug(millis());
            debugln(F("Read/publish sensors..."));
            temp_last_read = millis();
            read_temp_sensors();
        }

        // Periodically reinitialise sensors (e.g., here, every 5 minutes)
        const uint32_t SENSOR_REINIT_INTERVAL {300000}; // [ms]
        static uint32_t last_sensor_reinit = 0;

        if (millis() - last_sensor_reinit > SENSOR_REINIT_INTERVAL)
        {
            debugln(F("Re-init sensors..."));
            last_sensor_reinit = millis();
            get_temp_sensors();
        }
    }
}


// Memory usage, see docs/memory_usage.txt

// end of file

You should split your β€œArduino_Shared” files in different libraries!

I see two libraries here

  • MQTTUtils
  • DebugUtils

The seperated libraries should look something like this:

└── projects
    β”œβ”€β”€ Arduino_Shared
    β”‚   β”œβ”€β”€ DebugUtils
    β”‚   β”‚   β”œβ”€β”€ include
    β”‚   β”‚   β”‚   └── debug_utils.h
    β”‚   β”‚   β”œβ”€β”€ src
    β”‚   β”‚   β”‚   └── debug_utils.cpp
    β”‚   β”‚   └── library.json
    β”‚   └── MQTTUtils
    β”‚       β”œβ”€β”€ include
    β”‚       β”‚   └── mqtt_utils.h
    β”‚       β”œβ”€β”€ src
    β”‚       β”‚   └── mqtt_utils.cpp
    β”‚       └── library.json

where each library has it’s own library.json

Examples:
DebugUtils/library.json

{
    "name": "DebugUtils",
    "version": "1.0.0"
}

MQTTUtils/library.json

{
    "name": "MQTTUtils",
    "version": "1.0.0"
}

lib_extra_dirs is deprecated and replaced by symlink - See lib_extra_dirs β€” PlatformIO latest documentation

According to your file structure
/home/maxg/Workspaces/PlatformIO/Projects for projects
and
/home/maxg/Workspaces/PlatformIO/Projects/Arduino_Shared for libraries
you should be able to include your libraries in the projects platformio.ini by

lib_deps =
  symlink://../Arduino_Shared/MQTTUtils
  symlink://../Arduino_Shared/DebugUtils

and

#include <mqtt_utils.h>
#include <debug_utils.h>

into your source files.

1 Like

Thank you kindly for looking into this.
This is much cleaner, rather than stuffing all the file into one directory.
OK, this is what I have done; updated platformio.ini

; PlatformIO Project Configuration File
;
;   Build options: build flags, source filter
;   Upload options: custom upload port, speed and extra flags
;   Library options: dependencies, extra library storages
;   Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html

[env:uno]
platform = atmelavr
board = uno
framework = arduino

[env:uno_r4_minima]
platform = renesas-ra
board = uno_r4_minima
framework = arduino

[env]
lib_deps =
    arduino-libraries/Ethernet@^2.0.0
    knolleary/PubSubClient@^2.8
    paulstoffregen/OneWire@^2.3.8
    milesburton/DallasTemperature@^4.0.4
    symlink://../Arduino_Shared/MQTTUtils
    symlink://../Arduino_Shared/DebugUtils
monitor_speed = 115200
upload_port = /dev/ttyACM1
build_flags =
	-Wall
	-Werror
	-Wunused-variable

The new directory structure and naming (adopted yours for the directory names, which seems more common from a capitalisation perspective, but retained the file names).

/home/maxg/Workspaces/PlatformIO/Projects/Arduino_Shared:
total 16
drwxrwxr-x   4 maxg maxg 4096 Apr 22 09:04 .
drwxrwxrwx 110 maxg maxg 4096 Apr 20 19:20 ..
drwxrwxr-x   2 maxg maxg 4096 Apr 22 09:01 DebugUtils
drwxrwxr-x   2 maxg maxg 4096 Apr 22 09:00 MQTTUtils

/home/maxg/Workspaces/PlatformIO/Projects/Arduino_Shared/DebugUtils:
total 16
drwxrwxr-x 2 maxg maxg 4096 Apr 22 09:01 .
drwxrwxr-x 4 maxg maxg 4096 Apr 22 09:04 ..
-rw-rw-r-- 1 maxg maxg  310 Apr 19 22:56 debug_utils.h
-rw-rw-r-- 1 maxg maxg  299 Apr 22 09:01 library.json

/home/maxg/Workspaces/PlatformIO/Projects/Arduino_Shared/MQTTUtils:
total 24
drwxrwxr-x 2 maxg maxg 4096 Apr 22 09:00 .
drwxrwxr-x 4 maxg maxg 4096 Apr 22 09:04 ..
-rw-rw-r-- 1 maxg maxg  296 Apr 22 09:00 library.json
-rw-rw-r-- 1 maxg maxg 4251 Apr 21 19:35 mqtt_utils.cpp
-rw-rw-r-- 1 maxg maxg  569 Apr 21 19:34 mqtt_utils.h

The source file already had the directories as you’ve suggested.

However, compiling it gives me an error:

Processing uno (platform: atmelavr; board: uno; framework: arduino; lib_deps: arduino-libraries/Ethernet@^2.0.0, knolleary/PubSubClient@^2.8, paulstoffregen/OneWire@^2.3.8, milesburton/DallasTemperature@^4.0.4, symlink://../Arduino_Shared/MQTTUtils, symlink://../Arduino_Shared/DebugUtils; monitor_speed: 115200; upload_port: /dev/ttyACM1; build_flags: -Wall, -Werror, -Wunused-variable)
-------------------------------------------------------------------------------------
CONFIGURATION: https://docs.platformio.org/page/boards/atmelavr/uno.html
PLATFORM: Atmel AVR (5.0.0) > Arduino Uno
HARDWARE: ATMEGA328P 16MHz, 2KB RAM, 31.50KB Flash
DEBUG: Current (avr-stub) External (avr-stub, simavr)
PACKAGES: 
 - framework-arduino-avr @ 5.2.0 
 - toolchain-atmelavr @ 1.70300.191015 (7.3.0)
LDF: Library Dependency Finder -> https://bit.ly/configure-pio-ldf
LDF Modes: Finder ~ chain, Compatibility ~ soft
Found 11 compatible libraries
Scanning dependencies...
Dependency Graph
|-- Ethernet @ 2.0.2 (License: Unknown, Path: /home/maxg/Workspaces/PlatformIO/Projects/HotWaterTemperatures/.pio/libdeps/uno/Ethernet)
|   |-- SPI @ 1.0 (License: Unknown, Path: /home/maxg/.platformio/packages/framework-arduino-avr/libraries/SPI)
|-- PubSubClient @ 2.8.0 (License: Unknown, Path: /home/maxg/Workspaces/PlatformIO/Projects/HotWaterTemperatures/.pio/libdeps/uno/PubSubClient)
|-- OneWire @ 2.3.8 (License: Unknown, Path: /home/maxg/Workspaces/PlatformIO/Projects/HotWaterTemperatures/.pio/libdeps/uno/OneWire)
|-- DallasTemperature @ 4.0.4 (License: Unknown, Path: /home/maxg/Workspaces/PlatformIO/Projects/HotWaterTemperatures/.pio/libdeps/uno/DallasTemperature)
|   |-- OneWire @ 2.3.8 (License: Unknown, Path: /home/maxg/Workspaces/PlatformIO/Projects/HotWaterTemperatures/.pio/libdeps/uno/OneWire)
|-- MQTT utilities @ 1.0.0 (License: Unknown, Path: /home/maxg/Workspaces/PlatformIO/Projects/Arduino_Shared/MQTTUtils)
|   |-- Ethernet @ 2.0.2 (License: Unknown, Path: /home/maxg/Workspaces/PlatformIO/Projects/HotWaterTemperatures/.pio/libdeps/uno/Ethernet)
|   |   |-- SPI @ 1.0 (License: Unknown, Path: /home/maxg/.platformio/packages/framework-arduino-avr/libraries/SPI)
|   |-- PubSubClient @ 2.8.0 (License: Unknown, Path: /home/maxg/Workspaces/PlatformIO/Projects/HotWaterTemperatures/.pio/libdeps/uno/PubSubClient)
|   |-- DEBUG utilities @ 1.0.0 (License: Unknown, Path: /home/maxg/Workspaces/PlatformIO/Projects/Arduino_Shared/DebugUtils)
|-- DEBUG utilities @ 1.0.0 (License: Unknown, Path: /home/maxg/Workspaces/PlatformIO/Projects/Arduino_Shared/DebugUtils)
|-- SPI @ 1.0 (License: Unknown, Path: /home/maxg/.platformio/packages/framework-arduino-avr/libraries/SPI)
Building in release mode
avr-gcc-ar rc .pio/build/uno/lib7bc/libEthernet.a .pio/build/uno/lib7bc/Ethernet/Dhcp.cpp.o .pio/build/uno/lib7bc/Ethernet/Dns.cpp.o .pio/build/uno/lib7bc/Ethernet/Ethernet.cpp.o .pio/build/uno/lib7bc/Ethernet/EthernetClient.cpp.o .pio/build/uno/lib7bc/Ethernet/EthernetServer.cpp.o .pio/build/uno/lib7bc/Ethernet/EthernetUdp.cpp.o .pio/build/uno/lib7bc/Ethernet/socket.cpp.o .pio/build/uno/lib7bc/Ethernet/utility/w5100.cpp.o
avr-gcc-ar rc .pio/build/uno/lib436/libPubSubClient.a .pio/build/uno/lib436/PubSubClient/PubSubClient.cpp.o
avr-gcc-ranlib .pio/build/uno/lib7bc/libEthernet.a
avr-gcc-ranlib .pio/build/uno/lib436/libPubSubClient.a
avr-gcc-ar rc .pio/build/uno/libbff/libOneWire.a .pio/build/uno/libbff/OneWire/OneWire.cpp.o
avr-gcc-ranlib .pio/build/uno/libbff/libOneWire.a
avr-gcc-ar rc .pio/build/uno/libd93/libDallasTemperature.a .pio/build/uno/libd93/DallasTemperature/DallasTemperature.cpp.o
avr-gcc-ranlib .pio/build/uno/libd93/libDallasTemperature.a
avr-g++ -o .pio/build/uno/libcf0/MQTTUtils/mqtt_utils.cpp.o -c -fno-exceptions -fno-threadsafe-statics -fpermissive -std=gnu++11 -Wall -Werror -Wunused-variable -mmcu=atmega328p -Os -Wall -ffunction-sections -fdata-sections -flto -DPLATFORMIO=60118 -DARDUINO_AVR_UNO -DF_CPU=16000000L -DARDUINO_ARCH_AVR -DARDUINO=10808 -I/home/maxg/Workspaces/PlatformIO/Projects/Arduino_Shared/MQTTUtils -I/home/maxg/Workspaces/PlatformIO/Projects/Arduino_Shared/DebugUtils -I.pio/libdeps/uno/PubSubClient/src -I.pio/libdeps/uno/Ethernet/src -I/home/maxg/.platformio/packages/framework-arduino-avr/libraries/SPI/src -I/home/maxg/.platformio/packages/framework-arduino-avr/cores/arduino -I/home/maxg/.platformio/packages/framework-arduino-avr/variants/standard /home/maxg/Workspaces/PlatformIO/Projects/Arduino_Shared/MQTTUtils/mqtt_utils.cpp
avr-g++ -o .pio/build/uno/FrameworkArduino/HardwareSerial0.cpp.o -c -fno-exceptions -fno-threadsafe-statics -fpermissive -std=gnu++11 -Wall -Werror -Wunused-variable -mmcu=atmega328p -Os -Wall -ffunction-sections -fdata-sections -flto -DPLATFORMIO=60118 -DARDUINO_AVR_UNO -DF_CPU=16000000L -DARDUINO_ARCH_AVR -DARDUINO=10808 -I/home/maxg/.platformio/packages/framework-arduino-avr/cores/arduino -I/home/maxg/.platformio/packages/framework-arduino-avr/variants/standard /home/maxg/.platformio/packages/framework-arduino-avr/cores/arduino/HardwareSerial0.cpp
In file included from /home/maxg/Workspaces/PlatformIO/Projects/Arduino_Shared/MQTTUtils/mqtt_utils.cpp:1:0:
/home/maxg/Workspaces/PlatformIO/Projects/Arduino_Shared/MQTTUtils/mqtt_utils.h:8:10: fatal error: config.h: No such file or directory

****************************************************************
* Looking for config.h dependency? Check our library registry!
*
* CLI  > platformio lib search "header:config.h"
* Web  > https://registry.platformio.org/search?q=header:config.h
*
****************************************************************

 #include "config.h"
          ^~~~~~~~~~
compilation terminated.
avr-g++ -o .pio/build/uno/FrameworkArduino/HardwareSerial1.cpp.o -c -fno-exceptions -fno-threadsafe-statics -fpermissive -std=gnu++11 -Wall -Werror -Wunused-variable -mmcu=atmega328p -Os -Wall -ffunction-sections -fdata-sections -flto -DPLATFORMIO=60118 -DARDUINO_AVR_UNO -DF_CPU=16000000L -DARDUINO_ARCH_AVR -DARDUINO=10808 -I/home/maxg/.platformio/packages/framework-arduino-avr/cores/arduino -I/home/maxg/.platformio/packages/framework-arduino-avr/variants/standard /home/maxg/.platformio/packages/framework-arduino-avr/cores/arduino/HardwareSerial1.cpp
*** [.pio/build/uno/libcf0/MQTTUtils/mqtt_utils.cpp.o] Error 1
============================ [FAILED] Took 0.55 seconds ============================

Environment    Status    Duration
-------------  --------  ------------
uno            FAILED    00:00:00.547
uno_r4_minima  IGNORED
======================= 1 failed, 0 succeeded in 00:00:00.547 =======================
# [2025-04-22 09:05] maxg@x570 ~/Workspaces/PlatformIO/Projects/HotWaterTemperatures $ 

This config.h has given me a lot of problems in trying to figure this out. :frowning:

I reckon it makes sense that the confg.h remains in the project/src directory. While the little libs go into Arduino_Shared. Or do I have to bring the config.* contents back into main.cpp to avoid this problem?


[update] I have tried the latter, to no avail :frowning:

Executing task in folder HotWaterTemperatures: platformio run --environment uno 

Processing uno (platform: atmelavr; board: uno; framework: arduino)
-------------------------------------------------------------------------------------
Verbose mode can be enabled via `-v, --verbose` option
CONFIGURATION: https://docs.platformio.org/page/boards/atmelavr/uno.html
PLATFORM: Atmel AVR (5.0.0) > Arduino Uno
HARDWARE: ATMEGA328P 16MHz, 2KB RAM, 31.50KB Flash
DEBUG: Current (avr-stub) External (avr-stub, simavr)
PACKAGES: 
 - framework-arduino-avr @ 5.2.0 
 - toolchain-atmelavr @ 1.70300.191015 (7.3.0)
LDF: Library Dependency Finder -> https://bit.ly/configure-pio-ldf
LDF Modes: Finder ~ chain, Compatibility ~ soft
Found 11 compatible libraries
Scanning dependencies...
Dependency Graph
|-- Ethernet @ 2.0.2
|-- PubSubClient @ 2.8.0
|-- OneWire @ 2.3.8
|-- DallasTemperature @ 4.0.4
|-- MQTT utilities @ 1.0.0
|-- DEBUG utilities @ 1.0.0
|-- SPI @ 1.0
Building in release mode
Compiling .pio/build/uno/src/main.cpp.o
Compiling .pio/build/uno/lib7bc/Ethernet/EthernetServer.cpp.o
Compiling .pio/build/uno/lib7bc/Ethernet/EthernetUdp.cpp.o
Compiling .pio/build/uno/lib7bc/Ethernet/socket.cpp.o
Compiling .pio/build/uno/lib7bc/Ethernet/utility/w5100.cpp.o
Compiling .pio/build/uno/lib436/PubSubClient/PubSubClient.cpp.o
Compiling .pio/build/uno/libbff/OneWire/OneWire.cpp.o
Compiling .pio/build/uno/libd93/DallasTemperature/DallasTemperature.cpp.o
Compiling .pio/build/uno/libcf0/MQTTUtils/mqtt_utils.cpp.o
Archiving .pio/build/uno/libFrameworkArduinoVariant.a
Indexing .pio/build/uno/libFrameworkArduinoVariant.a
Compiling .pio/build/uno/FrameworkArduino/CDC.cpp.o
Compiling .pio/build/uno/FrameworkArduino/HardwareSerial.cpp.o
/home/maxg/Workspaces/PlatformIO/Projects/Arduino_Shared/MQTTUtils/mqtt_utils.cpp: In function 'void publish_mac_address()':
/home/maxg/Workspaces/PlatformIO/Projects/Arduino_Shared/MQTTUtils/mqtt_utils.cpp:14:14: error: 'MAC_ADDRESS' was not declared in this scope
              MAC_ADDRESS[0], MAC_ADDRESS[1], MAC_ADDRESS[2],
              ^~~~~~~~~~~
Compiling .pio/build/uno/FrameworkArduino/HardwareSerial0.cpp.o
/home/maxg/Workspaces/PlatformIO/Projects/Arduino_Shared/MQTTUtils/mqtt_utils.cpp:18:21: error: 'MQTT_PUB_MAC' was not declared in this scope
     publish_message(MQTT_PUB_MAC, mac_string);
                     ^~~~~~~~~~~~
/home/maxg/Workspaces/PlatformIO/Projects/Arduino_Shared/MQTTUtils/mqtt_utils.cpp:18:21: note: suggested alternative: 'MQTTPUBREC'
     publish_message(MQTT_PUB_MAC, mac_string);
                     ^~~~~~~~~~~~
                     MQTTPUBREC
/home/maxg/Workspaces/PlatformIO/Projects/Arduino_Shared/MQTTUtils/mqtt_utils.cpp: In function 'void publish_ip_address()':
/home/maxg/Workspaces/PlatformIO/Projects/Arduino_Shared/MQTTUtils/mqtt_utils.cpp:30:21: error: 'MQTT_PUB_IP' was not declared in this scope
     publish_message(MQTT_PUB_IP, ip_address);
                     ^~~~~~~~~~~
/home/maxg/Workspaces/PlatformIO/Projects/Arduino_Shared/MQTTUtils/mqtt_utils.cpp:30:21: note: suggested alternative: 'MQTTPUBREC'
     publish_message(MQTT_PUB_IP, ip_address);
                     ^~~~~~~~~~~
                     MQTTPUBREC
/home/maxg/Workspaces/PlatformIO/Projects/Arduino_Shared/MQTTUtils/mqtt_utils.cpp: In function 'void publish_message(const char*, const char*)':
/home/maxg/Workspaces/PlatformIO/Projects/Arduino_Shared/MQTTUtils/mqtt_utils.cpp:80:27: error: 'MQTT_PUB_TOPIC_BASE' was not declared in this scope
     strncat(topic_string, MQTT_PUB_TOPIC_BASE, BUF_SIZE_TOPIC - strlen(topic_string) - 1);
                           ^~~~~~~~~~~~~~~~~~~
/home/maxg/Workspaces/PlatformIO/Projects/Arduino_Shared/MQTTUtils/mqtt_utils.cpp: In function 'void publish_error(uint8_t, char*)':
/home/maxg/Workspaces/PlatformIO/Projects/Arduino_Shared/MQTTUtils/mqtt_utils.cpp:93:18: error: 'MQTT_CONNECT_ID' was not declared in this scope
     strncpy(buf, MQTT_CONNECT_ID, BUF_SIZE - strlen(buf) - 1);
                  ^~~~~~~~~~~~~~~
/home/maxg/Workspaces/PlatformIO/Projects/Arduino_Shared/MQTTUtils/mqtt_utils.cpp:93:18: note: suggested alternative: 'MQTT_CONNECTED'
     strncpy(buf, MQTT_CONNECT_ID, BUF_SIZE - strlen(buf) - 1);
                  ^~~~~~~~~~~~~~~
                  MQTT_CONNECTED
/home/maxg/Workspaces/PlatformIO/Projects/Arduino_Shared/MQTTUtils/mqtt_utils.cpp:114:21: error: 'MQTT_PUB_NOTIFICATION' was not declared in this scope
     publish_message(MQTT_PUB_NOTIFICATION, buf);
                     ^~~~~~~~~~~~~~~~~~~~~
/home/maxg/Workspaces/PlatformIO/Projects/Arduino_Shared/MQTTUtils/mqtt_utils.cpp: In function 'void send_post_boot_data()':
Compiling .pio/build/uno/FrameworkArduino/HardwareSerial1.cpp.o
/home/maxg/Workspaces/PlatformIO/Projects/Arduino_Shared/MQTTUtils/mqtt_utils.cpp:129:21: error: 'MQTT_PUB_VERSION' was not declared in this scope
     publish_message(MQTT_PUB_VERSION, CODE_VERSION);
                     ^~~~~~~~~~~~~~~~
/home/maxg/Workspaces/PlatformIO/Projects/Arduino_Shared/MQTTUtils/mqtt_utils.cpp:129:21: note: suggested alternative: 'MQTT_VERSION'
     publish_message(MQTT_PUB_VERSION, CODE_VERSION);
                     ^~~~~~~~~~~~~~~~
                     MQTT_VERSION
/home/maxg/Workspaces/PlatformIO/Projects/Arduino_Shared/MQTTUtils/mqtt_utils.cpp:129:39: error: 'CODE_VERSION' was not declared in this scope
     publish_message(MQTT_PUB_VERSION, CODE_VERSION);
                                       ^~~~~~~~~~~~
/home/maxg/Workspaces/PlatformIO/Projects/Arduino_Shared/MQTTUtils/mqtt_utils.cpp:129:39: note: suggested alternative: 'MQTT_VERSION'
     publish_message(MQTT_PUB_VERSION, CODE_VERSION);
                                       ^~~~~~~~~~~~
                                       MQTT_VERSION
/home/maxg/Workspaces/PlatformIO/Projects/Arduino_Shared/MQTTUtils/mqtt_utils.cpp: In function 'void reconnect_mqtt_client()':
/home/maxg/Workspaces/PlatformIO/Projects/Arduino_Shared/MQTTUtils/mqtt_utils.cpp:145:29: error: 'MQTT_CONNECT_ID' was not declared in this scope
         mqtt_client.connect(MQTT_CONNECT_ID);
                             ^~~~~~~~~~~~~~~
/home/maxg/Workspaces/PlatformIO/Projects/Arduino_Shared/MQTTUtils/mqtt_utils.cpp:145:29: note: suggested alternative: 'MQTT_CONNECTED'
         mqtt_client.connect(MQTT_CONNECT_ID);
                             ^~~~~~~~~~~~~~~
                             MQTT_CONNECTED
Archiving .pio/build/uno/libbff/libOneWire.a
Indexing .pio/build/uno/libbff/libOneWire.a
Archiving .pio/build/uno/lib7bc/libEthernet.a
*** [.pio/build/uno/libcf0/MQTTUtils/mqtt_utils.cpp.o] Error 1
Indexing .pio/build/uno/lib7bc/libEthernet.a
============================ [FAILED] Took 0.61 seconds ============================

Environment    Status    Duration
-------------  --------  ------------
uno            FAILED    00:00:00.609
======================= 1 failed, 0 succeeded in 00:00:00.609 =======================

 *  The terminal process "platformio 'run', '--environment', 'uno'" terminated with exit code: 1. 
 *  Terminal will be reused by tasks, press any key to close it. 

I put the config.cpp contents into main.cpp, and commented the included directive out in mqtt_utils.h. Now I am puzzled.

Your libraries can’t be dependent to a project file, which the config.h is.

That’s correct. The config.h is a config file per project. It should / must stay with the project.

This wouldn’t change anything.

The solution is to change your functions and make them more flexible by passing parameters to them.

Example:

mqtt_utils.h

...
void publish_mac_address(const char* mac_address, const char* pub_mac, const char* mqtt_pub_topic_base);
...

mqtt_utils.cpp

...
void publish_mac_address(const char* mac_address, const char* pub_mac, const char* mqtt_pub_topic_base)
{
    char mac_string[18] {0};

    snprintf(mac_string, sizeof(mac_string), "%02X:%02X:%02X:%02X:%02X:%02X",
             mac_address[0], mac_address[1], mac_address[2],
             mac_address[3], mac_address[4], mac_address[5]);

    mac_string[17] = '\0';
    publish_message(mqtt_pub_mac, mac_string, mqtt_pub_topic_base);
}
...

main.cpp

#include <mqtt_utils.h>
#include "config.h"

...
   publish_mac_address(MAC_ADDRESS, MQTT_PUB_MAC, MQTT_PUB_TOPIC_BASE);
...

1 Like

Thank you; very helpful.

It allowed me to rethink this… and have refactored the lot, in particular the library functions, passing in parameters now, using a config struct, null testing, etc.

1 Like

An alternative is the definition of macros in the β€œplatformio.ini” file, which can be used by libraries:

[env]
build_flags =
  -D MQTT_TOPIC='"ArgyleCourt/Shed/Tower/ShedHotWaterSystem/"'

or whether you want to split this into a separate section:

[config]
MQTT_TOPIC='"ArgyleCourt/Shed/Tower/ShedHotWaterSystem/"'

[env]
build_flags =
 -D MQTT_TOPIC=${config.MQTT_TOPIC}

mqtt_utils.h

#pragma once

const char* get_mqtt_topic();

mqtt_utils.cpp

#include "mqtt_utils.h"

// fallback if MQTT_TOPIC is not defined in platformio.ini
#ifndef MQTT_TOPIC
#define MQTT_TOPIC "default/topic"
#endif

const char* mqtt_topic = MQTT_TOPIC;

const char* get_mqtt_topic() {
    return mqtt_topic;
}
1 Like