Low memory available options

Attached is my code for this project. Its a 2 motor weld positioner, 1 motor controls the spindle head, the other controls the position angle. From 0 to 90deg, or 0 position to 7000 position.

Each button press of ccw or cw moves forward or backward 1 deg (or position 100)

You might notice the bottom section of my code for the position motor... the Ccw button is not returning the same positions as the Cw button.. this is because the code is unfinished, i previously has positions set in 10s and had to change them to 100 to reflect a gear box i added.

i figured instead of updating all those positions now before a solution to my problem would be unnecessary time wasted if i had to redo them for the solution anyway.

With all this im using an arduino uno and im getting low memory warning. Witch i highlight at the beginning of the code. I believe its because all of my calls to print to screen the new position.

Im interested to see if anyone has a solution to print to screen a new position from 0-90deg (based on position 0-7000) that would use less memory.

weld_positoner_2_motor.ino (38.7 KB)

By some reason my Win10 Pc nowdays refuses to download Your file. Could You try and attache it using code tags? Up to the left in this window, looks like </>.

  if (buttonStateCw == LOW && stepper2.currentPosition() == 0)  {

    stepper2.moveTo(100);
    stepper2.setMaxSpeed(angleSpeed);
    lcd.setCursor(7, 1);
    lcd.print("36 DEGREES      ");
  }
  else if (buttonStateCw == LOW && stepper2.currentPosition() == 100)  {

    stepper2.moveTo(200);
    stepper2.setMaxSpeed(angleSpeed);
    lcd.setCursor(7, 1);
    lcd.print("37 DEGREES      ");
  }
  else if (buttonStateCw == LOW && stepper2.currentPosition() == 200)  {

    stepper2.moveTo(300);
    stepper2.setMaxSpeed(angleSpeed);
    lcd.setCursor(7, 1);
    lcd.print("38 DEGREES      ");
  }
  else if (buttonStateCw == LOW && stepper2.currentPosition() == 300)  {

    stepper2.moveTo(400);
    stepper2.setMaxSpeed(angleSpeed);
    lcd.setCursor(7, 1);
    lcd.print("39 DEGREES      ");
  }
etc, etc, etc

This section of code is very repetitive with much code repeated and multiple prints. Consider putting the repeated code in a function. Printing text throughout the program should be done using the F() macro to save memory and don't print spaces if at all possible

I can download it here. You won't be able to post it inline because of the size.

UKHeliBob:
don't print spaces if at all possible

... or make a space printing function if you really have to do a lot of it.

I haven't checked every single if statement for consistency, and I'm not sure which part you have not finished, but the entire first half of those if statements could be replaced with a few simple calculations:

  int curPos = stepper2.currentPosition();
  if ((buttonStateCw == LOW) && (curPos % 100 == 0)) {
    stepper2.moveTo(100 * ((curPos / 100) + 1));
    stepper2.setMaxSpeed(angleSpeed);
    lcd.setCursor(7, 1);
    int curAngle = (curPos / 100) + 36;
    if (curAngle > 90) {
      curAngle = 90;
    }
    if (curAngle < 10) {
      lcd.print(" ");
    }
    lcd.print(curAngle);
    lcd.print(F("DEGREES      "));
  }

david_2018:
I haven't checked every single if statement for consistency, and I'm not sure which part you have not finished, but the entire first half of those if statements could be replaced with a few simple calculations:

  int curPos = stepper2.currentPosition();

if ((buttonStateCw == LOW) && (curPos % 100 == 0)) {
   stepper2.moveTo(100 * ((curPos / 100) + 1));
   stepper2.setMaxSpeed(angleSpeed);
   lcd.setCursor(7, 1);
   int curAngle = (curPos / 100) + 36;
   if (curAngle > 90) {
     curAngle = 90;
   }
   if (curAngle < 10) {
     lcd.print(" ");
   }
   lcd.print(curAngle);
   lcd.print(F("DEGREES      "));
 }

I wouldnt have been able to come up with this and im trying to understand it.. but not getting it fully. Anyway i tried it and it works, however its able to run past 90deg and i cant have that. Also i couldnt figure out a way to run it in the opposite direction with the Ccw button.

I dont understand how to use the function or F() macro, im trying but its more complex and cant find anything that explains it in a way i can understand.

The code i attached works as i want it to, would there be a reason to worry about the low memory if it works?

