Reducing Code Size to Keep Code Functional

I am making an Ethernet temperature logging module that uses an RTD temperature sensor and amp, a thermocouple and amp, and an analog TMP36 sensor and sends UDP packets to a master when asked. It also creates a webpage of its history for who ever logs into the IP of the device. The issue is my code takes up roughly 90% of the Uno's program storage and 71% of its dynamic memory. This sometimes causes code instability...

Does anyone have any suggestions that could reduce my code size at all? Criticism is welcome. Thanks!

My code is as follows:

#include <Adafruit_MAX31856.h>
#include <Adafruit_MAX31865.h>
#include <SPI.h>
#include <SD.h>
#include <Ethernet2.h>
#include <EthernetUdp2.h> 

//Thermocouple Amplifier Config
Adafruit_MAX31856 thermoAmp = Adafruit_MAX31856(6,7,8,9); //Creates the thermocouple instance with the pins in order: CS, DI, DO, CLK

//RTD Amplifier Config
Adafruit_MAX31865 rtdAmp = Adafruit_MAX31865(5,7,8,9);    //Creates the RTD instance with the pins in order: CS, DI, DO, CLK

//Ethernet Set-Up:
byte mac[] = {0x2C, 0xF7, 0xF1, 0x08, 0x04, 0x3C};  //The MAC Address on the Ethernet Card
IPAddress ip(172, 20, 50, 120);                           //The static IP of this device - Only used if DHCP server doesn't assign it an IP
EthernetServer server(80);                                //The port that the webserver will run on (Default: 80)
unsigned int localPort = 8888;                            //The port that the UDP server listens on (Default: 8888)
const char* serverIP = "172.20.50.200";                   //The IP Address of the computer with the server program running

//Buffers for receiving and sending data
char packetBuffer[12];                                    //Buffer to hold incoming packet - Set to 12 bytes, max is 24 bytes.
char ReplyBuffer[] = "Error";                             //Buffer to send data back in


//EthernetUDP instance to let us send and receive packets over UDP
EthernetUDP Udp;

//Create the variables that will hold the data read from the AUX ports, set them to 0 initially
float aux1 = 0.0, aux2 = 0.0, aux3 = 0.0, aux4 = 0.0;     


//The setup code is ran only one time when the arduino is first plugged in or the reset button is pressed
void setup() {

  //Start the RTD amplifier instance
   rtdAmp.begin(MAX31865_3WIRE);

  //Start the thermocouple amplifier instance and send the "K type" command to the chip
  thermoAmp.begin();
  delay(50);
  thermoAmp.setThermocoupleType(MAX31856_TCTYPE_K);
  
  //Start the Ethernet, webserver, and UDP instances:
   if (Ethernet.begin(mac) == 0) {
    //Try to congifure the Arduino Ethernet card using a static IP address instead of DHCP:
    Ethernet.begin(mac, ip);
  }
  Udp.begin(localPort);
  server.begin();

  //Wait .5 seconds for external boards to initialize
  delay(500);

  //Send a message to the server saying that this slave is online
  Udp.beginPacket(serverIP, localPort);
  Udp.write("Online");
  Udp.endPacket();

  //SD Card Set-Up: Make the chip select pin an output (Pin 4 is hardwired to be the SD Card CS pin)
  pinMode(4, OUTPUT);

  //SD Card Set-Up: Check if there is an SD Card present
  SD.begin(4);
}

void loop() {

  //Check if there's UDP data available - read the packet
  int packetSize = Udp.parsePacket();
  
  //Check for incoming webserver clients 
  EthernetClient client = server.available();

  //If there is a packet availible then handle it
  if (packetSize) {
    handleUDP();
  }

  //If there is a webserver client availible then handle it
  if (client) {
    handleWebserverClients(client);
  }

  delay(10);
}


void handleUDP() {
  
    //Read the packet into packetBufffer
    Udp.read(packetBuffer, 12);
    String request = String(packetBuffer);

    
      if(request.startsWith("tmp",0)){
        //Send the temperature to server 
        Udp.beginPacket(serverIP, localPort);
        String stringReply = String(String(((thermoAmp.readThermocoupleTemperature()*1.8) + 32)) + ", " + ((rtdAmp.temperature(100, 430.0)*1.8)+32));
        stringReply.toCharArray(ReplyBuffer, 50);
        Udp.write(ReplyBuffer);
        Udp.endPacket();
        writeToLogFile(stringReply);
      }else if(request.startsWith("aux",0)){
        //Clear the SD card and send a completed note back to the master
        handleSwitchModes(); //Updates aux1 through aux3 so we can send the data back
        Udp.beginPacket(serverIP, localPort);
        String stringReply = String("" + String(aux1) + "," + aux2 + "," + aux3 + "," + aux4);
        stringReply.toCharArray(ReplyBuffer, 50);
        Udp.write(ReplyBuffer);
        Udp.endPacket();
      }else if(request.startsWith("tim",0)){
        //Send the runtime to server in millis
        Udp.beginPacket(serverIP, localPort);
        String stringReply = String(millis() / 60000) + "min";
        stringReply.toCharArray(ReplyBuffer, 50);
        Udp.write(ReplyBuffer);
        Udp.endPacket();
      }else if(request.startsWith("clr",0)){
        //Clear the SD card and send a completed note back to the master
        Udp.beginPacket(serverIP, localPort);
        String stringReply = String(((SD.remove("log.txt") && SD.open("log.txt", FILE_WRITE)) ? "Done" : "Failed"));
        stringReply.toCharArray(ReplyBuffer, 50);
        Udp.write(ReplyBuffer);
        Udp.endPacket();
      }
}

