Out of memory issue

unsigned long lastExecTime[] = {0, 0, 0};

int charCount = 0;

char param[2];

char sglLineState = 'C';  // WB EB CL

char* carActionOld[4] = { "XXXX",  "NNNN",  "NNNN" } ; // nnnnn
char* carAction[4] = { "XXXX",  "NNNN",  "NNNN" } ;

int MotorSpeedPin[] = { 0,  10,  11 } ;
int MotorADirnPin = 12;

int MotorBDirnPin = 13;
int SingleLineRelayPin = 14;
int c;  // used in //printCarSpeed


char sCurrentProc;
bool bDebugging = false ;
int carLocation[3] = {0,  0,  0 } ;

unsigned long brakeStartMillis[2];
unsigned long accelSpdChMillis[2];
unsigned long carStopMillis[2];

int carSpeed[] = { 0,  0,  0 } ;
int speedIdx[] = { 0,  0,  0 } ;

char brakingStarted[] = { 'N',  'N',  'N'}; // using bool gives strange results

int detPins[] = { 99,  8,  2,  19,  7 } ;;  // old{99,  2,  3,  5,  6}  // DETS
char detSoftwareState[]    = { 'n',  'U',  'U',  'U',  'U' } ;
char detSoftwareStateOld[] = { 'n',  'U',  'U',  'U',  'U' } ;

int detHardwareStateConfirmed[] = {1,  1,  1,  1,  1};
int detHardwareStatePrevLoop[] = {1,  1,  1,  1,  1};
int detHardwareStateOld[] = {1,  1,  1,  1,  1};
unsigned long detOccInstant[] = {0,  0,  0,  0,  0};

bool bDetActive[] = {false,  false,  false,  false,  false}; // car over det 
            // must not start w car over det
int numDets = 4;

const int speeds[] = { 0,  30,  30,  30,  30,  30}; // 6 V

const int stimes[] = {0,  300,  500,  600,  700};
bool blnTesting = false;
int iSigR = 100;
int iSigG = 100;
unsigned long detLastChangeMillis[4];

unsigned long detClrStartMillis[5];
unsigned long stopTimer1;   // car 1 at stop at W sig
unsigned long detchTime[4];


bool bDetHWchanged[] = {false,  false,  false,  false,  false};
      // char to hold input
bool Complete = false;  // whether the char is complete
int incomingByte = 0;         // for incoming //mySerial data
/* char x;
char y; */
char cmd[2];// = {'A'}; //= {}; // chars received
// this was causing out-of-memory
//char* cmd;
// signals
int wSigRpin = 3;
int wSigGpin = 6;
int eSigRpin = 9;
int eSigGpin = 5;

I have a complicated program using a UNO to control a model rail layout. If I comment out the line char cmd[2]; it loads ok (but says the variable cmd isn’t defined, of course). If I leave that line in, it says

Global variables use 2155 bytes (105%) of dynamic memory, leaving -107 bytes for local variables. Maximum is 2048 bytes.

which implies that cmd is using 107 or more bytes of memory. I don’t understand. I can comment out other variables, but then the code doesn’t work. I know there’s a host of variables defined - maybe I’m expecting too much from the UNO. I tried PROGMEM but that on;y works for constants, and I don’t have any in the code. I feel I’m missing something, but can’t see what it is. The variables defined as long need to process time intervals (millis etc)

Thanks

Please post your full sketch rather than just part of it

I thought just the variable definitions would be enough. Here it is - it’s big!

// Version 2025-08-27 using char - Uploads ok on DELL under IDE 2.3.4
//   but on laptop under 2.3.6 fails w numerous errors 
// must use strcpy to assign strings to char vars
// must use char* fro params in func decs
// //mySerial monitor set for CR
// C:\Windows\SysWOW64\regsvr32 C:\Windows\SysWOW64\MSCOMM32.ocx
// arrays are zero-based 
#include <Arduino.h>
# include <SoftwareSerial.h>
SoftwareSerial mySerial(15,  16);// RX,  TX

unsigned long lastExecTime[] = {0, 0, 0};

int charCount = 0;

char param[2];

char sglLineState = 'C';  // WB EB CL

char* carActionOld[4] = { "XXXX",  "NNNN",  "NNNN" } ; // nnnnn
char* carAction[4] = { "XXXX",  "NNNN",  "NNNN" } ;

int MotorSpeedPin[] = { 0,  10,  11 } ;
int MotorADirnPin = 12;

int MotorBDirnPin = 13;
int SingleLineRelayPin = 14;
int c;  // used in //printCarSpeed


char sCurrentProc;
bool bDebugging = false ;
int carLocation[3] = {0,  0,  0 } ;

unsigned long brakeStartMillis[2];
unsigned long accelSpdChMillis[2];
unsigned long carStopMillis[2];

int carSpeed[] = { 0,  0,  0 } ;
int speedIdx[] = { 0,  0,  0 } ;

char brakingStarted[] = { 'N',  'N',  'N'}; // using bool gives strange results

int detPins[] = { 99,  8,  2,  19,  7 } ;;  // old{99,  2,  3,  5,  6}  // DETS
char detSoftwareState[]    = { 'n',  'U',  'U',  'U',  'U' } ;
char detSoftwareStateOld[] = { 'n',  'U',  'U',  'U',  'U' } ;

int detHardwareStateConfirmed[] = {1,  1,  1,  1,  1};
int detHardwareStatePrevLoop[] = {1,  1,  1,  1,  1};
int detHardwareStateOld[] = {1,  1,  1,  1,  1};
unsigned long detOccInstant[] = {0,  0,  0,  0,  0};

bool bDetActive[] = {false,  false,  false,  false,  false}; // car over det 
            // must not start w car over det
int numDets = 4;