The F() macro is used for .print() statements, it stores the quoted text in program memory instead of dynamic ram, freeing up the ram for variables whose values need to change while the sketch is running.

I see a couple of problems with the code I posted, mainly I forgot to limit it to 7000 steps. Here is the code with some comments added:

  int curPos = stepper2.currentPosition(); //stores a copy of stepper2.currentPositon()
  if ((buttonStateCw == LOW) && (curPos % 100 == 0) && (curPos < 7000)) { 
    //(curPos % 100) is curPos modulo 100, which gives the remainder after  dividing by 100
    //also need to limit curPos to a maximum of 7000
    stepper2.moveTo(curPos + 100); //move to 100 more than the current position
    stepper2.setMaxSpeed(angleSpeed);
    lcd.setCursor(7, 1);
    int curAngle = (curPos / 100) + 36; //angle start at 35 when current position is 0, then increases by 1 for each 100 steps
    if (curAngle > 90) { //limits angle to 90 (your original code reached 90 at 5400 but continues on to 6900
      curAngle = 90;
    }
    if (curAngle < 10) { //prints a leading space if angle is a single digit
      lcd.print(" ");
    }
    lcd.print(curAngle); //prints the angle
    lcd.print(F(" DEGREES      ")); //print the remaining text after the angle
  }

david_2018:
The F() macro is used for .print() statements, it stores the quoted text in program memory instead of dynamic ram, freeing up the ram for variables whose values need to change while the sketch is running.

I see a couple of problems with the code I posted, mainly I forgot to limit it to 7000 steps. Here is the code with some comments added:

  int curPos = stepper2.currentPosition(); //stores a copy of stepper2.currentPositon()

if ((buttonStateCw == LOW) && (curPos % 100 == 0) && (curPos < 7000)) {
   //(curPos % 100) is curPos modulo 100, which gives the remainder after  dividing by 100
   //also need to limit curPos to a maximum of 7000
   stepper2.moveTo(curPos + 100); //move to 100 more than the current position
   stepper2.setMaxSpeed(angleSpeed);
   lcd.setCursor(7, 1);
   int curAngle = (curPos / 100) + 36; //angle start at 35 when current position is 0, then increases by 1 for each 100 steps
   if (curAngle > 90) { //limits angle to 90 (your original code reached 90 at 5400 but continues on to 6900
     curAngle = 90;
   }
   if (curAngle < 10) { //prints a leading space if angle is a single digit
     lcd.print(" ");
   }
   lcd.print(curAngle); //prints the angle
   lcd.print(F(" DEGREES      ")); //print the remaining text after the angle
 }

This worked quite well, much thanks... only issue is i cant reverse the position. Ive tried multiple variations that i can think of and all that happens is the degrees on the lcd goes down, but the stepper dont move.

Since my code is so long, This is a short snipped of how this code would work

if (buttonStateCw == LOW && stepper2.currentPosition() == 0)  {

    stepper2.moveTo(100);
    stepper2.setMaxSpeed(angleSpeed);
   lcd.setCursor(7, 1);
    lcd.print("1 DEGREE"); //move forward to 1 degree
  }
  else if (buttonStateCw == LOW && stepper2.currentPosition() == 100)  {

    stepper2.moveTo(200);
    stepper2.setMaxSpeed(angleSpeed);
      lcd.setCursor(7, 1);
    lcd.print("2 DEGREE"); //move forward to 2 degree
  }

   
 if (buttonStateCcw == LOW && stepper2.currentPosition() == 200)  {

    stepper2.moveTo(100);
    stepper2.setMaxSpeed(angleSpeed);
  lcd.setCursor(7, 1);
    lcd.print("1 DEGREE"); //move backward to 1 degree
  }
  else if (buttonStateCcw == LOW && stepper2.currentPosition() == 100)  {

    stepper2.moveTo(0);
    stepper2.setMaxSpeed(angleSpeed);
      lcd.setCursor(7, 1);
    lcd.print("0 DEGREE"); //move backward to 0 degree
  }

Of course this would run up to 90degrees with the Cw button and down to 0 degrees with the Cce button witch is why my code is so long.. thats 180x of this code.

Something like this could replace your 1,800 lines of repetition:

