Program for Camera Slider/Stepper Motor does'nt work if too big (90%)

So i'm doing a project rn for an institut that works on speaker technology (i'm just a type of intern that just finished school) . And a collegue of mine needed to control a Camera Slider (to do measurements with a Speaker on top of the slider moving back and forth to a microphone and playing a noise), because the remote control that came with the slider wasn't sufficient enough for the precision she needed.

So I wrote a program on an Arduino Uno with an Ethernet Shield 2 on top, wired to a TMC2208 V3 and Limit Switches on each side of the Slider to:
*Calibrate at the beginning with the limit switches (Setup)
*then wait and listen for a UDP Packet (Loop)
*extract 4 different Variables from the packet (startPoint, lengthToDrive, timeToDrive, HowManyTimes)
*and also give a lot of information about how long it took and obviously the IP etc on the Serial monitor during the whole program
And it worked great.... it uses 68% Program Space and 29% Global Variables.

But then i wanted to replace the Serial Monitor with an external OLED and also send the most important information back as an UDP-Reply, so you didnt need to connect the Arduino to a PC but just plug it into the wall. So i put a 128x64x-SSD1306-OLED-Screen on the Arduino as well and as you could imagine the libraries for an OLED aren't really small and it used up a lot of Space (up to 90%) and it didnt work with the whole code, but when i commented out the big loop part with receiving the UDP-Packet and such to remove quite some space, the Setup worked fine with displaying everything on the OLED and also driving to the Limit Switches.
Ofc i wanted it to work completely so i looked up what could cause instability and the first thing i fixed was using Arduino-Strings for the OLED Function and switched my whole system to C-Strings, which seemed to work at first with getting further in the program, but it mainly just reduced Program Space and ended up not working again. I really tried a lot of methods of reducing space but i dont know how to get it smaller.
But based on things people said on another forum, i thought it shouldnt be a problem being almost full and it should work fine with 99% full, so i dont know at all what could be wrong with my Program.
I mainly used this example for UDP Communication and this for the OLED.
Here's the Code:

//Libraries for StepperDriver, Ethernet and OLED (SSD1306)
#include <SPI.h>
#include <Wire.h>
#include <BasicStepperDriver.h>
#include <Ethernet.h>
#include <EthernetUdp.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

//Constants for Motor Object, OLED Object and Arduino Pins
#define MOTOR_STEPS 200 
#define MICROSTEPS 8
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define LSwitch1 6
#define LSwitch2 7
#define DIR 2
#define STEP 3

//Ethernet Constants
byte mac[] = {0xA8, 0x61, 0x0A, 0xAE, 0x74, 0x88};
unsigned int localPort = 8888;

//Global Variables
float distance = 0.0, RPM = 60.0; 
int limit = 0;

//Defining Objects 
BasicStepperDriver slider(MOTOR_STEPS, DIR, STEP);
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
EthernetUDP Udp;

//C-Strings for UDP Reply
char packetBuffer[UDP_TX_PACKET_MAX_SIZE];
char fail1[44] = "Sent Distance too high, waiting for new UDP";
char fail2[38] = "Sent Distance <0, waiting for new UDP";
char fail3[34] = "RPM too high, waiting for new UDP";
char fail4[44] = "Sent Distance/RPM == 0, waiting for new UDP";
char fail5[31] = "Time <= 0, waiting for new UDP";
char packetReply[21] = "Received The Packet!";
char timeReply1[48] = "The Time it took to move the sent Distance was:";
char timeReply2[10], timeReply[60];

//C-Strings for OLED
char nuli[12] = "OLED Online",              dhcp[13] = "!!Ethernet!!";
char settingup[14] = "Setting up...",       startisset[19] = "Start is set to 0.";
char limitchar1[8] = "Limit: ",              limitchar2[6],  limitchar[15];
char distchar1[11] = "Distance: ",           distchar2[6],   distchar[18];
char setdone[16] = "Setup Finished!";
char startpos[18] = "Going to Start...",    tensecs[22] = "Starting in 10secs...";
char startmeas[21] = "Starting Measurement",startnow[5] = "NOW!";
char measdone[18] = "Measurement done!",    backinten[22] = "Going back in 10secs";
char finished[15] = "It's finished!",       newpacket[19] = "Ready for new UDP.";
char steady[12] = "Wait 20secs"; //(For Steady Measurements)