const int speeds[] = { 0,  30,  30,  30,  30,  30}; // 6 V

const int stimes[] = {0,  300,  500,  600,  700};
bool blnTesting = false;
int iSigR = 100;
int iSigG = 100;
unsigned long detLastChangeMillis[4];

unsigned long detClrStartMillis[5];
unsigned long stopTimer1;   // car 1 at stop at W sig
unsigned long detchTime[4];


bool bDetHWchanged[] = {false,  false,  false,  false,  false};
      // char to hold input
bool Complete = false;  // whether the char is complete
int incomingByte = 0;         // for incoming //mySerial data
/* char x;
char y; */
char cmd[2];// = {'A'}; //= {}; // chars received
// this was causing out-of-memory
//char* cmd;
// signals
int wSigRpin = 3;
int wSigGpin = 6;
int eSigRpin = 9;
int eSigGpin = 5;

//-------------------------------------------------------
                                                                             
//void writeLog(char p1 = "X", char* p2 ="Y", int p3 = 998, int p4 = 999, ){
void writeLog(char* p1 = "X", char* p2 ="Y", char* p3 = "Z", char* p4 = "W", char* p5 = "W",char* p6 = "W"){	
	Serial.print("millis: ");
	Serial.print(millis());
	Serial.print(" Proc: ");
	Serial.print(sCurrentProc);
	Serial.print(p1);
	Serial.print(p2);
	Serial.print(p3);
	Serial.println(p4);
}
void setup() {              // BEGIN SETUP
  //runTime = 0; 
  mySerial.begin(9600);

  Serial.begin(9600);
  mySerial.begin(9600);
  writeLog("Starting---------------------------------------------------------------------");
  //"119 detSoftwareState[1]", detSoftwareState[1], detSoftwareStateOld[2]);
  strcpy(sCurrentProc ,  "setup");
                    
 

  // DETECTOR PINS
  pinMode(2,  INPUT_PULLUP);  // det 2
  pinMode(7,  INPUT_PULLUP);   // det 4
  pinMode(8,  INPUT_PULLUP);   // det 1
  pinMode(19,  INPUT_PULLUP);  // det 3
  
  //sigs
  pinMode(3,  OUTPUT);  // w sig red
  pinMode(6,  OUTPUT);  // w sig grn
  pinMode(9,  OUTPUT);  // e sig red
  pinMode(5,  OUTPUT);  // e sig grn

  // motor pins
  pinMode(10,  OUTPUT);  // motor 1
  pinMode(11,  OUTPUT);  // motor 2
  
  //relay
  pinMode(SingleLineRelayPin,  OUTPUT);  // relays
  
  digitalWrite(MotorADirnPin,  HIGH);
  digitalWrite(MotorBDirnPin,  LOW);

  //digitalWrite(SingleLineRelayPin,  LOW);  // sets the digital pin 14 on - test relays - 
   //sglLineState = 'C';
   setSingleLineState('C',  "Setup - sglLineState set to C");
////delay(2000);   

// check if car over det and initialize detHardwareStateConfirmed
   for (int dxs = 1; dxs < 5; dxs++) {                   // FOR EACH DET IN CHK IF CAR OVER DET AT START
//writeLog(digitalRead(detPins[dxs]) == 0   );
     if (digitalRead(detPins[dxs]) == 0 ) {
writeLog("Car over Det: ", "", -1 , dxs);
       detHardwareStateConfirmed[dxs] = 0;
     } // if det pin 0
     else {
       detHardwareStateConfirmed[dxs] = 1;
      } // else det pin 1
   } // END FOR EACH DET IN CHK IF CAR OVER DET AT START
   
} // END setup
   // ------------------------------LOOP START
//char cmd[20];
int chCount;
int lcount;
void loop() {//...............................................VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV 

 
  strcpy(sCurrentProc ,  "loop");
  
   //chCount+= 1;
 // Serial.println(chCount, , "Now: ", chCount);
 
  //delay(5000); 
//return;
  if (bDebugging) { 
   ////delay (5000, "Running: ", sCurrentProc);
  }  // if debugging
  
  /* tLoop = millis() - tOld;
  tOld = millis(); 
  if (false) {  //tLoop != tLoopOld )  // loop time w return after this is 21,  22,  23
writeLog("143 loop time ", tLoop);  // loop is 10 ms here
  } // END IF DEBUGGING PRINT LOOP TIME
  tLoopOld = tLoop;*/

char newchar;


 if (mySerial.available()) {
//writeLog("mySerial available");
  incomingByte = mySerial.read();  
  Serial.print("221:");
  Serial.println(incomingByte);
  if (incomingByte != 13){
    
   cmd[chCount] = (char)incomingByte;
     //char* p = 
     writeLog("226: cmd[0]=", cmd[0] );
     writeLog("226: cmd[1]=", cmd[1] );
   chCount+=1;
  }
  else {
// 2025-08-25T16:25 incoming bytes disp ok in line 231 but garbage here    
writeLog("235 Got:");
    Serial.print(cmd[0]);
    Serial.println(cmd[1]);
    cmd[0] = cmd[0];
    cmd[1] = cmd[1];
    writeLog("Cmd:", cmd[0], cmd[1]);
    chCount = 0;
    cmd[0] = "";
    cmd[1] = "";
    ProcessSerial();
    //cmd = "";
  }
 }
 
  chkDets();
  chkCarAction();

}     //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^END LOOP^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^     loop


