Automated Tram - Code Help

I am creating a Lego (older 9 volt) Tram to run back and forth on a straight track in our holiday village display. I am not a programmer but have been searching and testing and learning a lot. I think I am getting close using the following code. I wont be able to test it out for a few days but wondered if you experts could spot any issues before testing again. Thank you!

//
// pin conntection setups
//
const int LD_A = 6; // detector a
const int LD_B = 7; // detector b
const int PWM = 3; // set pin 3 to PWM
const int DIR = 12; // set pin 12 to DIR
const int BRAKE = 9; // set pin 9 to BRAKE

const int STAY_TIME = 4 * 1000;

const int RIGHT_STOP = 0;
const int LEFT = 1;
const int LEFT_STOP = 2;
const int RIGHT = 3;
boolean ignore_detector_a = false;
boolean ignore_detector_b = false;
unsigned long time_to_leave = 0;
int train_control = RIGHT;

void setup() {
pinMode(LD_A, INPUT);
pinMode(LD_B, INPUT);
pinMode(PWM, OUTPUT);
pinMode(DIR, OUTPUT);
pinMode(BRAKE, OUTPUT);
}

void loop() {
int L_detected = LOW;
if(!ignore_detector_a) {
L_detected = digitalRead(LD_A);
if(L_detected) {
ignore_detector_a = true;
ignore_detector_b = false;
}
} else if(!ignore_detector_b) {
L_detected = digitalRead(LD_B);
if(L_detected) {
ignore_detector_b = true;
ignore_detector_a = false;
}
}

if (digitalRead(LD_A == LOW)) {
train_control = RIGHT_STOP;
}

if(digitalRead(LD_B == LOW)) {
train_control = LEFT_STOP;
}

program();

delay(20);
}

void program() {
switch(train_control) {
case RIGHT_STOP:
digitalWrite(DIR,HIGH); // train to move right
digitalWrite(BRAKE,HIGH); // brakes on to stop
analogWrite(PWM,0); // power set to 0 to stop
time_to_leave = millis() + STAY_TIME;
train_control = LEFT; // change state to LEFT

break;

case LEFT:
if(millis() > time_to_leave) {
digitalWrite(DIR,LOW); //train to move left
digitalWrite(BRAKE,LOW); //brakes off
analogWrite(PWM,100); //power set to 100

break;

case LEFT_STOP:
digitalWrite(DIR,LOW); //train to move left
digitalWrite(BRAKE,HIGH); //brakes on
analogWrite(PWM,0); //power set to 0
time_to_leave = millis() + STAY_TIME;
train_control = RIGHT; //change state to move right

break;

case RIGHT:
if(millis() > time_to_leave) {
digitalWrite(DIR,HIGH); //moves train right
digitalWrite(BRAKE,LOW); //brakes off
analogWrite(PWM,100); //set power to 100
}}}}

// End

I should also add I am using an Arduino UNO, Arduino Motor Control Shield, and (2) digital light detectors each placed and the end of the track.

Hello
Post the sketch well formated by using cntl T, with useful comments and in so called code tags "</>" again.

Trolley_Program.ino (2.2 KB)

Is this better? Again, I am just getting started in this world.

copy the sketch by using
grafik

to be found in this editor.

//
// pin conntection setups
//
const int LD_A = 6; // detector a
const int LD_B = 7; // detector b
const int PWM = 3; // set pin 3 to PWM
const int DIR = 12; // set pin 12 to DIR
const int BRAKE = 9; // set pin 9 to BRAKE


const int STAY_TIME = 4 * 1000;


const int RIGHT_STOP = 0;
const int LEFT = 1;
const int LEFT_STOP = 2;
const int RIGHT = 3;
boolean ignore_detector_a = false;
boolean ignore_detector_b = false;
unsigned long time_to_leave = 0;
int train_control = RIGHT;



void setup() {
  pinMode(LD_A, INPUT);
  pinMode(LD_B, INPUT);
  pinMode(PWM, OUTPUT);
  pinMode(DIR, OUTPUT);
  pinMode(BRAKE, OUTPUT);
}

void loop() {
  int L_detected = LOW;
  if(!ignore_detector_a) {
    L_detected = digitalRead(LD_A);
    if(L_detected) {
      ignore_detector_a = true;
      ignore_detector_b = false;
    }
  } else if(!ignore_detector_b) {
    L_detected = digitalRead(LD_B);
    if(L_detected) {
      ignore_detector_b = true;
      ignore_detector_a = false;
    }
  }

  if (digitalRead(LD_A == LOW)) {
    train_control = RIGHT_STOP;
  }

  if(digitalRead(LD_B == LOW)) {
    train_control = LEFT_STOP;
  }

  program();

  delay(20);
}