//Function for Displaying a C-String on an OLED
void OLED (char text[], int x, int y, int z, bool dub) {
  if(dub == true){
    display.clearDisplay();
  }
  display.setTextSize(z);
  display.setCursor(x, y);
  display.println(text);
  display.display();
}

//Function for Displaying IP Adress, LocalPort and Driveable distance constantly on the OLED
void OLEDconst(){
  const char ip[4] = "IP:", prt[6] = "Port:";
  display.clearDisplay();
  display.setTextSize(1);
  display.setCursor(0,0);
  display.println(ip);
  display.setCursor(20,0);
  display.println(Ethernet.localIP());
  display.setCursor(0,10);
  display.println(prt);
  display.setCursor(30,10);
  display.println(localPort);
  display.drawLine(0,20,127,20,WHITE);
  display.setCursor(0,50);
  display.println(distchar);
  display.display();
}

//Function for Replying a C-String to the IP and Port the Packet came from
void Reply(char rep[]) {
    Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
    Udp.write(rep);
    Udp.endPacket();
}

//Function for turning the StepperMotor while keeping track of timing and sending a Reply with the time it took
void moving (int cm, bool replying) {
    int moves = 0;
    moves = cm / 0.003745;
    
    long currentTime = millis();
    slider.move(moves);
    long newTime = millis()-currentTime;
    
    if(replying == true) {
      itoa(newTime, timeReply2, 10); strcat(timeReply, timeReply1); strcat(timeReply, timeReply2);
      Reply(timeReply);
    }
}

//Function for delay, with using millis() instead of delay()
void waitMillis (long delaymillis) {
  long newMillis = millis();
  while (true) {
    if (millis() - newMillis >= delaymillis) {
      return;
    }
  }
}