void ProcessSerial() { // VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV          BEGIN ProcessSerial
strcpy(sCurrentProc ,  "ProcessSerial");
  
 if (cmd[0] == 'Z') {
writeLog("77 Got: ", cmd[0], cmd[1]);
  }

  if (cmd[0] == 'S') {                                                        // cmd = s signal
writeLog("cmd s");
       digitalWrite(eSigGpin,  LOW);  //E SIG GN
      digitalWrite(eSigRpin,  LOW);  // W SIG RED
      digitalWrite(wSigGpin,  LOW);  // W SIG GRN
      digitalWrite(wSigRpin,  LOW);  // E SIG RED
      sglLineState = 'C';
 

//writeLog(cmd[1] == '1');
    //if (cmd[1] == '1') { digitalWrite(eSigGpin,  HIGH); } // END IF  //E SIG GN
    //if (cmd[1] == '1') { digitalWrite(wSigRpin,  HIGH); } // END IF   // W SIG RED
    if (cmd[1] == '1') { setSingleLineState('E',  "Manual cmd");}  // END IF
  
   // if (cmd[1] == '2') { digitalWrite(wSigGpin,  HIGH); } // END IF  // W SIG GRN
   // if (cmd[1] == '2') { digitalWrite(eSigRpin,  HIGH); } // END IF  // E SIG RED
    if (cmd[1] == '2') { setSingleLineState('W',  "Manual cmd");}  // END IF;}  // END IF
    if (cmd[1] == '3') { setSingleLineState('C',  "Manual cmd");}  // END IF                           // IF param == 3

      /* digitalWrite(eSigGpin,  LOW);  //E SIG GN
      digitalWrite(eSigRpin,  LOW);  // W SIG RED
      digitalWrite(wSigGpin,  LOW);  // W SIG GRN
      digitalWrite(wSigRpin,  LOW);  // E SIG RED
      sglLineState = 'C'; */
   // }                                                // END IF param == 3
writeLog("SL State: ", sglLineState);
  } // END IF cmd = s
 //printVar(255,  "cmd[0]",  cmd);
  if (cmd[0] == 'I') {  // print info               // cmd = i print info
  //https://www.google.com/search?q=ISO+C%2B%2B+forbids+converting+a+string+constant+to+%27char&oq=ISO+C%2B%2B+forbids+converting+a+string+constant+to+%27char&gs_lcrp=EgZjaHJvbWUqBggAEEUYOzIGCAAQRRg70gELNDY0MDQ4NWowajSoAgCwAgA&sourceid=chrome&ie=UTF-8
  //char str = "Hello,  world!";
    //printCarSpeed("Cmd "i"",  param,  "info".c_str());
    //printCarAction("Cmd "i"",  param,  "info");
    int carNum = cmd[1] - 48;  // also for 'b' etc
    
    //printVar(319,  "cmd[0]",  cmd);
    //printCarStatus(carNum,  "275 cmd i"); //   "cmd "i"");
  } // END IF cmd =  i print info
  
  if (cmd[0] == 'X') {  //                          // cmd = 'X' emerg stop
    
    carSpeed[1] = 0;
    carSpeed[2] = 0;
    analogWrite(MotorSpeedPin[1],  LOW);
    analogWrite(MotorSpeedPin[2],  LOW);
    strcpy(carAction[1], "NNNN");
    strcpy(carAction[2], "NNNN");
   
  }  // endif cmd = 'X' emerg stop
 
  if (cmd[0] == 'T') {  //
  
    if (cmd[1] == '1') {
      bDebugging = true;
    }
    else {
      bDebugging = false;
    }
//writeLog("305 bDebugging:", bDebugging);
  } // END IF cmd = t
  if (cmd[0] == 'P') {
    analogWrite(11,  60);
    //delay(1000);
    analogWrite(11, 12);
  }
  if (cmd[0] == 'A') {  //                          // cmd = a accelerate
    // inchar = "";
  int carNum = cmd[1] - 48;  // also for 'b' etc
    //accelSpdChMillis[carNum] = millis();              // manual cmd
    
    speedIdx[carNum] = 1; // was 0
    
    updateCarAction(carNum,  "ACCL",  "Manual cmd");
 
  } //  END IF CMD = "a"             
  
  if (cmd[0] == 'B') {                                    // if cmd = b brake
writeLog("395");
  int carNum = cmd[1] - 48;  // also for 'b' etc
  
    brakeStartMillis[carNum] = millis();  // manual b
    //strcpy(carActionOld[carNum],  carAction[carNum]);
    updateCarAction(carNum,   "BMAN",  "Manual cmd");  
    ////strcpy(fncmd[1], );
  //printCarStatus(carNum, "403 Got Manual cmd b"); // cmd b
  }                                                   // END IF cmd = b brake

  if (cmd[0] == 'D') {                                   // IF cmd = d dets
    for (int d = 1; d <= numDets; d++) {
      // THIS DOESN"T RELIES ON CHDETS BEING CALLED FROM LOOP
      // //delay(2000);
      writeLog("manual cmd - det ");
     
writeLog(" det[", d, "]: ");

writeLog(" old=", detSoftwareStateOld[d], " new=", detSoftwareState[d]);
    } // END IF  // for d
  } //                                                  // END IF cmd = d dets
  
  if (cmd[0] == 'W') {                                                                      // IF cmd = w set sigs WB
    //digitalWrite(SingleLineRelayPin,  HIGH);  // sets the digital pin 13 on - test relays - inner/car 2 WB
    //sglLineState = 'W';
    //fncmd[1] = "cmd w";
    ////strcpy(fncmd[1], );
        setSingleLineState('W',  "Manual cmd w");
  } //                                                                                  // END IF cmd = w set sigs WB
  
  if (cmd[0] == 'E') {                                                                     // IF cmd = e set sigs EB
    //digitalWrite(SingleLineRelayPin,  LOW);  // sets the digital pin 13 off i outer / car 1 EB
    //sglLineState = 'E';
    //fncmd[1] = "(cmd e";
    ////strcpy(fncmd[1], );
        setSingleLineState('E',  "Manual cmd e");
  } //                                                                                  // END IF cmd = e set sigs EB
  
  if (cmd[0] == 'C') {                                                                     // IF cmd = c set sigs CL
    //digitalWrite(SingleLineRelayPin,  LOW);  // sets the digital pin 13 off i outer / car 1 EB
    //sglLineState = 'C';
    //fncmd[1] = "cmd c";
    ////strcpy(fncmd[1], );
        setSingleLineState('C',  "Manual cmd c");
  } //                                                                                  // END IF cmd = c set sigs CL

  // end all code to process input here
  cmd[0] = 0;
writeLog("End process mySerial-------------------------------------------------------");
} //    ---------------------------------------- END PROCESS mySerial

