Programmable model railway with light dependent resistors

Good afternoon,

I’m really quite new to this and have had a go at implementing coding from a variety of different sources. Whilst reading Arduino for Dummies.

Please may you have a look at the program below, it currently has no errors but does not function correctly. The program is explained as i go & any help would be greatly appreciated.

#include <Wire.h>
#include <Adafruit_MotorShield.h>
#include "utility/Adafruit_PWMServoDriver.h"

// Create the motor shield object with the default I2C address
Adafruit_MotorShield AFMS = Adafruit_MotorShield(); 

// Select which 'port' - M4.
Adafruit_DCMotor *myMotor = AFMS.getMotor(1);

int sensorPin = A0;            // select the input pin for the ldr
unsigned int sensorValue = 0;  // variable to store the value coming from the ldr

void setup() {
  Serial.begin(9600);           // set up Serial library at 9600 bps
  Serial.println("Adafruit Motorshield v2 - DC Motor test!");

  AFMS.begin();  // create with the default frequency 1.6KHz
    
  // Set the speed to start, from 0 (off) to 255 (max speed)
  myMotor->setSpeed(150);

}

void loop() {
  uint8_t i;
  
   myMotor->run(FORWARD); // motor accellerates and keeps at a constand speed
  for (i=0; i<255; i++) {
    myMotor->setSpeed(i);  
  }
   sensorValue = analogRead(sensorPin);  // when sensor is seen / goes dim the motor should deccelerate 
  if(sensorValue<400) for (i=255; i!=0; i--) {
    myMotor->setSpeed(i);  
    delay(10);
  } 
  myMotor->run(RELEASE); // motor should wait for set delay period
  delay(1000);
    
  myMotor->run(BACKWARD); //  motor should then return in opposite direction accelerating and decelerating for set delay period
  for (i=0; i<255; i++) {
    myMotor->setSpeed(i);  
    delay(10);
  }
  for (i=255; i!=0; i--) {
    myMotor->setSpeed(i);  
    delay(10);
  }
  myMotor->run(RELEASE); // motor should wait for set delay period then go back to the beginning and start over
  delay(1000);
}

Hi. Welcome.

Please edit your first post and separate the code from your comments about the code.

That is, only the actual code (including code comments) should be inside the "code" tags. It makes it easier for us to read.

Like this:

... only code with code comments in here

You say that the code compiles without errors, but does not do what it is supposed to do. Please describe what it does, and what it is supposed to do. That allows us to determine where the problem is. For example, if it is supposed to say "hello world", but it says "pete", or maybe just "h", please tell us both of those things.

   sensorValue = analogRead(sensorPin);  // when sensor is seen / goes dim the motor should deccelerate 
  if(sensorValue<400) for (i=255; i!=0; i--) {
    myMotor->setSpeed(i);  
    delay(10);
  }

Change to:

   sensorValue = analogRead(sensorPin);  // when sensor is seen / goes dim the motor should deccelerate 
  if(sensorValue<400){
    for (i=255; i>=1; i--) {
      myMotor->setSpeed(i); 
      delay(10);
     }
  }

You also might consider using Hall effect or reed switches to detect trains, as LDRs may be affected by changes in ambient light and are more difficult to hide/disguise. Hall effect and reed switches can be buried beneath the track and ballast, so will be out of sight.

LDRs also respond slowly...

Really? I didn't know that. I set up a test, with an LED blinking and a LDR. These are my results:

That square wave is 5 Hz.

I'm assuming the LED responds quickly, because they are used for somewhat faster situations. So I hope I am timing the LDR.

It is reasonably fast to notice the light coming on, a bit slower for the light turning off. I measured 10 ms there to reach 50% of the voltage level. That is probably OK for a model train.

I don't think model trains move fast enough for any "slowness" in LDRs to matter. And if the level of ambient light causes a problem it would be easy to use another LDR as a "standard".

LDRs are cheap, small and easy to work with.

@Guy_Whiteing, you have not told us what is the purpose of the program?
What is it controlling?

...R

