New user needs help with train program

I’m still clueless. My mind just doent do code like this after doing 30 years of ladder logic. I may just give up on this.

Would you help me some more? I can’t see me using these anywhere else in my home, I have PLCs for that. I’m using this only because of its size.

Thanks

It would be a shame to give up now - you're about 80% of the way there by the look of it.

I will look at it again. But this function thing makes no sense to me to save scan time. Interrupts seem better, but I have no clue as to how to do it and dont understand most of the code at all.

OK, try this:

// Train Controller V1.1

const byte MotorVoltagePin = A0; // Motor Voltage via 0-25vdc divider to 0-5vdc
const byte CabLedPin = 2;        // Cab Light LED
const int  threshold = 400;      // Motor Voltage at which the LED goes off
const byte encoderPin = 3;       // the pin that the encoder is attached to
const byte FanRelayPin = 3;      // the pin that the Relay is attached to

int encoderCounter = 0;         // counter for the number of encoder stripes
int encoderState = 0;           // current state of the encoder
int lastEncoderState = 0;       // previous state of the encoder

void setup()
{
  // initialize the LED pin as an output:
  pinMode(CabLedPin, OUTPUT);
  // initialize the Encoder pin as an input:
  pinMode(encoderPin, INPUT);
  // initialize the Relay as an output:
  pinMode(FanRelayPin, OUTPUT);
  // initialize serial communication:
  Serial.begin(9600);
}

void loop()
{
DoCabLight();
DoChuffStuff();
}

void DoCabLight()
{
if (MotorIsOn())
  {
    digitalWrite(CabLedPin, HIGH);
  }
  else
  {
    digitalWrite(CabLedPin, LOW);
  }
}

void DoChuffStuff()
{
if (MotorIsOn())
  {
  // Chuff Logic
  encoderState = digitalRead(encoderPin);
  // compare the encoderState to its previous state
  if (encoderState != lastEncoderState)
    {
    // if the state has changed, increment the counter
    if (encoderState == HIGH)
      {
      // if the current state is HIGH then the encoder went from off to on:
      encoderCounter++;
      Serial.println("on");
      Serial.print("number of encoder pulses: ");
      Serial.println(encoderCounter);
      }
    else
      {
      // if the current state is LOW then the encoder went from on to off:
      Serial.println("off");
      }
    // Delay a little bit to avoid bouncing
    delay(50);
    }
  // save the current state as the last state, for next time through the loop
  lastEncoderState = encoderState;
  // turns on the RELAY every four pulses by checking the modulo of the encoder counter. 
  // The modulo function gives you the remainder of the division of two numbers:
  if (encoderCounter % 4 == 0)
    {
    digitalWrite(FanRelayPin, HIGH);
    }
  else
    {
    digitalWrite(FanRelayPin, LOW);
    }
  }
else
  {
    digitalWrite(FanRelayPin, LOW);        
  }
}

bool MotorIsOn()
{
int MotorVoltage = analogRead(MotorVoltagePin);
// if the analog value is high enough, motor has power
return MotorVoltage > threshold;
}

I just noticed a bug. The encoder pin and relay pin are the same, which seems unlikely.

Saving scan time isn't the purpose, in fact, it ever so slightly degrades scan time because of the overhead involved. It's to compartmentalize things so all the gory details aren't in loop(), just a series of function calls. This is to aid readability, organization, troubleshooting, modification, etc.

Lots of PLCs these days have a subroutine call in the instruction set. Functions are to C++ what JSRs are to a PLC.

Remedied here: New user needs help with train program - #3 by prrguy

Thanks.

Thank you very much. I still can’t wrap my head around it though. I know I should be happy you did the work for me, but it really makes me upset that I can’t learn a new trick. The “click” moment just hasn't happened.......

Doing the headlight dim then bright based on motor voltage should be easy, but I can’t see where in the code to add it, let alone how to really do it. Copy and pasting code was easy, but as you pointed out, it’s going to be slow.

You can follow the same pattern as DoCabLight. Most of the code is the same except that you will need analogWrite instead of digitalWrite.

Alternatively, if you're not planning to use Arduinos in the future, I can just do it for you.