if (buttonStateCw == LOW && (stepper2.currentPosition()%100 == 0)  {

    stepper2.moveTo(stepper2.currentPosition() + 100);
    stepper2.setMaxSpeed(angleSpeed);
      lcd.setCursor(7, 1);
    lcd.print((stepper2.currentPosition() + 100)/100); //move forward to 2 degree
    lcd.print(" DEGREE"); //move forward to 2 degree
  }

You can't cut and paste this, it's just a pencil sketch, you have to work out the details...

Your code is a bit confusing, because the angles and number of steps do not seem to match between the clockwise and counterclockwise directions. I would think that the stepper motor current position should be the same for a given angle, regardless of which direction it turned to get there.

david_2018:
Your code is a bit confusing, because the angles and number of steps do not seem to match between the clockwise and counterclockwise directions. I would think that the stepper motor current position should be the same for a given angle, regardless of which direction it turned to get there.

All of the counterclockwise positions are the wrong positions - this is where i didnt finish that code - going thru and updating positions 90 times is time consuming..

When I first wrote this code, I made it that 10 steps was 1 degree, I was just using the stepper motor for reference.

Then I got a worm drive gear box for the stepper motor and found its 100 steps for 1 degree. So i started changing the Clockwise positions.. and it was so time consuming and I was running out of memory so i figured id seek help. So I only did the clockwise positions up untill that point.

I finally noticed in all of my trial and error, that the 5+ came unhooked from my stepper driver witch is why I couldnt getting any of the variations I tried to get it to CCW to work.

Here is the code now, and it works perfect. I also found out 61 steps is more accurate of my gear box. This gear box is an old wiper motor from a car that I retrofitted with a stepper motor.

/* Sketch uses 9492 bytes (29%) of program storage space. Maximum is 32256 bytes.
Global variables use 464 bytes (22%) of dynamic memory, leaving 1584 bytes for local variables. Maximum is 2048 bytes.
*/

#include <AccelStepper.h>
#include <MultiStepper.h>
#include <LiquidCrystal.h>

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
// Define our three input button pins
#define  LEFT_PIN  11
#define  STOP_PIN  1
#define  RIGHT_PIN A2
#define LEFT_PIN2  A5
#define RIGHT_PIN2  A4
#define LIMIT_PIN  A3

// Define the stepper and the pins it will use
AccelStepper stepper1(AccelStepper::DRIVER, 2, 3);
AccelStepper stepper2(AccelStepper::DRIVER, 12, 13);

//Non Blocking Code Millis for the Potentiometer Speed Display on the LCD
unsigned long previousMillis = 1000;
const long interval = 2000;

// Define our maximum and minimum speed in steps per second (scale pot to these)
const int  MAX_SPEED = 6000; //Max potentiometer speed, 2000 steps per second divide by 200 steps (1 revolution)=10 revultions per second,=600rpm.  Geared at 3:1 this =200rpm per min on the PM727, that is 2 inchs per minute travel
const int MIN_SPEED = 8; //Min potentiometer speed
int buttonStateCw = 0;
int buttonStateCcw = 0;
const int angleSpeed = 4000; //Speed the Angle will Move when button is pushed
const int RPM = 12; // RPM lcd display of Indexing Head
void setup() {

  // Define our analog potentiometer input pin
  const int  SPEED_PIN = A1; // Input Pin location on the Arduino

  // Define our maximum and minimum speed in steps per second (scale pot to these)
  const int  MAX_SPEED = 4000; //Max potentiometer speed, 2000 steps per second divide by 200 steps (1 revolution)=10 revultions per second,=600rpm.  Geared at 3:1 this =200rpm per min on the PM727, that is 2 inchs per minute travel
  const int MIN_SPEED = 8; //Min potentiometer speed
  long initial_homing = -1; // Used to Home Stepper to Up or Down Limit Switchs

  // Set up the three button inputs, with pullups
  pinMode(LEFT_PIN, INPUT_PULLUP);
  pinMode(STOP_PIN, INPUT_PULLUP);
  pinMode(RIGHT_PIN, INPUT_PULLUP);
  pinMode(LEFT_PIN2, INPUT_PULLUP);

  pinMode(RIGHT_PIN2, INPUT_PULLUP);
  pinMode(LIMIT_PIN, INPUT_PULLUP);
  lcd.begin(16, 2);
  lcd.setCursor(0, 0);
  lcd.print("RPM:");
  lcd.setCursor(8, 0);
  lcd.print("DIR:");
  lcd.setCursor(0, 2);
  lcd.print("ANGLE:");
  lcd.setCursor(7, 1);
  lcd.print("HOMING");
  Serial.begin(9600);

  while (digitalRead(LIMIT_PIN)) {


    stepper2.moveTo(-initial_homing);  // Set the position to move to
    stepper2.setMaxSpeed(4000);      // Set Max Speed of Stepper (Slower to get better accuracy)
    stepper2.setAcceleration(100);  // Set Acceleration of Stepper
    initial_homing--;  // Decrease by 1 for next move if needed
    stepper2.run();  // Start moving the stepper

  }

  stepper2.setCurrentPosition(0);  // Set the current position as zero for now
  stepper2.setMaxSpeed(200);      // Set Max Speed of Stepper (Slower to get better accuracy)
  stepper2.setAcceleration(100);  // Set Acceleration of Stepper
  initial_homing = 1;
  stepper2.setCurrentPosition(0);
  stepper2.setMaxSpeed(20000.0);
  stepper1.setMaxSpeed(20000.0);
  stepper2.setAcceleration(10000);
  lcd.setCursor(7, 1);
  lcd.print("0  ");
  lcd.setCursor(10, 1);
  lcd.print("DEGREE");
}
// The only AccelStepper value we have to set here is the max speeed, which is higher than we'll ever go

void loop() {

  static float current_speed = 0.0;         // Holds current motor speed in steps/second
  static int analog_read_counter = 1000;    // Counts down to 0 to fire analog read
  static int sign = 0;                     // Holds -1, 1 or 0 to turn the motor on/off and control direction
  int analog_value = 0;              // Holds raw analog value.
  static float previouscurrent_speed = 0.05; //declares "currentMillis" as to reflect Arduino millis
  unsigned long currentMillis = millis();
  buttonStateCw = digitalRead(LEFT_PIN2);
  buttonStateCcw = digitalRead(RIGHT_PIN2);


  if (digitalRead(LEFT_PIN) == 0) {
    sign = 1; lcd.setCursor(13, 0);
    lcd.print(" CW ");
  }
  else if (digitalRead(RIGHT_PIN) == 0) {
    sign = -1; lcd.setCursor(13, 0);
    lcd.print("CCW ");
  }
  else if (digitalRead(STOP_PIN) == 0) {
    sign = 0; lcd.setCursor(12, 0);
    lcd.print("STOP");
  }

  // We only want to read the pot every so often (because it takes a long time we don't
  // want to do it every time through the main loop).
  if (analog_read_counter > 0) {
    analog_read_counter--;
  }
  else {
    analog_read_counter = 3000;

    // Now read the pot (from 0 to 1023)
    analog_value = analogRead(A1);

    // Give the stepper a chance to step if it needs to
    stepper1.runSpeed();

    //  And scale the pot's value from min to max speeds
    current_speed = sign * (((analog_value / 1023.0) * (MAX_SPEED - MIN_SPEED)) + MIN_SPEED);

    // Update the stepper to run at this new speed
    stepper1.setSpeed(current_speed);
  }



  if (currentMillis - previousMillis >= interval) {
    // save the last time you blinked the LED
    previousMillis = currentMillis;


    if (current_speed != previouscurrent_speed  ) {


      previouscurrent_speed = current_speed;
      lcd.setCursor(4, 0);
      lcd.print(float(float(current_speed) / float(RPM)), 1);

    }
  }


  // When a Direction Button is pressed, make it run in that direction indefinetly untill another button is pressed
  if (sign != 0) {
    stepper1.runSpeed();
  }


  int curPos = stepper2.currentPosition(); //stores a copy of stepper2.currentPositon()
  if ((buttonStateCw == LOW) && (curPos % 61 == 0) && (curPos < 7000)) {
    //(curPos % 100) is curPos modulo 100, which gives the remainder after  dividing by 100
    //also need to limit curPos to a maximum of 7000
    stepper2.moveTo(curPos + 61); //move to 100 more than the current position
    stepper2.setMaxSpeed(angleSpeed);
    lcd.setCursor(7, 1);
    int curAngle = (curPos / 61) + 1; //angle start at 35 when current position is 0, then increases by 1 for each 100 steps
    if (curAngle > 90) { //limits angle to 90 (your original code reached 90 at 5400 but continues on to 6900
      curAngle = 90;
    }
    if (curAngle < 10) { //prints a leading space if angle is a single digit
      lcd.print(" ");
    }
    lcd.print(curAngle); //prints the angle
    lcd.print(F(" DEGREES      ")); //print the remaining text after the angle
  }
  if ((buttonStateCcw == LOW) && (curPos % 61 == 0) && (curPos > 0)) {
    //(curPos % 100) is curPos modulo 100, which gives the remainder after  dividing by 100
    //also need to limit curPos to a maximum of 7000
    stepper2.moveTo(curPos - 61); //move to 100 more than the current position
    stepper2.setMaxSpeed(angleSpeed);
    lcd.setCursor(7, 1);
    int curAngle = (curPos / 61) - 1; //angle start at 35 when current position is 0, then increases by 1 for each 100 steps
    if (curAngle > 90) { //limits angle to 90 (your original code reached 90 at 5400 but continues on to 6900
      curAngle = 90;
    }
    if (curAngle < 10) { //prints a leading space if angle is a single digit
      lcd.print(" ");
    }
    lcd.print(curAngle); //prints the angle
    lcd.print(F(" DEGREES      ")); //print the remaining text after the angle
  }


  while (stepper2.currentPosition() != stepper2.targetPosition()) { // only move it while we're not at the target
    stepper2.runSpeedToPosition(); // I don't think this is blocking

  }
  if (sign != 0) {
    stepper1.runSpeed();
  }
}

Low memory?

You use #defines for small numbers. I was surprised to find out that they get stored as ints, 16 bits each.
Want to save RAM, make those const byte variables and they use 8 bits each.

Time after time you use an int where a byte (uint8_t) or char (int8_t) would do fine.

static int sign = 0; // to hold -1, 0, or 1 function static variables are stored on the heap

or

static char sign = 0; // still able to hold -1, 0, or 1

........

unsigned long currentMillis = millis(); // one way to WASTE 4 bytes

Just use the millis() function every time, it's not going to bog down the sketch.

king6fab:
I also found out 61 steps is more accurate of my gear box. This gear box is an old wiper motor from a car that I retrofitted with a stepper motor.

How many steps does it take to get to 90 degrees? 61 steps per degree would be around 5490, yet you are running clockwise to 7000 steps, which would be about 115 degrees.

david_2018:
How many steps does it take to get to 90 degrees? 61 steps per degree would be around 5490, yet you are running clockwise to 7000 steps, which would be about 115 degrees.

So i have a new problem to solve, after rigging up the retrofitted stepper/wipergear box... the gearbox stripped out instantly from the weight. So i bought a 80:1 stepper motor gear box . Put that on and it didnt have enough torque to lift the weld positioner. This changed plans alot, i had to modify the base for gear reductio, so i also made the positioner so it can be rotated nearly 0-180deg

So i made a chain drive gear reduction, witch is like a 5:1 ratio

This throws off my position tracking by alot. Watching in the serial monitor , When i get to 32400 steps, its starts counting down in negative numbers. 32400 steps is about 80deg on the machine. I thought by dividing the current steps by a large number i could come up close to 0-180 but once it gets to 32400 steps my degrees go back down

I had to change some parts of the code to get it the motor to run correctly, now i just need a way of tracking position over 32400 steps

    int curPos = stepper2.currentPosition(); //stores a copy of stepper2.currentPositon()
  if ((buttonStateCw == LOW)) {
    
    stepper2.move(600);
    stepper2.setSpeed(angleSpeed);
    lcd.setCursor(7, 1);
    int curAngle = (curPos / 600) + 1; 
    if (curAngle > 180) { 
      curAngle = 180;
    }
    if (curAngle < 10) { //prints a leading space if angle is a single digit
      lcd.print(" ");
    }
    lcd.print(curAngle); //prints the angle
    lcd.print(F(" DEGREES      ")); //print the remaining text after the angle
  }

Heres a video of it working https://youtu.be/jdk2L1FPg-0https://youtu.be/jdk2L1FPg-0

An int can count as high as 32767 and if you add to that you get negative values due to rollover.

See if counting large values with long variables makes a change.

I forgot to add, i had to change the "moveTo" to move because when it got to 32400 steps, it would automatically start running backwards. So im guessing accelstepper position control is limited to 32400.

Do you know if theres a way around that? Because my current code isnt tracking the position and can be ran past where it needs to stop at , ruining the gear box potentially. I have no more pins left for a limit switch either.

Figured it out, i was thinking the accelstepper library was limiting me, so i dug into the library code to find its all long variables, i completely over looked the "int curPos" part in this code thinking it was only for the angle and not the position.

Cant belive how many hours i spent trying to solve this or bypass it..

Guess thats the Learning curve, over thinking it.