Thank you all for your response even with formatting issues. It is greatly appreciated. The main purpose at the moment is just to get the locomotive to one end of the track by sensor and back again by delay. At a later date I would like introduce multiple sensors, servos for points and even a 180 turntable. The layout below is just a starter to get the programming side of things up and running on a desktop scale whilst in front of a computer.

One of the current problems when I first uploaded the program is that it does not actually do anything. I have ran other programs to ensure the LDR and motor are running individually, just not together.

As well as the statements I have used can anyone advise of a different way to get motor to accelerate up remain constant until the LDR is seen and then decelerate. Perhaps a While or Switch case. Although I would unsure where to begin.

See image below as mentioned above. X marks the point of the LDR, you can just about see it in the image.

Thank you again for the help. The adjustments to the curly brackets worked a treat, resolving the issue of it not working. Problem is, its did not run as I was expecting. I was hoping that the motor without a delay period set would keep accelerating and remain at a constant speed until the sensor is seen then as soon as it sees it, it would then start slowing down.

currently as the sketch is set at the moment without a delay in the motor run forward it jolts forward barely 5mm then runs backwards for the set delay period. With a delay period in the motor run forward it does not decelerate as soon as the sensor is seen it adds it on to the end of the acceleration delay period.

Would it be possible to accelerate up, run at a constant speed & once the LDR is seen respond almost straight away by decelerating.

... respond almost straight away ...

That phrase, and delay() in your code, are mutually incompatible.

See: Gammon Forum : Electronics : Microprocessors : How to do multiple things at once ... like cook bacon and eggs

[quote author=Nick Gammon date=1444109551 link=msg=2424960]
Really? I didn't know that. I set up a test, with an LED blinking and a LDR. These are my results:

That square wave is 5 Hz.

I'm assuming the LED responds quickly, because they are used for somewhat faster situations. So I hope I am timing the LDR.

It is reasonably fast to notice the light coming on, a bit slower for the light turning off. I measured 10 ms there to reach 50% of the voltage level. That is probably OK for a model train.[/quote]
They are slow WRT 16MHz, but fast WRT a model train!
My concern is that they're not easily hidden (as they have to be open to light), whereas Hall effect and reed switches can be completely covered and invisible.

Guy_Whiteing:
Thank you again for the help. The adjustments to the curly brackets worked a treat, resolving the issue of it not working. Problem is, its did not run as I was expecting. I was hoping that the motor without a delay period set would keep accelerating and remain at a constant speed until the sensor is seen then as soon as it sees it, it would then start slowing down.

currently as the sketch is set at the moment without a delay in the motor run forward it jolts forward barely 5mm then runs backwards for the set delay period. With a delay period in the motor run forward it does not decelerate as soon as the sensor is seen it adds it on to the end of the acceleration delay period.

Would it be possible to accelerate up, run at a constant speed & once the LDR is seen respond almost straight away by decelerating.

I’m using the Adafruit motor shield V1. You’ll have to adjust the code for the V2, but take a look at this:
The different speed steps in the ‘fasterslower’ function is because my loco runs at different speeds in different directions!

/ Adafruit Motor shield library
// copyright Adafruit Industries LLC, 2009
// this code is public domain, enjoy!
// Adjusted by me