I’m going to give it a shot. Thanks for everything so far.

// Train Controller V3.0

const byte MotorVoltagePin = A0; // Motor Voltage via 0-25vdc divider to 0-5vdc
const byte CabLedPin = 2;        // Cab Light LED
const int  threshold = 400;      // Motor Voltage at which the LED goes off
const byte encoderPin = 3;       // the pin that the encoder is attached to
const byte FanRelayPin = 4;      // the pin that the Relay is attached to
const byte HeadlightPin = A1;     // the pin the headlight is attached to

int encoderCounter = 0;         // counter for the number of encoder stripes
int encoderState = 0;           // current state of the encoder
int lastEncoderState = 0;       // previous state of the encoder

void setup()
{
  // initialize the LED pin as an output:
  pinMode(CabLedPin, OUTPUT);
  // initialize the Encoder pin as an input:
  pinMode(encoderPin, INPUT);
  // initialize the Relay Pin as an output:
  pinMode(FanRelayPin, OUTPUT);
  // initialize serial communication:
  Serial.begin(9600);
}

void loop()
{
  DoCabLight();
  DoChuffStuff();
  DoHeadlight();
}

void DoCabLight()
{
  if (MotorIsOn())
  {
    digitalWrite(CabLedPin, HIGH);
  }
  else
  {
    digitalWrite(CabLedPin, LOW);
  }
}


void DoHeadlight()
{
  if (MotorIsOn())
  {
    analogWrite(HeadlightPin, 1023);
  }
  else
  {
    analogWrite(HeadlightPin, 600);
  }
}









void DoChuffStuff()
{
  if (MotorIsOn())
  {
    // Chuff Logic
    encoderState = digitalRead(encoderPin);
    // compare the encoderState to its previous state
    if (encoderState != lastEncoderState)
    {
      // if the state has changed, increment the counter
      if (encoderState == HIGH)
      {
        // if the current state is HIGH then the encoder went from off to on:
        encoderCounter++;
        Serial.println("on");
        Serial.print("number of encoder pulses: ");
        Serial.println(encoderCounter);
      }
      else
      {
        // if the current state is LOW then the encoder went from on to off:
        Serial.println("off");
      }
      // Delay a little bit to avoid bouncing
      delay(50);
    }
    // save the current state as the last state, for next time through the loop
    lastEncoderState = encoderState;
    // turns on the RELAY every four pulses by checking the modulo of the encoder counter.
    // The modulo function gives you the remainder of the division of two numbers:
    if (encoderCounter % 4 == 0)
    {
      digitalWrite(FanRelayPin, HIGH);
    }
    else
    {
      digitalWrite(FanRelayPin, LOW);
    }
  }
  else
  {
    digitalWrite(FanRelayPin, LOW);
  }
}

bool MotorIsOn()
{
  int MotorVoltage = analogRead(MotorVoltagePin);
  // if the analog value is high enough, motor has power
  return MotorVoltage > threshold;
}

The above is my attempt, please let me know if its OK. Thanks

Very close. AnalogWrite doesn't take numbers that big though - biggest is 255.

Also, only a subset of pins can do it. Look at the docs to find out which ones work on whatever Arduino you have.

So 0-100% PWM is 0-255 for arduino?

I have the pro micro

Yup, 0-255.

Looks like pin 5 will do PWM.

So I have a couple more things to do.

  1. There is headlight powered by an existing controller on the train. 16vac from a FET. I’d like to sence that via a digital pin to enable the headlight logic in the Arduino.

  2. The chuff code should always run as it provides data to the sound system as to when to make the chuff sound. But the existing controller has a smoke unit FET 18vac to supply power to the smoke unit. I’d like to be able to control the fan output with a digital input.

  3. The chuff may need to turn on and off the fan for longer periods when moving slow and faster when moving faster.

Any help would be great!

Is what you have so far working?

How do these trains usually get D.C. From an AC supply?

How does the train know how fast it's going?

I haven’t tested it yet, I’m breadboarding it soon.

The radio receiver/ speed controller has a bridge rectifier on it, speed is from a tape encoder strip picked up with a IR led, receiver pair usually with the older controller. The newer speed controller used back emf to do cruise control load based speed control.