void setSingleLineState(char PsglLineState,  char* event) {             // VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV           
 strcpy(sCurrentProc ,  "ProcessSerial"); 
  if (true){                               // IF bDebugging
writeLog( " Called By: ", event, " Sgl Line State Now: ", PsglLineState);
  }                                      // END IF bDebugging
  if (PsglLineState == 'E') {                                  // if set EB

    digitalWrite(eSigGpin,  LOW);   //E SIG GN
    digitalWrite(eSigRpin,  HIGH);  // E SIG RED

    digitalWrite(wSigGpin,  HIGH);  // W SIG GRN
    digitalWrite(wSigRpin,  LOW);   // W SIG RED
  digitalWrite(SingleLineRelayPin,  LOW);
  } //                                                        // END IF set EB
  
  if (PsglLineState == 'W') {                                 // IF set WB

    digitalWrite(eSigGpin,  HIGH);  //E SIG GN
    digitalWrite(eSigRpin,  LOW);   // E SIG RED

    digitalWrite(wSigGpin,  LOW);   // W SIG GRN
    digitalWrite(wSigRpin,  HIGH);  // W SIG RED
  digitalWrite(SingleLineRelayPin,   HIGH);
  } //                                                         // END IF set WB

  if (PsglLineState == 'C') {                                  // if set CL

    digitalWrite(eSigGpin,  LOW);  //E SIG GN
    digitalWrite(eSigRpin,  LOW);  // E SIG RED

    digitalWrite(wSigGpin,  LOW);  // W SIG GRN
    digitalWrite(wSigRpin,  LOW);  // W SIG RED
  digitalWrite(SingleLineRelayPin,  LOW);
  
  }                                  // END IF set CL
  sglLineState = PsglLineState; // added 2025-08-19 a/c not being set 
writeLog(millis());
  writeLog(" In setSingleLineState sglLineState now set to ", sglLineState);
} // // END PROC setSingleLineState
                                 
unsigned long tdiffOld;

void chkCarAction() { //VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV/ BEGIN chkCarAction
  
  
  strcpy(sCurrentProc,  "chkCarAction");
                               

 int carNum;
    /* printVar(486,  "carNum",  "1");
    printVar(487,  "carAction",  carAction[1]);
     printVar(488,  "carNum",  "2");
    printVar(489,  "carAction",  carAction[2]); */

 for (carNum = 1; carNum <= 2; carNum++) { // BEGIN for each car main loop
writeLog("Running chkCarAction Car ", carNum);
  // strncmp() function returns zero when the two strings are same up to n chars and non-zero when they are different
// IF CAR BRAKING  
    if (strncmp(carAction[carNum],  "BSIG",  4) == 0 || strncmp(carAction[carNum],  "BMAN",  4) == 0  || strncmp(carAction[carNum],  "BSTP",  4 ) == 0 ) { // 
writeLog("538 car");
      writeLog(carNum, " Action: ", carAction[carNum]);  
      //strcpy(sDoing ,  "Process Car Braking");
      // add condition else calls processCarBraking when car not in braking mode  - no - carAction not set to COST when speed 0
      processCarBraking(carNum);
  }  // END IF carAction BRAKING
     // start car after stop at sig if line clear -----------------------------------------------

    if (strncmp(carAction[carNum] ,  "LSIG",  4) == 0) {//vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvBEGIN if loading sig
   
        if (millis() - stopTimer1 > 5000) {
          //strcpy(carActionOld[carNum],  carAction[carNum]);
          updateCarAction(carNum,   "WSIG",  "Loading timer exp");

        } // END IF stop timer expired
      } // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^END IF loading sig
writeLog("445"); // debug  
// 2025-08-18T16:28 was continuing to brake at ESIG a/c 2nd condn not present
    if (strncmp(carAction[carNum], "LSTP",  4) == 0) { //vvvvvvvvvvvvvvvvvvvvvvvv     BEGIN if loading stop
 
      if (millis() - stopTimer1 > 5000) { //vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvBEGIN if stop timer expired
          //strcpy(carActionOld[carNum],  carAction[carNum]);
      writeLog("547 Car ", carNum);
          updateCarAction(carNum,   "ACCL",  "Stop timer expired");  // starting from stop not at signal
writeLog("550 Car ", carNum, " now ACCL");
      //accelSpdChMillis[carNum] = millis(); // stop timer expired
          //strcpy(fnParam[1] ,  "Start from Stop ");
          ////printCarStatus(carNum,  "Start from Stop ");
          //writewriteLog(carNum,  carAction[carNum]);
      } //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^END IF stop timer exp
    } //  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^END IF loading stop

    if (strncmp(carAction[carNum],  "WSIG",  4) == 0 ) {  // VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV BEGIN IF waiting sig
   
      if (carNum == 1) {

        if (sglLineState == 'C') {
          sglLineState = 'E';
          ////strcpy(fnParam[1] ,  );
            // set sig if car was waiting line clear
setSingleLineState('E',  "Car 1 was waiting sig"); // 565 was waiting and line now clear in chkCarAction
        }
        if (sglLineState == 'E') {
            //strcpy(carActionOld[carNum],  carAction[carNum]);
            updateCarAction(carNum,   "ACCL",  "Signal Cleared");
            ////strcpy(fnParam[1] ,   "Sig cleared");
            ////printCarStatus(1,  "Start from Stop ");
        } // END IF EB
      } // END IF car 1
      if (carNum == 2) {
        if (sglLineState == 'C') {
          //sglLineState = 'W';
          ////strcpy(fnParam[1] , );
               setSingleLineState('W',  "Car 2 was waiting sig");
        }
        if (sglLineState == 'W') {
            //strcpy(carActionOld[carNum],  carAction[carNum]);
            updateCarAction(carNum,   "ACCL",  "Signal cleared");
            ////strcpy(fnParam[1],   "Sig cleared");
            //printCarStatus(2,   "Sig cleared");
        } // END IF WB
      } // END IF car 2
    } // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^END IF WSIG

    /* if (strncmp(carAction[carNum],  carActionOld[carNum],  4) != 0 ) {

    } */

  if (strncmp(carAction[carNum],   "ACCL",  4) == 0) { // and others
    unsigned long tdiff = millis() - accelSpdChMillis[carNum]; 
writeLog("-------------------------------------------------------------");
      writeLog("614 car no. is ");  
writeLog( carNum);
    
      
      writeLog("617 tdiff: ", tdiff, " 621 prev accel ch start: ", accelSpdChMillis[carNum]);
writeLog(" 623 millis now: ", millis(), " Speedidx: ", speedIdx[carNum]);
      // was 300
  if (tdiff > 5000) { //stimes[speedIdx[carNum]] ) {  // 2025-05-21  - fixed why change it? - was stimes meant for BB?


        if (speedIdx[carNum] < 5) {
      speedIdx[carNum] += 1; 
      
            carSpeed[carNum] = speeds[speedIdx[carNum]];
      accelSpdChMillis[carNum] = millis();
      writeLog( "accelSpdChMillis now= ", static_cast<char>(accelSpdChMillis[carNum]),  " 530 speed: ", static_cast<char>(speeds[speedIdx[carNum]]));
      
      
      //digitalWrite(MotorSpeedPin[carNum], HIGH);
      analogWrite(MotorSpeedPin[carNum],  carSpeed[carNum]); // car accelertaing
            //strcpy(fnParam[1] ,  "601 accelerating");
            //printCarStatus(carNum,  "631 accelerating" );
        }
      
      if (speedIdx[carNum] == 5) {
        
        writeLog("540 speed ", "", static_cast<char>(speeds[speedIdx[carNum]]));
        //strcpy(carActionOld[carNum],  carAction[carNum]);
        updateCarAction(carNum,   "COST",  "Full speed reached");
        ////strcpy(fnParam[1] ,  "578 Max spd reached");
        //printCarStatus(carNum,  "678 Max spd reached"); ////printCarStatus doesnt" print Called By after this -
      }
      else { // not at 5 yet - covered above

      }
        } // END IF time for increment                                                       // speedIdx[carNum] <5
 
  } // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^END IF carAction == "A"
  //strcpy(carActionOld[carNum],  carAction[carNum]);
} //                FOR  carNum



} // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^END chkCarAction