#include <AFMotor.h>
AF_DCMotor motor(1);
int state = 1; // initial state is 1, the "idle" state.
int fastestSpeed = 250; //could be up to 255
int slowestSpeed = 100;  //slowest speed at which the motor will turn in my loco.
unsigned long beginTime = millis();
unsigned long ts = millis();
unsigned long runtime = 50000;
int currentspeed = 100; //just below the slowest speed at which the motor will turn in my loco.
void setup() {
  motor.run(RELEASE);
  Serial.begin(9600);
}    
void loop() {  
  switch(state)
  {
  case 1:
    motor.run(FORWARD);
    Serial.println("Forwards");
    state ++;
    break;
  case 2:    
    //Accel
    if (millis() - beginTime >= 1000) { //increase speed every second
      beginTime = millis();
      fasterslower(fastestSpeed);
    }
    if (currentspeed >= fastestSpeed) state ++; //max speed reached
    break;
  case 3:
    if(millis() - ts >= runtime) //continue at max speed until time elapsed
    {
      state ++; 
      ts = millis();
    }
    break;
  case 4:
    //Decell
    if (millis() - beginTime >= 1000) {
      beginTime = millis();
      fasterslower(slowestSpeed); //now slow down
    }
    if (currentspeed <= slowestSpeed){ //has motor stopped
      state ++;   
      motor.run(RELEASE);
      Serial.println ("Stopped");
    }
    break;
  case 5:
    if(millis()- ts >= runtime){ //standing at station time
      state ++;
      ts = millis();
    }
    break; 
  case 6:
    Serial.println ("Reverse");
    motor.run(BACKWARD);
    state ++; 
    break;
  case 7:    
    //Accel
    if (millis() - beginTime >= 1000) {
      beginTime = millis();
      fasterslower(fastestSpeed); //accelerate to max speed backwards
    }
    if (currentspeed >= fastestSpeed) state ++; Has it reached max speed?
    break;
  case 8:
    if(millis() - ts >= runtime) //continue at max speed for this time
    {
      state ++;
      ts = millis();
    }
    break;
  case 9:
    //Decell
    if (millis() - beginTime >= 1000) { //slow down in 1 second steps
      beginTime = millis();
      fasterslower(slowestSpeed);
    }
    if (currentspeed <= slowestSpeed){  //has it stopped?
      state ++;
      motor.run(RELEASE);
      Serial.println ("Stopped");
    }
    break;
  case 10:
    if(millis() - ts >= runtime)  // stopped in station for ts milliseconds
    {
      ts = millis();
      state ++;
    }
    break; 
  case 11:
    state = 1;  // Return to the "idle" state and start again
    ts = millis();
    break;
  default:  //should never get here, but just in case...
    state = 1;
    break;
  }
}
int fasterslower(unsigned int changespeed)
{
  if (currentspeed < changespeed) currentspeed = currentspeed +5;
  else currentspeed = currentspeed -4;
  motor.setSpeed(currentspeed); 
  Serial.print ("Speed now  ");
  Serial.println (currentspeed);
  return currentspeed;
}

You’ll have to add the code for your LDRs in there.

The code in your first post looks like it would make the train go into reverse even if the sensor is not seen. I put the “go forward” code in a function that does not return until the sensor is seen.

// move train forward
void goFoward()
{
  int trainSpeed = 0;
  boolean sensorSeen = false;

  // move forward
  myMotor->run(FORWARD);

  // move until sensor is seen
  while( !sensorSeen )
  {
    // increment speed if not at max
    trainSpeed += (trainSpeed == 254) ? 0 : 1;
    myMotor->setSpeed(trainSpeed);

    // look for sensor
    sensorValue = analogRead(sensorPin);  // when sensor is seen / goes dim the motor should deccelerate
    sensorSeen == (sensorValue<400);

  } // while

  // ramp down speed
  for (trainSpeed=255; trainSpeed!=0; trainSpeed--) {
    myMotor->setSpeed(trainSpeed);  
    delay(10);

  }

}

void loop() {

  // move forward until sensor is seen
  goForward();

  myMotor->run(RELEASE); // motor should wait for set delay period
  delay(1000);

  myMotor->run(BACKWARD); //  motor should then return in opposite direction accelerating and decelerating for set delay period
  for (i=0; i<255; i++) {
    myMotor->setSpeed(i);  
    delay(10);
  }
  for (i=255; i!=0; i--) {
    myMotor->setSpeed(i);  
    delay(10);
  }
  myMotor->run(RELEASE); // motor should wait for set delay period then go back to the beginning and start over
  delay(1000);
}

