Multiple Definitions Error

I'm getting an error when trying to compile my code using PlatformIO in VSCode.

.pio\build\uno\src\lpcooling_calibration\lpcooling_calibration.cpp.o (symbol from plugin): In function `substring(char*, unsigned char, unsigned char, char*)':
(.text+0x0): multiple definition of `substring(char*, unsigned char, unsigned char, char*)'
.pio\build\uno\src\globals.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pio\build\uno\src\lpcooling_calibration\lpcooling_calibration.cpp.o (symbol from plugin): In function `substring(char*, unsigned char, unsigned char, char*)':
(.text+0x0): multiple definition of `concat(char const*, char, char const*, char*)'
.pio\build\uno\src\globals.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pio\build\uno\src\lpcooling_calibration\lpcooling_calibration.cpp.o (symbol from plugin): In function `substring(char*, unsigned char, unsigned char, char*)':
(.text+0x0): multiple definition of `concat(char const*, char, char*)'
.pio\build\uno\src\globals.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pio\build\uno\src\lpcooling_calibration\lpcooling_calibration.cpp.o (symbol from plugin): In function `substring(char*, unsigned char, unsigned char, char*)':
(.text+0x0): multiple definition of `concat(char const*, char const*, char*)'
.pio\build\uno\src\globals.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pio\build\uno\src\lpcooling_calibration\lpcooling_calibration.cpp.o (symbol from plugin): In function `substring(char*, unsigned char, unsigned char, char*)':
(.text+0x0): multiple definition of `remove(char*, unsigned char, unsigned char)'
.pio\build\uno\src\globals.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pio\build\uno\src\lpcooling_calibration\lpcooling_calibration.cpp.o (symbol from plugin): In function `substring(char*, unsigned char, unsigned char, char*)':
(.text+0x0): multiple definition of `mapProgress(unsigned int, long, long)'
.pio\build\uno\src\globals.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pio\build\uno\src\lpcooling_calibration\lpcooling_calibration.cpp.o (symbol from plugin): In function `substring(char*, unsigned char, unsigned char, char*)':
(.text+0x0): multiple definition of `mapProgress(unsigned int, float, float)'
.pio\build\uno\src\globals.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pio\build\uno\src\lpcooling_i2c\lpcooling_i2c.cpp.o (symbol from plugin): In function `lcd':
(.text+0x0): multiple definition of `substring(char*, unsigned char, unsigned char, char*)'
.pio\build\uno\src\globals.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pio\build\uno\src\lpcooling_i2c\lpcooling_i2c.cpp.o (symbol from plugin): In function `lcd':
(.text+0x0): multiple definition of `concat(char const*, char, char const*, char*)'
.pio\build\uno\src\globals.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pio\build\uno\src\lpcooling_i2c\lpcooling_i2c.cpp.o (symbol from plugin): In function `lcd':
(.text+0x0): multiple definition of `concat(char const*, char, char*)'
.pio\build\uno\src\globals.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pio\build\uno\src\lpcooling_i2c\lpcooling_i2c.cpp.o (symbol from plugin): In function `lcd':
(.text+0x0): multiple definition of `concat(char const*, char const*, char*)'
.pio\build\uno\src\globals.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pio\build\uno\src\lpcooling_i2c\lpcooling_i2c.cpp.o (symbol from plugin): In function `lcd':
(.text+0x0): multiple definition of `remove(char*, unsigned char, unsigned char)'
.pio\build\uno\src\globals.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pio\build\uno\src\lpcooling_i2c\lpcooling_i2c.cpp.o (symbol from plugin): In function `lcd':
(.text+0x0): multiple definition of `mapProgress(unsigned int, long, long)'
.pio\build\uno\src\globals.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pio\build\uno\src\lpcooling_i2c\lpcooling_i2c.cpp.o (symbol from plugin): In function `lcd':
(.text+0x0): multiple definition of `mapProgress(unsigned int, float, float)'
.pio\build\uno\src\globals.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pio\build\uno\src\lpcooling_menu\lpcooling_menu.cpp.o (symbol from plugin): In function `substring(char*, unsigned char, unsigned char, char*)':
(.text+0x0): multiple definition of `substring(char*, unsigned char, unsigned char, char*)'
.pio\build\uno\src\globals.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pio\build\uno\src\lpcooling_menu\lpcooling_menu.cpp.o (symbol from plugin): In function `substring(char*, unsigned char, unsigned char, char*)':
(.text+0x0): multiple definition of `concat(char const*, char, char const*, char*)'
.pio\build\uno\src\globals.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pio\build\uno\src\lpcooling_menu\lpcooling_menu.cpp.o (symbol from plugin): In function `substring(char*, unsigned char, unsigned char, char*)':
(.text+0x0): multiple definition of `concat(char const*, char, char*)'
.pio\build\uno\src\globals.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pio\build\uno\src\lpcooling_menu\lpcooling_menu.cpp.o (symbol from plugin): In function `substring(char*, unsigned char, unsigned char, char*)':
(.text+0x0): multiple definition of `concat(char const*, char const*, char*)'
.pio\build\uno\src\globals.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pio\build\uno\src\lpcooling_menu\lpcooling_menu.cpp.o (symbol from plugin): In function `substring(char*, unsigned char, unsigned char, char*)':
(.text+0x0): multiple definition of `remove(char*, unsigned char, unsigned char)'
.pio\build\uno\src\globals.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pio\build\uno\src\lpcooling_menu\lpcooling_menu.cpp.o (symbol from plugin): In function `substring(char*, unsigned char, unsigned char, char*)':
(.text+0x0): multiple definition of `mapProgress(unsigned int, long, long)'
.pio\build\uno\src\globals.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pio\build\uno\src\lpcooling_menu\lpcooling_menu.cpp.o (symbol from plugin): In function `substring(char*, unsigned char, unsigned char, char*)':
(.text+0x0): multiple definition of `mapProgress(unsigned int, float, float)'
.pio\build\uno\src\globals.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pio\build\uno\src\lpcooling_monitoring\lpcooling_monitoring.cpp.o (symbol from plugin): In function `lastLoggingTime':
(.text+0x0): multiple definition of `substring(char*, unsigned char, unsigned char, char*)'
.pio\build\uno\src\globals.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pio\build\uno\src\lpcooling_monitoring\lpcooling_monitoring.cpp.o (symbol from plugin): In function `lastLoggingTime':
(.text+0x0): multiple definition of `concat(char const*, char, char const*, char*)'
.pio\build\uno\src\globals.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pio\build\uno\src\lpcooling_monitoring\lpcooling_monitoring.cpp.o (symbol from plugin): In function `lastLoggingTime':
(.text+0x0): multiple definition of `concat(char const*, char, char*)'
.pio\build\uno\src\globals.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pio\build\uno\src\lpcooling_monitoring\lpcooling_monitoring.cpp.o (symbol from plugin): In function `lastLoggingTime':
(.text+0x0): multiple definition of `concat(char const*, char const*, char*)'
.pio\build\uno\src\globals.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pio\build\uno\src\lpcooling_monitoring\lpcooling_monitoring.cpp.o (symbol from plugin): In function `lastLoggingTime':
(.text+0x0): multiple definition of `remove(char*, unsigned char, unsigned char)'
.pio\build\uno\src\globals.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pio\build\uno\src\lpcooling_monitoring\lpcooling_monitoring.cpp.o (symbol from plugin): In function `lastLoggingTime':
(.text+0x0): multiple definition of `mapProgress(unsigned int, long, long)'
.pio\build\uno\src\globals.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pio\build\uno\src\lpcooling_monitoring\lpcooling_monitoring.cpp.o (symbol from plugin): In function `lastLoggingTime':
(.text+0x0): multiple definition of `mapProgress(unsigned int, float, float)'
.pio\build\uno\src\globals.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pio\build\uno\src\lpcooling_utils\lpcooling_utils.cpp.o (symbol from plugin): In function `substring(char*, unsigned char, unsigned char, char*)':
(.text+0x0): multiple definition of `substring(char*, unsigned char, unsigned char, char*)'
.pio\build\uno\src\globals.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pio\build\uno\src\lpcooling_utils\lpcooling_utils.cpp.o (symbol from plugin): In function `substring(char*, unsigned char, unsigned char, char*)':
(.text+0x0): multiple definition of `concat(char const*, char, char const*, char*)'
.pio\build\uno\src\globals.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pio\build\uno\src\lpcooling_utils\lpcooling_utils.cpp.o (symbol from plugin): In function `substring(char*, unsigned char, unsigned char, char*)':
(.text+0x0): multiple definition of `concat(char const*, char, char*)'
.pio\build\uno\src\globals.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pio\build\uno\src\lpcooling_utils\lpcooling_utils.cpp.o (symbol from plugin): In function `substring(char*, unsigned char, unsigned char, char*)':
(.text+0x0): multiple definition of `concat(char const*, char const*, char*)'
.pio\build\uno\src\globals.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pio\build\uno\src\lpcooling_utils\lpcooling_utils.cpp.o (symbol from plugin): In function `substring(char*, unsigned char, unsigned char, char*)':
(.text+0x0): multiple definition of `remove(char*, unsigned char, unsigned char)'
.pio\build\uno\src\globals.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pio\build\uno\src\lpcooling_utils\lpcooling_utils.cpp.o (symbol from plugin): In function `substring(char*, unsigned char, unsigned char, char*)':
(.text+0x0): multiple definition of `mapProgress(unsigned int, long, long)'
.pio\build\uno\src\globals.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pio\build\uno\src\lpcooling_utils\lpcooling_utils.cpp.o (symbol from plugin): In function `substring(char*, unsigned char, unsigned char, char*)':
(.text+0x0): multiple definition of `mapProgress(unsigned int, float, float)'
.pio\build\uno\src\globals.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pio\build\uno\src\main.ino.cpp.o (symbol from plugin): In function `substring(char*, unsigned char, unsigned char, char*)':
(.text+0x0): multiple definition of `substring(char*, unsigned char, unsigned char, char*)'
.pio\build\uno\src\globals.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pio\build\uno\src\main.ino.cpp.o (symbol from plugin): In function `substring(char*, unsigned char, unsigned char, char*)':
(.text+0x0): multiple definition of `concat(char const*, char, char const*, char*)'
.pio\build\uno\src\globals.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pio\build\uno\src\main.ino.cpp.o (symbol from plugin): In function `substring(char*, unsigned char, unsigned char, char*)':
(.text+0x0): multiple definition of `concat(char const*, char, char*)'
.pio\build\uno\src\globals.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pio\build\uno\src\main.ino.cpp.o (symbol from plugin): In function `substring(char*, unsigned char, unsigned char, char*)':
(.text+0x0): multiple definition of `concat(char const*, char const*, char*)'
.pio\build\uno\src\globals.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pio\build\uno\src\main.ino.cpp.o (symbol from plugin): In function `substring(char*, unsigned char, unsigned char, char*)':
(.text+0x0): multiple definition of `remove(char*, unsigned char, unsigned char)'
.pio\build\uno\src\globals.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pio\build\uno\src\main.ino.cpp.o (symbol from plugin): In function `substring(char*, unsigned char, unsigned char, char*)':
(.text+0x0): multiple definition of `mapProgress(unsigned int, long, long)'
.pio\build\uno\src\globals.cpp.o (symbol from plugin):(.text+0x0): first defined here
.pio\build\uno\src\main.ino.cpp.o (symbol from plugin): In function `substring(char*, unsigned char, unsigned char, char*)':
(.text+0x0): multiple definition of `mapProgress(unsigned int, float, float)'
.pio\build\uno\src\globals.cpp.o (symbol from plugin):(.text+0x0): first defined here
C:\Users\Shaun\AppData\Local\Temp\ccFNxbJg.ltrans0.ltrans.o: In function `main':
<artificial>:(.text.startup+0x452): undefined reference to `setup'
<artificial>:(.text.startup+0x45a): undefined reference to `loop'
collect2.exe: error: ld returned 1 exit status
*** [.pio\build\uno\firmware.elf] Error 1

I've been moving the fucntion into new source files to try and avoid multiple definitions of these functions and using namespaces to no success. If someone can help me make sense of this error I'd appreciate it.

main.ino

#include <Arduino.h>
//User header files
#include "main.h"
#include "globals.h"
#include "lpcooling_utils/lpcooling_utils.h"
#include "lpcooling_i2c/lpcooling_i2c.h"
#include "lpcooling_menu/lpcooling_menu.h"
#include "lpcooling_calibration/lpcooling_calibration.h"
#include "lpcooling_monitoring/lpcooling_monitoring.h"

namespace lpcooling{
  
  void setup() {
    // put your setup code here, to run once:
    // initialise serial communication
    Serial.begin(SERIAL_BAUD);

    i2c__init();
    Wire.begin();

    // initialise the push buttons
    pinMode(buttonUpPin, INPUT_PULLUP);
    pinMode(buttonDownPin, INPUT_PULLUP);
    pinMode(buttonLeftPin, INPUT_PULLUP);
    pinMode(buttonRightPin, INPUT_PULLUP);
    pinMode(buttonEnterPin, INPUT_PULLUP);

    // initialise the toggle switch
    pinMode(toggleSwitchPin, INPUT_PULLUP);
    delay(100);
    startSampling();

    // initialise mode according to the toggleSwitch state and attach interrupts 
    // monitor__init();
    if (digitalRead(toggleSwitchPin)){
      isMonitoring = true;
      isCalibrating = false;
      isSampling = true;
    } else {
      isMonitoring = false;
      calibrationState = CALIBRATION_IDLE;
      isCalibrating = true;
      isSampling = true;
    }
    // menuMain__init();
  }

  

  void loop() {
    // get readings from the pressure sensor and digital input
    menuMain();
    readButtons();
    monitorMain();
    calibration();
  }
} // namespace lpcooling

main.h

#ifndef LPCOOLING_H
#define LPCOOLING_H

#include <Arduino.h>
#include <SPI.h>
#include <Wire.h>

#include <LcdMenu.h>
#include <string.h>
#include <stdint.h>

namespace lpcooling{
    // Function prototypes******************************************************************************************************************

}   // namespace lpcooling

#endif  // LPCOOLING_H

globals.h

#ifndef GLOBALS_H
#define GLOBALS_H

#include <MenuItem.h>
#include <LcdMenu.h>
#include <string.h>

namespace lpcooling{
  //Global variables, constants and definitions*******************************************************************************************************************

  //Globals variables************************************************************************
  // declare global variables
  extern bool isOnBacklight;
  extern double pressureReading;
  extern bool valveState;
  extern String vState;
  extern bool isSampling;
  extern bool isMonitoring;
  extern bool isCalibrating;
  extern double pressureBar;
  extern float scaleFactor;

  // decalare button states
  enum ButtonState{
      BUTTON_NONE,
      BUTTON_UP,
      BUTTON_DOWN,
      BUTTON_LEFT,
      BUTTON_RIGHT,
      BUTTON_ENTER,
  };

  extern ButtonState currentButtonState;

  enum CalibrationState {
    CALIBRATION_IDLE,
    CALIBRATION_INPUT_0,
    CALIBRATION_INPUT_0_1,
    CALIBRATION_INPUT_1,
    CALIBRATION_INPUT_2,
    CALIBRATION_INPUT_3_1,
    CALIBRATION_INPUT_3_2,
    CALIBRATION_INPUT_3_3,
    CALIBRATION_COMPLETE
  };

  // extern RTC_DS3231 rtc;

  // define SD card chip 
  extern const int chipSelectSD;
  extern const int SSDCard;
  // extern const unsigned long lcdScrollDelay;
  extern const int calibrationRepeats;


  extern CalibrationState calibrationState;

  //CONSTANTS*********************************************************************************

  //Constants and definitions
  // define calibration analogue pins
  #define input1Pin A0  
  #define input2Pin A1  

  #define pressureSensorPin A2

  #define input0Pin A3

  #define REF_VOLTAGE 5.12 
  #define CAL_VOLTAGE 3275

  // define the 5 push buttons digital input pins
  #define buttonUpPin 4
  #define buttonDownPin 3
  #define buttonLeftPin 0
  #define buttonRightPin 1
  #define buttonEnterPin 5

  // define the toggle switch digital input
  #define toggleSwitchPin 2

  // define valve state pin
  #define valveStatePin 8

  // define LCD paramaters
  #define LCD_SELECT 0x27
  #define LCD_ROWS 2
  #define LCD_COLS 16
  #define LCD_SCROLL_DELAY 1000
  #define LCD_BLANK "~"

  // define serial rate
  #define SERIAL_BAUD 9600

  // configure keyboard keys (ASCII)
  #define UP 56        // NUMPAD 8
  #define DOWN 50      // NUMPAD 2
  #define LEFT 52      // NUMPAD 4
  #define RIGHT 54     // NUMPAD 6
  #define ENTER 53     // NUMPAD 5
  #define BACK 55      // NUMPAD 7
  #define BACKSPACE 8  // BACKSPACE
  #define CLEAR 46     // NUMPAD .

  #endif  //GLOBALS_H

  
} // namespace lpcooling

globals.cpp

//STD Libraries
#include <SPI.h>
#include <Wire.h>
#include <RTClib.h>
#include <string.h>
#include <stdint.h>

//User header files
#include "globals.h"

namespace lpcooling{

    // define and create variables for the pressure sensor and analogue input readings
    bool isOnBacklight = false;
    double pressureReading = 0.0;
    bool digitalReading = false;
    bool valveState = false;
    bool isCalibrating = false;
    bool isSampling = true;
    bool isMonitoring = false;
    double pressureBar = 0.0;
    float scaleFactor = 1.0;
    String vState = "";

    const int chipSelectSD = 4;
    const int SSDCard = 10;
    // const unsigned long lcdScrollDelay = 1000;
    const int calibrationRepeats = 10;

    // initialise button state
    ButtonState currentButtonState = BUTTON_NONE;

    CalibrationState calibrationState = CALIBRATION_IDLE;

}

lpcooling_utils.h

#ifndef LPCOOLING_UTILS_H
#define LPCOOLING_UTILS_H



double getPressureReading();
void startSampling();
void startMonitoring();
void stopMonitoring();
void readButtons();

#endif // lpcooling_utils

lpcooling_utils.cpp

#include "../globals.h"

using namespace lpcooling;

void startSampling(){
    isSampling = true;
    isMonitoring = false;
    isCalibrating = false;
}

void startMonitoring(){
    isSampling = true;
    isMonitoring = true;
    isCalibrating = false;
}

void stopMonitoring(){
    isSampling = true;
    isMonitoring = false;
    // reinitialise default calibarionState
    calibrationState = CALIBRATION_IDLE;
    isCalibrating = true;
}

double getPressureReading(){
    return pressureBar;
}

void readButtons(){
bool buttonUpState = digitalRead(buttonUpPin);
if (buttonUpState == HIGH){
    currentButtonState = BUTTON_UP;
} else{
    currentButtonState = BUTTON_NONE;
}

bool buttonDownState = digitalRead(buttonDownPin);
if (buttonDownState == HIGH){
    currentButtonState = BUTTON_DOWN;
} else{
    currentButtonState = BUTTON_NONE;
}

bool buttonLeftState = digitalRead(buttonLeftPin);
if (buttonLeftState == HIGH){
    currentButtonState = BUTTON_LEFT;
} else{
    currentButtonState = BUTTON_NONE;
}

bool buttonRightState = digitalRead(buttonRightPin);
if (buttonRightState == HIGH){
    currentButtonState = BUTTON_RIGHT;
} else{
    currentButtonState = BUTTON_NONE;
}

bool buttonEnterState = digitalRead(buttonEnterPin);
if (buttonEnterState == HIGH){
    currentButtonState = BUTTON_ENTER;
} else{
    currentButtonState = BUTTON_NONE;
}
}

lpcooling_monitoring.h

#ifndef LPCOOLING_MONITORING_H
#define LPCOOLING_MONITORING_H

#include <RTClib.h>

// Function prototypes******************************************************************************************************************
void monitor__init();
void monitorMain();
void handleMonitoring();

#endif  // LPCOOLING_MONITORING_H

lpcooling_monitoring.cpp

//User header files
#include "../globals.h"
// #include "../main.h"
#include "lpcooling_monitoring.h"
#include "../lpcooling_utils/lpcooling_utils.h"
#include "../lpcooling_i2c/lpcooling_i2c.h"

using namespace lpcooling;

double pressureSum = 0.0;
double pressureMin = 0.0;
double pressureMax = 0.0;
int sampleCount = 0;

const int numSamples = 100;
const unsigned long loggingInterval = 5000;
DateTime lastLoggingTime;

void monitor__init() {
  // put your setup code here, to run once:
  attachInterrupt(digitalPinToInterrupt(toggleSwitchPin), startMonitoring, RISING);
  attachInterrupt(digitalPinToInterrupt(toggleSwitchPin), stopMonitoring, FALLING);
}

void monitorMain() {
  // put your main code here, to run repeatedly:
  //presure reading
  if (isSampling){
    pressureReading = analogRead(pressureSensorPin)*scaleFactor;
    pressureSum += pressureReading;
    sampleCount++;

    if (pressureReading > pressureMax){
      pressureMax = pressureReading;
    }
    else if (pressureReading < pressureMin){
      pressureMin = pressureReading;
    }

    if (sampleCount >= numSamples){
      //convert analogue voltage reading to pressure value
      // double pressureBar = 0.25*((2.176*(pressureSum/sampleCount)/2 + 2.72)/680) -1;
      // double pressureBar = 0.25*((1.088*(pressureSum/sampleCount)/2 + 2.72)/680) -1;
      pressureBar = 1.2*((2.176*(pressureSum/sampleCount) + 2.72)/680) -4.8;
      //valve state
      valveState = digitalRead(valveStatePin);
      vState = valveState ? "Active" : "Inactive";

      // update the display with the current readings
      lcdClear();
      writeLCD("Pressure: " + String(pressureBar) + " bar", "Valve: " + vState );

      // reset sampleCount and pressureSum
      sampleCount = 0;
      pressureSum = 0;
      // log the readings to the SD card
      if (isMonitoring){
        handleMonitoring();
      }
    }
  }
}

void handleMonitoring() {
  // code to handle data logging and display of the readings
  DateTime now = getNow();
  if (isMonitoring){
    
    unsigned long elapsedMillis = now.unixtime() - lastLoggingTime.unixtime();

    if (elapsedMillis >= loggingInterval){
      lastLoggingTime = now;
      String sdTxt [] = { now.timestamp() + " Pressure Reading (Min|Nom|Max): " + pressureMin  + "|" + String(pressureBar) + "|" + pressureMax + " " + "Safety Valve: " + vState };
      writeSDCard(sdTxt);
    }
  }
  else{
    unsigned long elapsedMillis = now.unixtime() - lastLoggingTime.unixtime();

    if (elapsedMillis >= loggingInterval){
      lastLoggingTime = now;
      String sdTxt [] = {"CALIBRATION MODE ACTIVE"};
      writeSDCard(sdTxt);     
    }
  }
}

lpcooling_menu.h

#ifndef LPCOOLING_MENU_H
#define LPCOOLING_MENU_H

#include <Arduino.h>


// Function prototypes******************************************************************************************************************
void menuMain__init();
void menuMain();

#endif  // LPCOOLING_MENU_H

lpcooling_menu.cpp

//STD Libraries
#include <LcdMenu.h>
#include <Constants.h>
#include <ItemCommand.h>
#include <ItemSubMenu.h>
#include <ItemToggle.h>

//User header files
#include "../globals.h"
// #include "../main.h"
#include "../lpcooling_i2c/lpcooling_i2c.h"
#include "lpcooling_menu.h"

using namespace lpcooling;

MenuItem* settingsMenu[] = { ITEM_TOGGLE("Toggle Backlight", toggleBacklight),
                                        ITEM_BASIC("Clear SD Card") };
LcdMenu menu(LCD_ROWS, LCD_COLS);

// add the submenus to the main menu
MAIN_MENU(
    ITEM_BASIC("Monitoring"),
    ITEM_BASIC("Calibration"),
    ITEM_SUBMENU("Settings", settingsMenu)
);

void menuMain__init() {
  // Initialize LcdMenu with the menu items
  menu.setupLcdWithMenu(LCD_SELECT, mainMenu);
}

void menuMain() {

    if (currentButtonState == BUTTON_UP)
        menu.up();
    else if (currentButtonState == BUTTON_DOWN)
        menu.down();
    else if (currentButtonState == BUTTON_LEFT)
        menu.left();
    else if (currentButtonState == BUTTON_RIGHT)
        menu.right();
    else if (currentButtonState == BUTTON_ENTER)
        menu.enter();
}

lpcoolong_i2c.h

#ifndef LPCOOLING_I2C_H
#define LPCOOLING_I2C_H

#include <string.h>
#include <RTClib.h>

void scrollMessage(int row, String message, int delayTime, int totalColumns);
void writeLCD(String textline1, String textline2);
void writeSDCard(String text[]);
void toggleBacklight(uint8_t  isOn);
DateTime getNow();
void lcdClear();
int retryWriteSD ();
void i2c__init();
#endif // LPCOOLING_I2C_H

lpcooling_i2c.cpp

#include "../globals.h"

#include <RTClib.h>
#include <LiquidCrystal_I2C.h>
#include <SD.h>
#include <string.h>

using namespace lpcooling;

// define and initialise LCD 
LiquidCrystal_I2C lcd(LCD_SELECT, LCD_COLS, LCD_ROWS);
// define and initialise RTC
RTC_DS3231 rtc;
// define dataFile for RTC
File dataFile;

void i2c__init(){
    lcd.init();
    lcd.backlight();
    isOnBacklight = true;

    // set SS to output
    pinMode(SSDCard,OUTPUT);

    // initialise the RTC
    if (! rtc.begin()) {
      lcd.print("RTC failed");
      while (1);
    }

    if(rtc.lostPower()) {
      // this will adjust to the date and time at compilation
      rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    }
    // initialise the SD card
    if (!SD.begin(chipSelectSD)) {
      lcd.print("SD card failed");
      while (1);
    }
}

void scrollMessage(int row, String message, int delayTime, int totalColumns) {
    for (int i=0; i < totalColumns; i++) {
        message = " " + message;  
    } 
    message = message + " "; 
    for (unsigned int position = 0; position < message.length(); position++) {
        lcd.setCursor(0, row);
        lcd.print(message.substring(position, position + totalColumns));
        delay(delayTime);
    }
}

void writeLCD(String textline1, String textline2){
    if (textline1 != LCD_BLANK && textline1.length() < LCD_COLS){
        lcd.setCursor(0, 0);
        lcd.print(textline1);
    }
    else {
        scrollMessage(0, textline1, LCD_SCROLL_DELAY, LCD_COLS);
    }

    if (textline2 != LCD_BLANK && textline2.length() < LCD_COLS){
        lcd.setCursor(1, 0);
        lcd.print(textline2);
    }
    else {
        scrollMessage(1, textline2, LCD_SCROLL_DELAY, LCD_COLS);
    }
}

void lcdClear(){
    lcd.clear();
}

void toggleBacklight(uint8_t  isOn){
    if (isOn) {
        lcd.backlight();
        isOn = true;
    } else {
        lcd.noBacklight();
        isOn = false;
    }
}

int retryWriteSD (){
    int retryCount = 0;
    bool writeSuccess = false;

    while (retryCount < 4 && ! writeSuccess ){
    if (! dataFile){
        retryCount++;
        writeSuccess = false;
        writeLCD("SD CARD WRITE", "ERROR (" + String(retryCount) + "/3)");
        delay(50);
    } else{
        writeSuccess = true;
        return 1;
    }

    }
    return 0;
}

DateTime getNow(){
    DateTime now = rtc.now();
    return now;
}

void writeSDCard(String text[]){
    DateTime now = getNow();
    if (isMonitoring){
    String fileName = String(now.year()) + "-" + String(now.month()) + "-" + String(now.day()) + "-MONITORING.txt";
    
    dataFile = SD.open(fileName,FILE_WRITE);
    if (retryWriteSD() == 1){
        dataFile.println(text[0]);
        dataFile.flush();
        dataFile.close();
    }else{
        lcdClear();
        writeLCD("DATA NOT", "RECORDED");
    }
    } else if (isCalibrating){
    String fileName = String(now.year()) + "-" + String(now.month()) + "-" + String(now.day()) + "-CALIBRATION.txt";
    dataFile = SD.open(fileName,FILE_WRITE);

    if (retryWriteSD() == 1){
        dataFile.println(text[0]);
        dataFile.println(text[1]);
        dataFile.println(text[2]);
        dataFile.println(text[3]);
        dataFile.flush();
        dataFile.close();
    }else{
        lcdClear();
        writeLCD("DATA NOT", "RECORDED");
    }
    }
}

lpcooling_calibration.h

#ifndef LPCOOLING_CALIBRATION_H
#define LPCOOLING_CALIBRATION_H

// Function prototypes******************************************************************************************************************
void calibration();
void handleUserInput();
void readInputValues();

#endif  // LPCOOLING_CALIBRATION_H

lpcooling_calibration.cpp

#include <string.h>

//User header files
// #include "../main.h"
#include "../lpcooling_i2c/lpcooling_i2c.h"
#include "../lpcooling_utils/lpcooling_utils.h"
#include "../globals.h"
#include "lpcooling_calibration.h"

using namespace lpcooling;

long input0Sum = 0;
long input0Sum10 [10] ;
long input0SumHigh = 0;
long input0SumLow = 0;

double input1Value = 0.0;
double input2Value = 0.0;
double input31Value = 0.0;
bool input32Value = false;

double convertedInput1 = 0.0;
double convertedInput2 = 0.0;

void calibration() {
  while (isCalibrating && digitalRead(toggleSwitchPin)){
    switch (calibrationState) {
        case CALIBRATION_IDLE:{
          // Display idle screen or instructions
          // Wait for user input to start calibration
          lcdClear();
          writeLCD("Press enter to","      start     ");
          break;
        }

        case CALIBRATION_INPUT_0:{
          // display instructions for internal reference analogue calibration
          lcdClear();
          writeLCD("CONNECT PIN 3.3V","TO PIN A3 AND PRESS ENTER TO CONTINUE");
          break;
        }

        case CALIBRATION_INPUT_0_1:{
          readInputValues();

          long sum = 0;
          for (int k = 0; k < 10; k++){
            sum += input0Sum10[k];
          }
          long average = sum/10;
          scaleFactor = CAL_VOLTAGE/average;

          lcdClear();
          writeLCD("CAL_0 COMPLETE","PRESS ENTER");
          break;
        }

        case CALIBRATION_INPUT_1:{
          // Display input 1 calibration screen
          // Read and display input 1 value in real-time
          // Allow user to adjust potentiometer for calibration
          // Wait for user to press enter button to proceed to the next input
          readInputValues();
          convertedInput1 = 4.8 * input1Value;
          lcdClear();
          writeLCD("Current: " + String(convertedInput1),"Target: 19V");
          break;
        }


        case CALIBRATION_INPUT_2:{
          // Display input 2 calibration screen
          // Read and display input 2 value in real-time
          // Allow user to adjust potentiometer for calibration
          // Wait for user to press enter button to proceed to the next input
          readInputValues();
          convertedInput2 = 4.8 * input2Value;
          lcdClear();
          writeLCD("Current: " + String(convertedInput2), "Target: 6.7V" );
          break;
        }


        case CALIBRATION_INPUT_3_1:{
          // Display input 3 calibration screen
          // Read and display input 3 value in real-time
          // Allow user to adjust potentiometer for calibration
          // Wait for user to press enter button to complete the calibration
          // readInputValues();
          // double convertedInput3 = 1.2*((2.176*(input3Value) + 2.72)/680) -4.8;
          lcdClear();
          writeLCD("Apply pressure", String(getPressureReading()) + " bar/2.3 bar");
          break;
        }


        case CALIBRATION_INPUT_3_2:{
          lcdClear();
          writeLCD("Adjust input 2","until active");
          break;
        }


        case CALIBRATION_INPUT_3_3:{
          lcdClear();
          writeLCD("Valve State: ", vState );
          break;
        }

        case CALIBRATION_COMPLETE:{
          // Display calibration complete screen or message
          // Perform any necessary actions after calibration is complete
          // wait for data to finish being written to SD card
          String calibrationTxt [] = {"CALIBRATION INFORMATION START", 
                                      "Input 1: " + String(convertedInput1) + "/19V", 
                                      "Input 2: " + String(convertedInput2) + "/6.7V", 
                                      "Pressure Sensor Reading: " + String(getPressureReading()) + "/2.3bar" } ;
          writeSDCard(calibrationTxt);
          lcdClear();
          writeLCD("Calibration Done", "Toggle Switch");
          break;
        }
      }
      // Perform any other tasks, such as handling user input, LCD updates, etc.
      handleUserInput();
      }
}

void handleUserInput() {
  // Handle user input, such as button presses, for calibration process

  switch (calibrationState) {
    case CALIBRATION_IDLE:
      // Check if user pressed start button to initiate calibration
      // Transition to CALIBRATION_INPUT_0 state
      if (currentButtonState == BUTTON_ENTER)
        calibrationState = CALIBRATION_INPUT_0;
      break;

    case CALIBRATION_INPUT_0:
      // Check if user pressed enter button to proceed to next input
      // Transition to CALIBRATION_INPUT_1 state
      if (currentButtonState == BUTTON_ENTER)
        calibrationState = CALIBRATION_INPUT_0_1;
      break;    

    case CALIBRATION_INPUT_0_1:
      // Check if user pressed enter button to proceed to next input
      // Transition to CALIBRATION_INPUT_1 state
      if (currentButtonState == BUTTON_ENTER)
        calibrationState = CALIBRATION_INPUT_1;
      break;    

    case CALIBRATION_INPUT_1:
      // Check if user pressed enter button to proceed to next input
      // Transition to CALIBRATION_INPUT_2 state
      if (currentButtonState == BUTTON_ENTER)
        calibrationState = CALIBRATION_INPUT_2;
      break;

    case CALIBRATION_INPUT_2:
      // Check if user pressed enter button to proceed to next input
      // Transition to CALIBRATION_INPUT_3_1 state
      if (currentButtonState == BUTTON_ENTER)
        calibrationState = CALIBRATION_INPUT_3_1;
      break;

    case CALIBRATION_INPUT_3_1:
      // Check if user pressed enter button to complete calibration
      // Transition to CALIBRATION_INPUT_3_2 state
      if (currentButtonState == BUTTON_ENTER)
        calibrationState = CALIBRATION_INPUT_3_2;
      break;

    case CALIBRATION_INPUT_3_2:
      // Check if user pressed enter button to complete calibration
      // Transition to CALIBRATION_INPUT_3_3 state
      if (currentButtonState == BUTTON_ENTER)
        calibrationState = CALIBRATION_INPUT_3_3;
      break;

    case CALIBRATION_INPUT_3_3:
      // Check if user pressed enter button to complete calibration
      // Transition to CALIBRATION_COMPLETE state
      if (currentButtonState ==  BUTTON_ENTER)
        calibrationState = CALIBRATION_COMPLETE;
      break;

    case CALIBRATION_COMPLETE:
      // Handle any additional user input or actions after calibration is complete
      break;
  }
}

void readInputValues() {
  // Read and update the input values for calibration

  switch (calibrationState) {

    case CALIBRATION_INPUT_0:
      break;
    

    case CALIBRATION_INPUT_0_1:{
      // calibration code
      analogReference(DEFAULT);
      for (int k = 0; k < 10; k++){
        input0Sum = 0;
        for (int j = 0; j < calibrationRepeats; j++){
          input0Sum += analogRead(input0Pin)*scaleFactor;
          delay(7);
        }
        if (input0Sum > input0SumHigh){
          input0SumHigh = input0Sum;
        } else if (input0Sum < input0SumLow){
          input0SumLow = input0Sum;
        }
        input0Sum10[k] = input0Sum ;
      }
      break;
    }
      

    case CALIBRATION_INPUT_1:
      input1Value = analogRead(input1Pin)*scaleFactor;
      // Update LCD to display input 1 value in real-time
      break;

    case CALIBRATION_INPUT_2:
      input2Value = analogRead(input2Pin)*scaleFactor;
      // Update LCD to display input 2 value in real-time
      break;

    case CALIBRATION_INPUT_3_1:
      input31Value = analogRead(pressureSensorPin)*scaleFactor;
      // Update LCD to display input 3 value in real-time
      break;


    case CALIBRATION_INPUT_3_2:
      input32Value = digitalRead(valveStatePin);
      // Update LCD to display input 3 value in real-time
      break;    
      
    case CALIBRATION_INPUT_3_3:
      // input3_3Value = analogRead(input3Pin);
      // Update LCD to display input 3 value in real-time
      break;

    case CALIBRATION_COMPLETE:
      break;

    case CALIBRATION_IDLE:
      break;
  }
}

Without looking, multiple definitions often caused by having ‘extra’ source files up in the project folder…. e.g. ‘old version.ino’

The IDE makes a tab for each valid source file - even if they’re not being used !

You have such a complicated source code module files it's hard to interpret (in those cases, better upload a single ZIP file with all the modules), anyway in this "matrioska" of headers and cpp code I suspect you have multiple "#include" of the same source (one of the libraries, I suppose, as the symbols the compiler is complaining about aren't part of your code) maybe because you don't have #ifndef .. #define on all of your header (or you should #include just the libraries you really make use in that specific module).
Just to make an example, the first errors the compiler is complaining about are substring, concat, remove, all shoud be String.h functions.

Sorry, it seems I can't tell you more than that.

I don't have a much experience using namespaces, but isn't it will a problem of including arduino's setup() and loop() in your own namespace?

It seems to be a representative of the typical Italian way of programming.

COPY/PASTA :full_moon_with_face:

Could be. I still wonder why he needs all that, with namespaces, .h, .cpp and dozens of "#include" with high chance of some badly nested/redefined includes.
My advice is to keep things simple, avoid the proliferation of C++ modules (*.h and *.cpp) unless they're modules to be used in more than a single project (in this case, make them a library instead...) and be sure to include only the libraries needed by the single specific module and use #ifndef to avoid including this again which has already been included.

I think it's more like the classic "spaghetti coding", where everything is mixed together with an excessive amout of relationships and unnecessary complications , like a "minestrone" (italian name for a mixed vegetable soup, sometimes with small pasta added). :sunglasses:

PS: BTW, yes, I'm italian but I never make "spaghetti coding"... :sweat_smile:

1 Like

Thanks for the reply I'll have a look and try and sus out where this is happening. I'm not a coder by career but like to learn if you could give some tips on how to write better code keep in mind i've added code to try and get rid of this error so it might seem too much. As a resource I've been asking ChatGPT for best practises as I code but it doesn't seem to be true.

Thanks for the reply if you had the oppertunity to be constructive would you give some general guidelines and some tips on how to write better code keep in mind i've added code to try and get rid of this error so it might seem too much. As a resource I've been asking ChatGPT for best practises as I code but it doesn't seem to be true.

My standard advice:

Keep asking the robot until he/she/it gives you the correct result you expect.

One answer could probably be 42.

If it were up to me I would ban the use of ChatGPT. It's causing more damage to inexperienced programmers than the asteroid did to the dinosaurs.
I don't know exactly what level is your programming skill, but please, avoid ChatGPT like the plague, because it is not and should not be a substitute for studying programming.

It's also pretty hard to tell you here how to start with a better programming style, with that kind of "mammoth" code.

As a general rule of thumb for those starting out in programming, unless your project grows to hundreds or thousands of code line, you can just keep starting with a single *.ino file where you'll put everything (except the libraries you are using, referenced by one or more "#include").
Then, the first step is to keep your configuration parameters inside a *.h file (e.g. "config.h") you add to your project folder, as a "configuration file" containing constants and "#define"s you need in your code, simplifying the process of cabling (you can read here the pin numbers to connect the various items like LEDs, buttons, etc), and changing something to make some fine tuning (like the default speed of a servo motor, or the threshold value for a sensor).

You could even have more than one *.ino file, or *.h files where you will store specific functions and variables to handle a portion of the project (e.g. everything for LCD display handle), but this is already a useful feature for projects grown beyond a certain size.

The last steps for a quite large project could include defining some C++ custom class to be used inside your project, and in this case you need to create *.h and *.cpp files. This can be useful both for fairly large projects, and to have class reusability in multiple projects (in this case, the class should be converted into a library).

That said, do now I hope you can see what the problem is. I think your project (at least what I can desume from the code pieces you posted here) doesn't need a structure that includes all such C++ classes. No one can say you can't do that, but it seems to me that the structure you were trying to build and manage is not something you can do by yourself if your programming skills aren't at that level, and you have just relied on ChatGPT to write the code.
That's ok using "config.h" for global configuration constants and variables, it'd even be ok using like a "lcd.h" to define everything about the LCD and so on, but my advise is to avoid namespaces, C++ classes, and so on, if you don't know exactly how they work and/or what you're doing.

What I would do if I were you is to avoid ChatGPT and start writing a program in a simpler and more linear way, learning things one step at the time.

Nope, at least unless chatGPT change its name to "Deep Thought". :sunglasses:

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.