Using Millis With Other Data Types

On the Arduino reference page at: http://arduino.cc/en/Reference/Millis, it states: “Note that the parameter for millis is an unsigned long, errors may be generated if a Programmer tries to do math with other datatypes such as ints”. No further explanation is given.

The code below is for a model train controller and works just fine. I wanted to reduce the refresh rate of the serial monitor to once per second so I added two simple lines of code using the millis function. While it gave the desired refresh rate, it affected other calculations in the sketch. Specifically, the program uses acceleration and deceleration rates for realism in which the train speed (spd) takes time to catch up to the throttle setting. The added code, however, appears to have negated the spd calculations as spd now equals throttle settings.

The added code appears in void monitor() and has been commented out. I can’t figure out how this code can affect other calculations which is why I mentioned the above note about millis and other data types. Does this appear to be the case or am I just overlooking something?

// MODEL TRAIN CONTROLLER 

int THROTTLE_PIN = 0;  //analog
int INPUT1_PIN = 2;  //digital
int INPUT2_PIN = 3;  //digital
int ENABLE_PIN = 11;  //digital PWM
int LED_PIN = 13;  //digital
 
int THROTTLE_PROPORTION = 4;
int THROTTLE_THRESHOLD = 5;
int SPEED_MAX = 253;  
float ACCELERATION_PROPORTION = 0.1;
int MIN_SPEED = 30;

int dir = 0;
float spd;
int throttle;
float acceleration;
int state = 0;
const unsigned long smInterval = 1000 ;
unsigned long smTimer = 0 ;

void setup() {
  Serial.begin(9600);
        digitalWrite(ENABLE_PIN, HIGH);
        pinMode(9,INPUT);
        pinMode(10,INPUT);
        pinMode(8,OUTPUT);
        pinMode(6,OUTPUT);
	pinMode(5,OUTPUT);
	pinMode(4,OUTPUT); 
     smTimer = millis() ;
}

int oldDir = 1;

void loop() {
        throttle = getThrottle();
        if (throttle <= THROTTLE_THRESHOLD) {
            throttle = 0;
           digitalWrite(LED_PIN,HIGH);
        } else digitalWrite(LED_PIN,LOW);
        accelerate();
        updateSpeed();
        monitor();

    if (throttle == 0 && spd == 0) {  //allow direction change only when stopped
      updateButtons();
    }
        if (dir == 0 && oldDir != dir) {
          digitalWrite(6,HIGH);
                    digitalWrite(8,LOW);
        } else if (dir == 1 && oldDir != dir) {
          digitalWrite(6,LOW);
          digitalWrite(8,HIGH);          
        }
        oldDir = dir;
}

void updateButtons() {
	if (digitalRead(10) == LOW) dir =0; 
	if (digitalRead(9) == LOW) dir =1;
}
 
int getThrottle() {
        int t = SPEED_MAX - analogRead(THROTTLE_PIN) / THROTTLE_PROPORTION;
        if (t > 5) t += MIN_SPEED;
        if (t > SPEED_MAX) {
          Serial.print("Warning, the throttle is: ");
          Serial.println(t);
        }
        return t;
}
 
void accelerate() {
        if (throttle != spd) {
                if (spd < throttle) {
                        if (spd >5 && spd < MIN_SPEED) spd += 10;
                        spd += 0.2; // rate of acceleration;
                        if (spd > throttle) spd = throttle;
                        if (state != 1) ; 
                        state = 1;
                        if (dir == 0) digitalWrite(5,HIGH);
                        if (dir == 1) digitalWrite(4,HIGH);
                } else if (spd > throttle) {
                         if (spd >5 && spd < MIN_SPEED) spd -= 10;
                        spd -= 0.2; //acceleration;
                        if (spd < throttle) spd = throttle;
                        if (spd < 0) spd = 0;
                        if (state != 2) ; 
                        state = 2;
                        if (dir == 0) digitalWrite(5,HIGH);
                        if (dir == 1) digitalWrite(4,HIGH);
                }
 
            //      if (spd == throttle  &&  spd != 0) {
                    if (spd > throttle-1 && spd < throttle+1 && spd != 0) { // reduce noise while cruising
                        if (state != 3); 
                        state = 3;  
                }
                   if (throttle == 0 && spd != 0) {
                      spd -= 0.2;
                   if (state != 3); 
                        state = 4;  
        }
        }

        digitalWrite(4,LOW);
        digitalWrite(5,LOW);
} 

void monitor() {
 //    if  ((millis() - smTimer) >= smInterval)
   { 
 //    smTimer = millis() ;   
        Serial.print(spd);
        Serial.print(" :: ");
        Serial.print(throttle);
        Serial.print(" :: ");        
    switch (state) {
    case 0:
   Serial.println("Stopped");
      break;
    case 1:
   Serial.println("Accelerating");
   break;
        case 2:
    Serial.println("Decelerating");
      break;
    case 3:
   Serial.println("Cruising");
      break; 
      case 4:
   Serial.println("Coasting");
      break; 
      }
    }

}
void updateSpeed() {
        if (dir == 1) {
                digitalWrite(INPUT1_PIN,HIGH);
                digitalWrite(INPUT2_PIN,LOW);
        } else if (dir == 0) {
                digitalWrite(INPUT1_PIN,LOW);
                digitalWrite(INPUT2_PIN,HIGH);
        } 

        analogWrite(ENABLE_PIN, spd); 
       
        if (spd == 0) {
                digitalWrite(INPUT1_PIN,LOW);
                digitalWrite(INPUT2_PIN,LOW);
                if (state != 0); 
                state = 0;
        }
}

You don't seem to have any logic to control how rapidly the acceleration occurs - you just ramp the speed up each time accelerate() is called. Since it's called unconditionally from within loop(), it's not surprising that the acceleration is almost instantaneous. What puzzles me is why it wasn't behaving like that previously, but perhaps you had some code that took significant time to run which was slugging things for you.

I suggest you use the same technique you used (and then commented out) in monitor() to run the accelerate() function at regular intervals.

Lawren5:
On the Arduino reference page at: http://arduino.cc/en/Reference/Millis, it states: “Note that the parameter for millis is an unsigned long, errors may be generated if a Programmer tries to do math with other datatypes such as ints”. No further explanation is given.

This just means to store the value from millis() into an unsigned long.

// These are wrong:
int time = millis();
long time = millis();
float time = millis();
// correct data type to store the value returned by millis()
unsigned long time = millis();

PeterH – Yes, I see what you’re saying about controlling the rate of acceleration which got me to thinking. The refresh rate of serial monitor was also not controlled which may have slowed the acceleration calculations. Once I reduced the refresh rate to once per second, the rate of acceleration calculations were no longer restricted, thus giving the appearance that train speed (spd) was tracking throttle. I’m new at all this but that’s my theory anyway.

James C4S – Yes, the reference shows you are correct about the millis data type.

I’ve since added timers to control both the acceleration and monitor refresh rates and the program is working perfectly.

Thanks to both of you for your help.
Lawren5