You have to split things more. The loop has to run and just check everything. Other functions should handle the actions. And things like acceleration are best done with millis() (blink without delay). Or have a look at my [/url=https://github.com/sseptillion/Septillion_LM298]LM298 library. It's all about the idea. You can change the library to use the motorshield. Here I made a split between setSpeed and currentSpeed. And a update() function to check to see if it's time for the next speed step. I have to say I did not test it completely in this form, made some untested changes).

Also, you need a sensor on both ends. Now you have to guess when the train is at the end..

And just split the code more into the tasks you need and keep in mind the loop goes fast. Just act on what you need to do etc

Good afternoon,

So after some thought i've decided to use reed switches rather the light dependent resistors. This is because of not have uniform light source and also the reed switches are so small I can hide the under the track.

The next problem which i thought would be quite simple was to right some code using Boolean.

void loop(){
  
        digitalWrite(motor1Pin1,LOW); 
        digitalWrite(motor1Pin2, HIGH);
}
   if (digitalRead(reedSwitch1 == HIGH) && digitalWrite (motor1Pin1 == LOW) {
        delay (10);
        digitalWrite(motor1Pin1, HIGH); 
        digitalWrite(motor1Pin2, LOW);
}
    if (digitalRead(reedSwitch2 == HIGH) && digitalWrite(motor1Pin1, HIGH) {
        delay (10);
        digitalWrite(motor1Pin1,LOW); 
        digitalWrite(motor1Pin2, HIGH);
}

The locomotive is driving along forward with pin1 low and pin2 high. If the reed switch is detected and pin 1 is low it will stop / delay then reverse pin1 will go high and pin2 will go low. This will be the same at the other end when reedSwitch2 is detected.

Can anyone suggest an alternative way to do this. I had thought of calling it in void() but was not sure how to approach this.

I'm also getting the errors:

expected unqualified-id before numeric constant #define HIGH 0x1

in expansion of macro 'HIGH'

expected unqualified-id before 'if'

Thank you for any help in advance - Guy

   if (digitalRead(reedSwitch1 == HIGH) && digitalWrite (motor1Pin1 == LOW) {

Think about this. digitalRead accepts a pin number as a parameter. If reedSwitch1 is LOW, the result is of the comparison is 0. So you will read pin 0.

Is that what you wanted?

Also, digitalWrite requires two parameters.

We can't help with the errors, since you didn't post the lines that caused them.

Little OT
...decided to use reed switches rather the light dependent resistors..

Hope you know that reed switches operate when magnetic field is present.
I used to have HO, N and Z gauge but never bothered to play with reed switches as detector simply because they MAY detect the engine motor but will never detect the end of the train without putting a sizable magnet on the "caboose ".
And the physical orientation of the passing magnetic car needs to be adjusted too.

You should look into "reflective IR detector" technology. The one I am using is adjustable for distance sensitivity. It will detect object ( tested with pencil) from practically zero distance to about a meter.
No magnetic field needed and independent from ambient light too.
If interested I can send you more details.

Vaclav may have a good point there. I trust you will test these reed switches before implementing lots of them.

Guy_Whiteing:
Good afternoon,

So after some thought i've decided to use reed switches rather the light dependent resistors. This is because of not have uniform light source and also the reed switches are so small I can hide the under the track.

The next problem which i thought would be quite simple was to right some code using Boolean.

void loop(){

digitalWrite(motor1Pin1,LOW);
        digitalWrite(motor1Pin2, HIGH);
}
  if (digitalRead(reedSwitch1 == HIGH) && digitalWrite (motor1Pin1 == LOW) {
        delay (10);
        digitalWrite(motor1Pin1, HIGH);
        digitalWrite(motor1Pin2, LOW);
}
    if (digitalRead(reedSwitch2 == HIGH) && digitalWrite(motor1Pin1, HIGH) {
        delay (10);
        digitalWrite(motor1Pin1,LOW);
        digitalWrite(motor1Pin2, HIGH);
}

Nope! Try this instead:

boolean flag;

void setup(){
  digitalWrite(motor1Pin1,LOW);
  flag = true;//set flag when motor1Pin1 is LOW
  digitalWrite(motor1Pin2, HIGH);
}
void loop(){
   if (digitalRead(reedSwitch1)== HIGH && flag == true) {
        delay (10);
        digitalWrite(motor1Pin1, HIGH); 
         flag = false; //clear flag when motor1Pin1 is HIGH
        digitalWrite(motor1Pin2, LOW);
   }
    if (digitalRead(reedSwitch2) == HIGH && flag == false) {
        delay (10);
        digitalWrite(motor1Pin1,LOW); 
         flag = true;  //set flag when motor1Pin1 is LOW
        digitalWrite(motor1Pin2, HIGH);
   }
}

Note that you're testing whether the digitalRead of reedSwitch is HIGH, not the reed switch.