void setup() {
  //Limit Switches
    pinMode(LSwitch1, INPUT_PULLUP); pinMode(LSwitch2, INPUT_PULLUP);
  //Begin OLED Object
    if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    for(;;);
    }
    display.display();
    waitMillis(2000);
    display.clearDisplay(); display.setTextColor(WHITE);
    OLED(nuli,30,30,1,true);
    waitMillis(2000);
  //Begin Ethernet Object
    if (Ethernet.begin(mac) == 0) {
      OLED(dhcp,5,30,1,true);
        for(;;)
        ;
    }
  //Begin UDP and Slider Objects
    Udp.begin(localPort);
    slider.begin(RPM, MICROSTEPS);

  //Begin Setup for Slider
    OLED(settingup,0,15,1,true);
  //Find the first Limit Switch
    while (true) {
      if (digitalRead(LSwitch1) == LOW) {
          slider.move(200);
          OLED(startisset,0,30,1,false);
          waitMillis(500);
          break;
      }else {//Move while Limit Switch isn't pressed
          slider.move(-1); delayMicroseconds(500);
      }
    }
  //Find the second Limit Switch
    while (true) {
      if (digitalRead(LSwitch2) == LOW) {
          slider.move(-200); //Slider moves back to give space for Limit Switch
          limit = limit - 200;
          distance = limit * 0.003745; //Calculates Distance based on Analog Measurements
          //Add the C-Strings to include the integer variables and give out on OLED
          itoa(limit, limitchar2, 10); strcat(limitchar, limitchar1); strcat(limitchar, limitchar2);
          OLED(limitchar,0,40,1,false);
          itoa(distance, distchar2, 10); strcat(distchar, distchar1); strcat(distchar, distchar2);
          waitMillis(500);
          slider.move(-limit);
          break;
      }else {//Move while Limit Switch isn't pressed
          slider.move(1); delayMicroseconds(500);
          limit++; //Count Limit Variable up each step
      }
    }
    OLED(setdone,0,30,1,false); 
    OLEDconst();
    waitMillis(1000);
}
void loop() {
    int packetSize = Udp.parsePacket();
    const char k[2] = ","; char *token;
    int lengthCM = 0, rounds = 0, Time = 0, CMx = 0, c = 1, startPoint = 0;
    char RPMdata[10];
    if (packetSize) {//If packet is available
    //is supposed to be a UDP Packet containing:("startPoint","lengthToDrive","timeItShouldTake","amountOfTimesToDrive") Ex.: 20,80,30,2 
      Udp.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE); 
      token = strtok(packetBuffer, k); //safe the first part of Message in token
      while(token != NULL){
        //safe all the parts in int Variables to use later on
          if (c == 1) {startPoint = atoi(token);} 
          if (c == 2) {lengthCM = atoi(token);}
          if (c == 3) {Time = atoi(token);}
          if (c == 4) {rounds = atoi(token);break;}
        token = strtok(NULL, k);
        c++;
      }
      memset(packetBuffer, 0, packetSize); //reset Buffer for next Packet
    //convert lengthCM to a positive in CMx for correct calculation  
      if(lengthCM < 0) {
        CMx = lengthCM * (-1);
      }else{
        CMx = lengthCM;
      }
    //Calculate RPM for Stepper Motor
      RPM = ((CMx/0.003745)*0.0375)/(Time-((CMx/0.003745)*0.0000168));
    //Failsaves for Data that wouldnt make sense to the Driver/Motor
      if (startPoint + lengthCM > distance) {Reply(fail1); return;}
      if (startPoint + lengthCM < 0) {Reply(fail2); return;}
      if (RPM > 100) {Reply(fail3); return;}
      if (RPM <= 0) {Reply(fail4); return;}
      if (Time <= 0) {Reply(fail5); return;}
      
      Reply(packetReply); //send packet back that it was successful
      waitMillis(2000);

    //Do the measurement
      OLED(startpos,0,30,1,true); OLEDconst();
      moving(startPoint, false);
      slider.setRPM(RPM);
      int odd = rounds;
      OLED(tensecs,0,30,1,true); OLEDconst();
      waitMillis(10000);
      OLED(startmeas,0,25,1,true); OLED(startnow,15,35,2,false); OLEDconst();
      for (int i=0; i<=100; i++) {
        if (rounds == 0) {break;}
          moving(lengthCM, true);
          rounds--;
          waitMillis(1000);
        if (rounds == 0) {break;}
          moving(-lengthCM, true);
          rounds--;
          waitMillis(1000);
      }
      OLED(measdone,0,30,1,true); OLEDconst();
      slider.setRPM(60);
      //OLED(steady,0,30,1,true); OLEDconst();   //for taking steady measurements
      //waitMillis(20000);
      OLED(backinten,0,30,1,true); OLEDconst();
      waitMillis(10000);
    //Move back to beginning
      if (odd % 2) {moving(-(startPoint+lengthCM),false);}else {moving(-startPoint,false);}
      OLED(finished,0,30,1,true); OLEDconst();
      waitMillis(1000);
      OLED(newpacket,0,30,1,true); OLEDconst();
  }
    waitMillis(50);//Wait 50 Milliseconds between looking for a Packet
}

A lot of wasted RAM there.

1 Like

well yeah but as i said, shouldnt it work with still 10% left on program space and 33% left on global variables? like im looking for something that causes instability. but thanks for responding

You have an OLED - the frame buffer for that is allocated at run-time, so doesn't show up in the compile-time stats

1 Like

oh could you explain that a little more, pls? whats the frame buffer and can i do anything about it apart from trying to shorten the program? Are other screens better for that maybe? i wanted to use an LCD first but it used up too many pins on my Arduino.

The OLED typically requires a buffer of 128x64 bits = 1024 bytes of RAM.
If all you ever display is text this can be reduced to virtually zero by using a text-only library.
If you require graphics, you can use a reduced frame buffer library (u8glib ?), at the expense of draw speed

An LCD with I2C adapter uses two pins (a bit slow though).

You could make sure your string literals remain in flash memory by using the F() macro when you print them.

1 Like

ah, well i tried to look for different libraries because i really just use text, do you have a good library for that? and i used the F() "trick" on the first Version of the Program with the Serial Monitor, but i thought it only saves global variables space and i still have a bunch more than that, but it seems like i misunderstood something there. Thanks

yeah i think you solved it man, i found the SSD1306Ascii library and i think its gonna save a lot of space which should most definitly be enough. thanks a lot