void handleWebserverClients(EthernetClient client) {
    // an http request ends with a blank line
    boolean currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        if (c == '\n' && currentLineIsBlank) {
          // send a standard http response header
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println("Connection: close");
          client.println();
          client.println("<!DOCTYPE HTML>");
          client.println("<html><pre>");

          //Open the log file for reading:
          File myfile = SD.open("log.txt");
          if (myfile)
          {
            //Read all the text written on the file
            while (myfile.available()) 
            {
              client.write(myfile.read());
            }
            // close the file:
            myfile.close();
          } else {
            // if the file didn't open, report an error:
            client.println("Error opening the data file! Try cycling the power of the Arduino or take the SD card out, put in back in, and try again...");
            //The SD card may have been removed and inserted again so try to reload it
            SD.begin(4);
          }
          
          client.println("</pre></html>");
          break;
        }
        if (c == '\n') {
          // you're starting a new line
          currentLineIsBlank = true;
        }
        else if (c != '\r') {
          // you've gotten a character on the current line
          currentLineIsBlank = false;
        }
      }
    }
    // give the web browser time to receive the data
    delay(1);
    // close the connection:
    client.stop();
}

void writeToLogFile(String dataToWrite) {
  File myfile = SD.open("log.txt", FILE_WRITE);
  
  if (myfile)
  {
    myfile.print(millis() / 1000); //Print seconds since start
    myfile.print(": ");
    myfile.println(dataToWrite);   //Print the data
    myfile.close();                //close the file:
  } else {
    //If the file didn't open, report an error:
    //The SD card may not have initialized - try again
    SD.begin(4);
  }
}

void handleSwitchModes() {

  //Check if the switch is in the "ON" position
  if(digitalRead(3) == HIGH) {
    
    //Set up the AUX ports for a single Analog Temperature Device (TMP36)
    //Set A0 pin to output 5v, set A1 pin input, and set A2 to 0v(low)
    pinMode(A0, OUTPUT);
    pinMode(A1, INPUT);
    pinMode(A2, OUTPUT);
    digitalWrite(A0, HIGH);
    digitalWrite(A2, LOW);

    //Convert the analog signal on pin A1 to millivolts
    float voltage = analogRead(1) * 5.0;
    voltage /= 1024.0;
    

    //Read the temperature from analog pin 3 - https://learn.adafruit.com/tmp36-temperature-sensor/using-a-temp-sensor
    aux4 = 0.0;
    aux3 = 0.0;
    aux2 = (((voltage - 0.5)*100)*1.8) + 32;
    aux1 = 0.0;
    
  }else{
    
    //Set up the AUX ports to read analog signals in
    pinMode(A0, INPUT);
    pinMode(A1, INPUT);
    pinMode(A2, INPUT);
    pinMode(A3, INPUT);

    //Read the temperature from the analog pins
    aux4 = analogRead(A3);
    aux3 = analogRead(A2);
    aux2 = analogRead(A1);
    aux1 = analogRead(A0);
  }
}

I'd ditch String, and make better use of the F() macro.

I'd define the 'file' global to ensure the buffer stays the same spot in memory

If it is as you say unstable, then the use of String is probably the cause. Use c strings.

I don't see an obvious way to reduce the program memory use (fortunately you can go all the way to 100% without issue - you don't need to keep some open) - you're using a lot of fairly bulky libraries, and 32k isn't all that much flash. For connected applications, a larger (in terms of flash and ram) microcontroller is often more suitable. The Atmega 1284p (used in many third-party boards) and 2560 (ex, Mega2560) come to mind if you're staying with 8-bit AVRs

Thanks for the answers guys! I used F() where I could (atleast the way I think you wanted me to, AWOL)

I also made myFile global like Knut_ny said.

I am not sure what you mean by "Use c strings" DrAzzy, any more information there?

Your variable 'packetBuffer' looks like an example of a 'C' string -- an array of 'char' terminated by '\0'.

Edit:

Helicopter12:
I am not sure what you mean by "Use c strings" DrAzzy, any more information there?

Don't use the C++ String class. Use nul-terminated char arrays, the way God intended.

To do this, you will need to familiarise yourself with the libc standard library functions.