void program() {
  switch(train_control) {
  case RIGHT_STOP:
    digitalWrite(DIR,HIGH);    // train to move right
    digitalWrite(BRAKE,HIGH); // brakes on to stop
    analogWrite(PWM,0);  // power set to 0 to stop
    time_to_leave = millis() + STAY_TIME;
    train_control = LEFT; // change state to LEFT
   
    break;
  
  case LEFT:
    if(millis() > time_to_leave) {
      digitalWrite(DIR,LOW);  //train to move left
      digitalWrite(BRAKE,LOW); //brakes off
      analogWrite(PWM,100); //power set to 100
  
    break;
  case LEFT_STOP:
    digitalWrite(DIR,LOW); //train to move left
    digitalWrite(BRAKE,HIGH); //brakes on
    analogWrite(PWM,0); //power set to 0
    time_to_leave = millis() + STAY_TIME;
    train_control = RIGHT; //change state to move right
    
    break;
    
  case RIGHT:
    if(millis() > time_to_leave) {
      digitalWrite(DIR,HIGH); //moves train right
      digitalWrite(BRAKE,LOW); //brakes off
      analogWrite(PWM,100); //set power to 100
  }}}}
 

// End

Can you please add an explanation of the program logic, in plain English terms? Give us a run down on how it works...

I could decode it blindfolded, intoxicated and with annoying music playing in the background, but I would rather you make it easy for me.

C/C++ code does not "speak for itself". Except to nerdy types who can't tie their own shoelaces. :slight_smile:

Okay, there's one error, I guess there could be more. This says, compare the pin value LD_B which is 6 with LOW which is 1, use it as a pin number for the digitalRead(). So it will compare "6==1" and find "false" which is equal to zero, so it will actually read pin 0.

consider

#undef MyHW
#ifdef MyHW
const int DetRight = A1;
const int DetLeft  = A2;

const int PWM   = 11;
const int DIR   = 10;
const int BRAKE = 12;

#else
const int DetRight = 6;
const int DetLeft  = 7;
const int PWM      = 3;
const int DIR      = 12;
const int BRAKE    = 9;
#endif

enum { STOP, RUN };
int state =STOP;


enum { RIGHT = HIGH, LEFT = LOW };
int dir = RIGHT;

unsigned long msec;
unsigned long msecLst;
const unsigned long STAY_TIME = 4 * 1000;

// -----------------------------------------------------------------------------
void
stop (void)
{
    analogWrite  (PWM,   0);
    digitalWrite (BRAKE, HIGH);
    msecLst = msec;
    state   = STOP;
    Serial.println ("stop");
}

// -----------------------------------------------------------------------------
void
loop (void)
{
    msec = millis ();

    switch (state)  {
    case STOP:
        if ((msec - msecLst) > STAY_TIME)  {
            digitalWrite (DIR, dir);

            analogWrite  (PWM,   255);
            digitalWrite (BRAKE, LOW);
            state = RUN;
            Serial.println ("start");
        }
        break;

    case RUN:
        if (RIGHT == dir && LOW == digitalRead (DetRight))  {
            Serial.println (" right");
            dir = LEFT;
            stop ();
        }
        else if (LEFT == dir && LOW == digitalRead (DetLeft))  {
            Serial.println (" left");
            dir = RIGHT;
            stop ();
        }
        break;
    }
}

// -----------------------------------------------------------------------------
void
setup (void)
{
    Serial.begin (9600);

    pinMode (DetRight, INPUT_PULLUP);
    pinMode (DetLeft,  INPUT_PULLUP);

    pinMode (PWM,  OUTPUT);
    pinMode (DIR,  OUTPUT);
    pinMode (BRAKE, OUTPUT);
}

What is the code setting the ignore variables for? As far as I can see, they are never used.

Just as well, it's a terrible idea.

when a train reaches the end and a detector becomes active, that detector needs to be ignore, someway or another, until the train has moved away from it to prevent immediately stopping

Thank you all.

Yes so the train needs to move to the end of the line, trip a sensor so it stops, then move the other direction until hitting sensor B at the other end, then repeat.

I have had success making the train run forward and backward on a complete track without sensors using just time delays but when I turn it into a trolley or tram and want it to run continuously I run into trouble due to my lack of experience.

I found the bones of the code above from a model train forum. I have adapted it to run my lego train. The trouble is when I test my new theories or code, if it doesn't work, I have no idea where to start looking for the problem.

I can follow that. So instead of LOW, what should I use to read the sensor is tripped and I want the Stop program to run.

I think what you want is

 if( digitalRead( LD_B) == LOW ) {

Unless you're enjoying the process and want to improve your programming skills, I suggest that you switch over to the code @gcjr wrote for you.

Thank you so much for this. A couple questions:
-my light sensors only have digital outputs. Can I still plug them into A1 and A2 or does this need to change to open Digital spots like 6 and 7? Does this change the code any where else?
-Why do we define PWM, DIR and BRAKE as 11,10 and 12 then redefine them after #else?

I am actually really enjoying this but I will definitely switch over. My code may be a little too complicated for me.

I really wish I would have gotten into this earlier in life, I am having a blast learning and frustrating myself. haha. The lego train has been a great beginner tool for learning rudimentary coding.

almost all pins can be used as general purpose I/O, GPIO

the c-preprocessor commands allow different values to be easily selected. i needed different values to test on my hardware

Hello
Please do yourself the favor and "think" in datablocks and subroutines working on this blocks.
Thus will generate slim programs to be coded fast and could be debugged fast too.

notabene:
You may study the IPO-Model as startpoint for your project.