[Solved] Control a motor with acceleration and variable speed, no delays

I am building a power supply/throttle for a model tram layout, and I want to use my Uno R3 to not only power the train but also to run accessories (eg, traffic lights, crossing signals, etc) that rely on timing. My hope is to eventually have everything automated, with sensors to detect where the tram is on the layout so that it can stop at stations, trigger crossing lights, etc.
I want to have some amount of "momentum" on the train so that it runs more realistically, but I have so far only managed to figure out how to do it using delays, which will end up messing up traffic lights and detection when those get implemented. Is there a way that I can implement acceleration without using delay?

Here is the code I am currently using, It does exactly what I want it to, but will cause problems if I try to add any more features:

int sensVal = 0; //Value read from pot
int val = 0; //Desired speed value
int spd = 0; //Actual speed to run train
int accel = 150; // Speed to accelerate, lower numbers are faster
int decel = 60; //Speed to decelerate, lower numbers are faster
int outPin = 10; //Motor control pin

void setup() {
  //set inputs and outputs:
  pinMode(outPin, OUTPUT);
  pinMode(0, INPUT);
  
  setPwmFrequency(10,1); //set a higher, inaudible PWM frequency on pins 9 and 10
  
  Serial.begin(9600);
  
  //Leave voltage at 0 while pot is low, reset button can thus be used as emergency stop:
  analogWrite(outPin, 0);
  while (sensVal < 100) {
    sensVal = analogRead(0);
  }
}

void loop() {
  sensVal = analogRead(0); //get value from pot
  sensVal = map(sensVal, 0, 1023, 0, 140); //map to PWM range
  sensVal = constrain(sensVal, 0, 255); //allows for pot range tweaking
  
  //stops powering the motor below stall
  if (sensVal < 20) {
    val = 0;
    }
  else val = sensVal;
  
  Serial.println(sensVal); //debugging
  Serial.println(val);     //""
  Serial.println(spd);     //""
  
  // ****Acceleration**** 
  if (spd < val) {
    analogWrite(outPin, spd);
    //Serial.println(spd);
    delay(accel);
    spd++;
  }
  else if (spd > val) {
    analogWrite(outPin, spd);
    //Serial.println(spd);
    delay(decel);
    spd = spd / 1.01;
  }
  
}

//-------------------------------------------------------------------------------------

void setPwmFrequency(int pin, int divisor) {
  byte mode;
  if(pin == 5 || pin == 6 || pin == 9 || pin == 10) {
    switch(divisor) {
      case 1: mode = 0x01; break;
      case 8: mode = 0x02; break;
      case 64: mode = 0x03; break;
      case 256: mode = 0x04; break;
      case 1024: mode = 0x05; break;
      default: return;
    }
    if(pin == 5 || pin == 6) {
      TCCR0B = TCCR0B & 0b11111000 | mode;
    } else {
      TCCR1B = TCCR1B & 0b11111000 | mode;
    }
  } else if(pin == 3 || pin == 11) {
    switch(divisor) {
      case 1: mode = 0x01; break;
      case 8: mode = 0x02; break;
      case 32: mode = 0x03; break;
      case 64: mode = 0x04; break;
      case 128: mode = 0x05; break;
      case 256: mode = 0x06; break;
      case 1024: mode = 0x7; break;
      default: return;
    }
    TCCR2B = TCCR2B & 0b11111000 | mode;
  }
}

The setPwmFrequency() function was taken from the arduino playground, I am using it so I can eliminate the squeal that was driving me nuts (now its driving my dog nuts :grin:)

To eliminate the delays, use something like this in loop:

   static unsigned long lastAccelTime = 0;
   static unsigned long lastDecelTime = 0;

   unsigned long currentTime = millis();

  // ****Acceleration**** 
  if (currentTime - lastAccelTime > accel) {
    lastAccelTime = currentTime;
    if (spd < val) {
      spd++;
      analogWrite(outPin, spd);
    }
  }

  // ****Deceleration**** 
  if (currentTime - lastDecelTime > decel) {
    lastDecelTime = currentTime;
    if (spd > val) {
      spd = spd / 1.01;
      analogWrite(outPin, spd);
    }
  }

Wow, that was a whole lot easier than I expected! I have spent a lot of time trying to figure out how to use that currentTime - lastTime format and just couldn't get it until now. I will definitely be using that more often!

johnwasser:
To eliminate the delays, use something like this in loop:

   static unsigned long lastAccelTime = 0;

static unsigned long lastDecelTime = 0;

unsigned long currentTime = millis();

// Acceleration
  if (currentTime - lastAccelTime > accel) {
    lastAccelTime = currentTime;
    if (spd < val) {
      spd++;
      analogWrite(outPin, spd);
    }
  }

// Deceleration
  if (currentTime - lastDecelTime > decel) {
    lastDecelTime = currentTime;
    if (spd > val) {
      spd = spd / 1.01;
      analogWrite(outPin, spd);
    }
  }

Why did you use the line:
spd = spd / 1.01;
Instead of
spd -=1;

I'm fixing to use this same code snippet, and am interested. I'm sure there's a reason, so I'd like to know.

Reduces the value of spd by 1% of current value each iteration, results in a tapering curve instead of a linear ramp, giving a smoother stop, run this sketch and look at the numbers. Vely crever, John.

void setup()
{
  Serial.begin(9600);
}
void loop()
{
  static float spd = 255.0;
  byte val = 10;
  if (spd > val) {
      spd = spd / 1.01;
      Serial.print(spd);
      Serial.print("\t");
      Serial.println((byte)spd);
  }    
  
}