void updateCarAction(int carNum,   char *sNewCarAction,  char *sReason) {
  
  
  strcpy(sCurrentProc, "updateCarAction");
writeLog(" 677 In updateCarAction: Car ", carNum, " Old Action: ", carActionOld[carNum]);
   
   strcpy(carActionOld[carNum],  carAction[carNum]);
   strcpy(carAction[carNum],  sNewCarAction);
   //2025-08-24T16:11
   if (strncmp(carAction[carNum],  "ACCL",  4) == 0) {
      accelSpdChMillis[carNum]  = millis();
      writeLog("679 in updateCarAction car ", carNum, " accelSpdChMillis= ", accelSpdChMillis[carNum]);
   }


}

void chkDets() {
	 strcpy(sCurrentProc ,  "chkdets");
writeLog("Running chkDets", "chkg dets");
  bool bdetch = false;
  
  

 
int s;
// save hw state every loop - if changed,  save change time start timer
//                            if not changed,  read timer and act if time exceeded
//                 don't need detHardwareStateConfirmed[??


for (int dc = 1; dc < 5; dc++) {       // CHANGE THIS!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!11
  // check for det stuck occ
  //bDetHWchanged[dc] = false;
  //if (bDebugging){//detSoftwareState[dc] == 'X' && millis() - detOccTime[dc] > 5000 ) {
writeLog("811 Chkng Det HW: ", dc);
    
  //}
  int s = digitalRead(detPins[dc]);
  // if (dc == 4) { //bDebugging) {
writeLog("715 Det ", dc, " HW state: ", s);
  // }
  // detHardwareStateConfirmed is set after timeout
  // detHardwareStatePrevLoop is last reading of pin
  if (s != detHardwareStatePrevLoop[dc]) { 
        
        detchTime[dc] = millis();
        bDetHWchanged[dc] = true;
        //if (dc == 4) {
          //static_cast<char>(A);
          writeLog("801 HW Det ", static_cast<char>(dc), " ch at ", static_cast<char>(detchTime[dc]), " to ", static_cast<char>(s));
// ? only when car over det 1          
/* 801 HW Det 1 ch at 81265390 to 0
801 HW Det 1 ch at 81265426 to 1     */      
        
        //}
        detHardwareStatePrevLoop[dc] = s;
        
        continue;
  }
  if (bDetHWchanged[dc]) {
writeLog("742 detHardwareStatePrevLoop=", detHardwareStatePrevLoop[dc]);
  //}
  //if(s == detHardwareStatePrevLoop[dc]) { // we know det hasn't changed here if bDetHWchanged is false
  
  //det HARDWARE state not changed  how long in this state?
                                        // avoiding bounce?
                                        
    //detHardwareStatePrevLoop[dc]  = s;                                  
    unsigned long diff = millis() - detchTime[dc];
    if (dc == 4) {

    }
    if (bDebugging) {
writeLog("635 detchTime ", static_cast<char>(detchTime[dc]));
      }
      
    if (diff < 150) { // TEST!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!  2000 too long to det blue car at 4
writeLog("864   ");
      continue;
    }

    
    if (dc == 2) {

    }
    if (bDebugging) {
writeLog("647 detchTime ", static_cast<char>(detchTime[dc]));
      }
      
    /* if (diff < 200) { // TEST!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!  2000 too long to det blue car at 4
      // not timeout yet
      //continue;
    } */

    //bDetHWchanged[dc] = false;
    
    if (s == 1) { 
      detSoftwareState[dc] = 'U';
    }
    else {
      detSoftwareState[dc] = 'X';
    }
  
    if (detSoftwareStateOld[dc] != detSoftwareState[dc]) { // so no debug during timeout
writeLog(" In chkDets: Det ", dc,  " software now ", detSoftwareState[dc]);
      
writeLog("D", dc, "=", detSoftwareState[dc]);
      
      
      processDetChange(dc);
      detSoftwareStateOld[dc] = detSoftwareState[dc];
    }
//  }

  }  // if hardware state unchanged
      //detHardwareStateConfirmed[dc] = s; // set this every time to avoid multiple reports of same state

  } // for dc
} // chkdets

void processDetChange(int detChanged) {
strcpy(sCurrentProc, "processDetChange");
// ACT ON CHANGED DET
bool bDetCh = false;
  //int dx = 1;
  // already reported in chkDets
writeLog(" In processDetChange detChanged = ");
  writeLog(detChanged, " Now ", detSoftwareState[detChanged]);
  
  if (detChanged == 1) {

    bDetCh = true;
  
  if (detSoftwareState[detChanged] == 'X') {
    updateCarAction(1,  "BSTP",  "processDetChange: Det occ");
    
       strcpy(carActionOld[1] ,  carAction[1]);
      strcpy(carAction[1] ,  "BSTP");
      carLocation[1] = 1;
writeLog("In processDetChange: Car: 1 Location: 1");
    }  
    if (detSoftwareState[detChanged] == 'U') {
        //strcpy(sglLineState,   'C');
        
        setSingleLineState('C',  "Det 1 clr");
    }  
    
  } // end dx  = 1
    
 
 if (detChanged == 4) {
  

    bDetCh = true;
// DET 4 CH  
  if (detSoftwareState[detChanged] == 'X') {
writeLog("In processDetChange 946: Car: 1 @ Det 4");
    updateCarAction(1,  "BSIG",  "processDetChange: Det occ");
       strcpy(carActionOld[1] ,  carAction[1]);
      strcpy(carAction[1] ,  "BSIG");
      carLocation[1] = 4;
      
    if (sglLineState == 'C') {
      setSingleLineState('E',  "Car 1 at Det 4"); // 953 in processDetChange
     // strcpy("carAction
    }
    else {
    //strcpy     
     writeLog("Car 1 waiting line clear");
    }
    }  
 
    
  } // end dx  = 4
    
//----------------------------------------------END 1 & 4---------------------------
 //dx = 3;          //------------------------- DET 3
 // if (detSoftwareState[detChanged] != detSoftwareStateOld[detChanged]) {
  if (detChanged == 3) {
    //detChangedTime[detChanged] = millis();
    bDetCh = true;
    //strcpy(fnParam[1] ,  "Det 3 changed:");
    //printDetState(dx,  "Det 3 changed:"); 
    
    strcpy(detSoftwareStateOld[detChanged],  detSoftwareState[detChanged]);  // MOVE TO END OF THIS PROC???????????????????????????????////
    //if (bDebugging) {
writeLog("973 Det 3 SW now ", detSoftwareState[detChanged]);
    // }
      
    if (detSoftwareState[detChanged] == 'X') {
    updateCarAction(2,  "BSTP",  "Det occ");
    
      strcpy(carActionOld[2] ,  carAction[2]);
      strcpy(carAction[2] ,  "BSTP");
      carLocation[2] = 3;
writeLog("Car: 2 Location: 3");
      //strcpy(fnParam[1] ,  "Det 3 occ");
    //printCarStatus(2 ,  "934 Det 3 occ");
    }  
    
    if (detSoftwareState[detChanged] == 'U') {
        //strcpy(sglLineState,   'C');
        ////strcpy(fnParam[1] ,  "Det 3 clr");
        setSingleLineState('C',  "processDetChange: Det 3 clr");
    }  
    
   
} // end det 3  
 
  if (detChanged == 2) {
    //detChangedTime[detChanged] = millis();
    bDetCh = true;

   
  if (detSoftwareState[detChanged] == 'X') {
       // car 1 outer approach   // STAGE 2
writeLog("In processDetChange: Car: 2 @ Det 2"); 
    updateCarAction(2,  "BSIG",  "Det occ");
 
      carLocation[2] = 2;
writeLog("Car: 2 Location: 2");

      if (sglLineState == 'C') {
 
        setSingleLineState('W',  "Car 2 at det 2");
    }
    
        else {
    //strcpy     
     writeLog("Car 2 waiting line clear");
 
      } // END IF
    } // det occ
   }// end det 2 changed

}

void processCarBraking(int carNum) {
strcpy(sCurrentProc, "chkCarAction");
unsigned long tdiff;
unsigned long tNow = millis();

// with 12 V and max speed = 9 - both cars w/o trailer
      tdiff = 300; // default 300 car 2 stops immed clear of det 3 but max speed was ? 10

    
    // Set braking speed-change intervals - see next
  if (carNum == 1 && carLocation[carNum] == 4) { // SL ENTRY
    //tdiff = 275; // 2025-05-13 to //delay initial BB was 1000 b4 tk clng 2025-05-16
  }
  else if (carNum == 1 && carLocation[carNum] == 1) { // SL EXIT
    //tdiff = 475;
  
  }    // if car 1 at det 1
  
// Initiate braking    
  if (brakingStarted[carNum] == 'N' ) {
    brakeStartMillis[carNum] = tNow;
writeLog("1115 braking started");
    
    brakingStarted[carNum] = 'Y';
writeLog(" In processCarBraking: Braking started for car ", carNum);
    speedIdx[carNum]-= 1; //  init first braking step
    //continue; // next car
    return;
  } // end if brakingStarted = N
  // HERE IF BRAKING HAS STARTED
    // CHANGE SPEED IN BRAKING
writeLog(" In processCarBraking car ");
  writeLog(carNum, "Braking started");
    if (tNow - brakeStartMillis[carNum] > tdiff) {   // BEGIN IF TIMER EXPIRED - 1st speed redn

    writeLog( " 833 processCarBraking Car ", static_cast<char>(carNum), " Speed: ", static_cast<char>(speeds[speedIdx[carNum]]));
    
        if (speedIdx[carNum] > 0) {       // BEGIN IF SPEED > 0 
          speedIdx[carNum] -= 1;
          carSpeed[carNum] = speeds[speedIdx[carNum]];
          //printCarSpeed(416,  carNum,  "BB speed dec");
    
    
          analogWrite(MotorSpeedPin[carNum],  carSpeed[carNum]);  // set speed BB 
          brakeStartMillis[carNum] = millis();  // reset BB timer was cmtd out - why??
          ////strcpy(fnParam[1] ,  "431 Now BB");
          //printCarStatus(carNum,  "BRAKING" ); // BB speed < 5
          
//if (tdiff != tdiffOld) {  
writeLog("tdiff old : ", tdiffOld);  
writeLog(" tdiff now : ", tdiff);

tdiffOld = tdiff;
writeLog("tdiffold now : ", tdiffOld);
//}  
writeLog("565: ", millis());          
          
        } // END  CHANGE SPEED IN BRAKING
        
        
        else {        // ELSE car stopped: speedIdx[carNum] = 0  
    brakingStarted[carNum] = 'N';
writeLog(" 1149 In processCarBraking: Car ");    
writeLog(carNum, " now stopped");
         
    
          
          if (strncmp(carAction[carNum],  "BSIG", 4) == 0) {   //  BEGIN IF BSIG
     
            //strcpy(carActionOld[carNum], carAction[carNum] );
            updateCarAction(carNum,   "LSIG",  "Braking finished");
            // don"t need cmt - actionold printed in printCarAction
            //printCarAction("",  carNum,  "");
            ////strcpy(fnParam[1] ,  "now loading sig");
            //printCarStatus(carNum,  "550 now loading sig"); // loading sig
writeLog("1154 LSIG Car ", carNum);
      
      if (carNum == 1) {
        //carLocation[carNum] = 4 ;
        
      }
              stopTimer1 = millis();
        
        writeLog(" Location ", carLocation[carNum]);
        } //                                      //  END IF BS

            // restart car from stop not at sig - need to
            // 2025-04-12 why if carNum == 1???? condition removed
         
        if (strncmp(carAction[carNum],  "BSTP",  4) == 0) {    //  BEGIN IF BSTP
    
              //carLocation[carNum] == "4 BB")
              //strcpy(carActionOld[carNum],  carAction[carNum]);
              updateCarAction(carNum,   "LSTP",  "Braking finished");
              //printCarAction("",  carNum,  "");
              //carLocation[carNum] = 4;
              stopTimer1 = millis();
              ////strcpy(fnParam[1],  "now BB stop");
              //printCarStatus(carNum,  "1189 now BB stop");
            } //                                            END IF BP
writeLog("417"); // debug   
            if (strncmp(carAction[carNum], "BMAN",  4) == 0) {  // manual cmd - inclu above      //  BEGIN IF BB MANUAL
            //strcpy(carActionOld[carNum],  carAction[carNum]);
            updateCarAction(carNum,   "NNNN",  "Braking manual");//, "1193 carAction set to NNNN");
 
            } //                                                                         END IF BB MANUAL

          }  // END ELSE CAR STOPPED

} // END IF BRAKE TIMER EXPIRED
  
}

Just a casual observation…

You are creating a number of ‘disconnected’ or unrelated arrays - which may be better served by a structure, than a single array of that structure.

That will make your code easier to read, maintain, expand and debug,

It does not compile when the line is commented out:

test.ino:185:4: error: 'cmd' was not declared in this scope
test.ino:194:18: error: 'cmd' was not declared in this scope
test.ino:216:6: error: 'cmd' was not declared in this scope
...

So the following does not hold.

Looks like I better buy a Mega. What model do I need - there seems to be a wide range of prices depending on the model.

Thanks

There is only one Mega. You're possibly best of with an original (though they are relatively expensive).

At least you will not have issues with boards that have fake CH340 chips.

You can save RAM by using the F() macro; you'll have to rewrite some functions (at least the writeLog) to use __FlashStringHelper.

Serial.print("millis: "); can e.g. be changed to Serial.print(F("millis: "));

Things like this are expensive on RAM

You'll need to rewrite writeLog to be able to make use of the F() macro in the calls.

Also you can buy a ESP32 that costs the same as and uno but has much more memory.

This is a very basic example using writeLog() that only takes one argument.

/*
Using RAM
Sketch uses 1550 bytes (4%) of program storage space. Maximum is 32256 bytes.
Global variables use 264 bytes (12%) of dynamic memory, leaving 1784 bytes for local variables. Maximum is 2048 bytes.

Using F() macro and __FlashStringHelper
Sketch uses 1560 bytes (4%) of program storage space. Maximum is 32256 bytes.
Global variables use 188 bytes (9%) of dynamic memory, leaving 1860 bytes for local variables. Maximum is 2048 bytes.

*/

void writeLog(const char* p1 = "X")
{
  Serial.println(p1);
}

void writeLog(const __FlashStringHelper *p1)
{
  Serial.println(p1);
}

void setup()
{
  Serial.begin(115200);

  writeLog("End process mySerial-------------------------------------------------------");
  //writeLog(F("End process mySerial-------------------------------------------------------"));
}

void loop()
{
}

Code as shown results in

Sketch uses 1550 bytes (4%) of program storage space. Maximum is 32256 bytes.
Global variables use 264 bytes (12%) of dynamic memory, leaving 1784 bytes for local variables. Maximum is 2048 bytes.

If you disable the first call to writeLog() in setup() and enable the second one the result is

Sketch uses 1560 bytes (4%) of program storage space. Maximum is 32256 bytes.
Global variables use 188 bytes (9%) of dynamic memory, leaving 1860 bytes for local variables. Maximum is 2048 bytes.

You save 76 bytes of RAM.

I did not study your code in depth; to play it safe you should limit the RAM usage to roughly 1600 bytes (on an Uno).

It's a sore to the eyes, i was happy when i scrolled passed it. But the F() macro should really fix the issue.

You are doing a lot of strcpy and strcmp to store and compare fixed texts. Since part of your variables are already char*, it would be a lot easier to just change the pointer instead of changing the text, then do a straight comparison to see if the pointers match. Would save a lot of code and time, since moving a char array is a lot slower than changing a pointer.

strcpy and strcmp have versions that use PROGMEM, but I don't think that is needed with this code, if you instead just manipulate char*'s.

< edit >
You have mySerial.begin() twice in setup.

Before you go any further, start fixing the important warnings important. You probably don't see them at the moment; you can make them visible by setting Compiler warnings to All in File → Preferences and you probably don't know what the important ones are.

C:\Users\bugge\OneDrive\Documents\Arduino\1_forum.arduino.cc\1404496_OutOfMemory\1404496.improved\1404496.improved.ino: In function 'void ProcessSerial()':
C:\Users\bugge\OneDrive\Documents\Arduino\1_forum.arduino.cc\1404496_OutOfMemory\1404496.improved\1404496.improved.ino:226:39: warning: invalid conversion from 'char' to 'char*' [-fpermissive]
   strcpy(sCurrentProc, "ProcessSerial");

sCurrentProc is a character, not a c-string.

or

C:\Users\bugge\OneDrive\Documents\Arduino\1_forum.arduino.cc\1404496_OutOfMemory\1404496.improved\1404496.improved.ino:89:6: note:   initializing argument 1 of 'void writeLog(char*, char*, char*, char*, char*, char*)'
 void writeLog(char* p1 = "X", char* p2 = "Y", char* p3 = "Z", char* p4 = "W", char* p5 = "W", char* p6 = "W")
      ^~~~~~~~
C:\Users\bugge\OneDrive\Documents\Arduino\1_forum.arduino.cc\1404496_OutOfMemory\1404496.improved\1404496.improved.ino:416:20: warning: ISO C++ forbids converting a string constant to 'char*' [-Wwrite-strings]
   writeLog(millis());

The first argument of writeLog() is a pointer to a character (character array); you should not pass an unsigned long.

And there probably is a lot more, I did not dig through the warnings.

Question
How did you get to this code? Did it ever work (in a previous version)?

Thanks for the work you’ve done to help me. The code worked ok but I decided to write a function (writeLog) to which I could pass a number of parameters, instead of using multiple Serial.print commands - because you can’t print more than one item in each print command, so it got a bit tedious whenever I wanted to change what I was writing to the log. By the way, I’m actually writing the logging data to my PC running a Visual Basic program. I think I’ll just revert to the way I was doing it before.

Not quite true

Take a look at the sprintf() and snprintf() functions. One line of code to put the data in a buffer and a second one to print the buffer contents

Thanks. I’ll try that.

A couple of things about sprintf(), there is a version sprintf_P() that uses PROGMEM for the format string, and a string argument stored in PROGMEM uses %S instead of &s, in which case you use PSTR() instead of F() for text literals.

Memory issues can be so tricky to debug. It often comes down to how variables are being declared and used. A good practice is to check for any large global arrays or strings that might be taking up a lot of space. Also, using the F() macro for constant strings can save a significant amount of dynamic memory. It's usually a process of elimination to find the source, but these two tips are great places to start.

Based on IDE 2.3.6, Allow Serial.print(); Serial.print(); on same line - #12 by J-M-L you can bring the RAM usage down to 761 bytes by consistently using the F() macro.

I did rename the functions from debug to writeLog as shown below.

// renamed debug to writeLog
void writeLog()
{
  Serial.println();
}

template<typename T, typename... Args>
void writeLog(T first, Args... args)
{
  Serial.print(first);
  Serial.write(' ');
  writeLog(args...);
}

and removed your existing writeLog.

The above makes use of the standard Serial.print() and recursion (explanation IDE 2.3.6, Allow Serial.print(); Serial.print(); on same line - #22 by J-M-L)

Example of modified call to the new writeLog

      writeLog(F("Car over Det: "), F(""), -1, dxs);

Note:
I can not 100% explain it (because of the template).

Did you get my explanation from the other post or was that too complicated ?

Yes.

I'm a bit stuck with template<typename T, typename... Args>.
I think that you define a template with a name T and (a list of) arguments of that type called Args.

The other thing is the recursion. Why is the first argument from the list removed after / during each call? Unless that is how variadic actually works.