Newbie Having a programming issue...on HO train project

HI,

I'm new to Arduino and C++ programming. I did some programming in the past but it was years ago!

I have a small problem with a project I'm working on for my HO train layout. I'm putting together an Axle counter for my trains so I can simulate a defect detector. The way it works is I have 3 IR sensors (TCRT5000) hooked to a NANO and a 1602 I2C display. Two sensors are pointing towards the track to see if a train is on the track and detects it in either direction. In between them is a sensor that counts the wheels. They're hooked to D2 for count sensor and D4 & D6 for the train sensor. Once a train leaves the outer 2 sensors, it displays the total axle count until another train comes by. Then the count resets and it counts all over again.

The circuit works exactly the way I want it too but, it misses the first wheel every time and for the life of me I can't figure out why? The first wheel goes by and it isn't counted but the sensor LED flashes so it sees the first wheel. Then the second wheel goes by and it's counted as wheel #1?

I would appreciate any help as I'm so close to making this work. It'll be such a fun addition to my layout.

Thanks in advance,
Bob.......

Here's my code so far:

/* I2C LCD with Arduino example code. More info: https://www.makerguides.com */

// Include the libraries:
// LiquidCrystal_I2C.h: https://github.com/johnrickman/LiquidCrystal_I2C
#include <Wire.h>               // Library for I2C communication
#include <LiquidCrystal_I2C.h>  // Library for LCD
bool occupied = false;                  // is train in sensor range
int rev = 0;                    // wheel counter
int axle = 0;                   // convert to axles
int total_axles = 0;            // total number of axles
int lastState = HIGH;
int state;
int axlePin = 2;                 // IR axle counter connected to Digital pin 2
int eastboundPin = 4;                 // IR train detector left connected to Digital pin 4
int westboundPin = 6;                 // IR train detector right connected to Digital pin 6
int axleInputState;
int eastBoundInputState;
int westBoundInputState;

// Wiring: SDA pin is connected to A4 and SCL pin to A5.
// Connect to LCD via I2C, default address 0x27 (A0-A2 not jumpered)
LiquidCrystal_I2C lcd = LiquidCrystal_I2C(0x27, 16, 2); // Change to (0x27,20,4) for 20x4 LCD.

void setup() {
  // Initiate the LCD:
  lcd.init();                   // init LCD
  lcd.backlight();
  lcd.print("Booting");         // just for fun!
  lcd.setCursor(0, 1);
  lcd.print("Please wait...");
  delay (2000);
  lcd.clear();                  // setup initial display
  lcd.setCursor(10, 0);
  lcd.print("Axles");
  lcd.setCursor(11, 1);
  lcd.print("0");
  pinMode(axlePin, INPUT);       // set digital pin 2 as input for wheel counter
  pinMode(eastboundPin, INPUT);       // set digital pin 4 as input for eastbound train detector
  pinMode(westboundPin, INPUT);       // set digital pin 6 as input for westbound train detector
}

void loop()

{

  eastBoundInputState = digitalRead(eastboundPin);   // read the eastbound train input to check for a train
  westBoundInputState = digitalRead(westboundPin);   // read the westbound train input to check for a train
  if (eastBoundInputState == LOW || westBoundInputState == LOW) {            // train is detected in either direction
    lcd.setCursor(1, 0);
    lcd.print("Train");           // print train here
    lcd.setCursor(0, 1);
    lcd.print("Detected");
    if (occupied == true) {             // if there's a train reset screen to "0"
      lcd.setCursor(11, 1);
      lcd.print("0    ");         // set screen to "0"
    }
    occupied = false;                    // reset train moved

  }
  else {                          // train not detected
    lcd.setCursor(1, 0);
    lcd.print("     ");           // clear the screen
    lcd.setCursor(0, 1);
    lcd.print("        ");
    total_axles = axle;           // total axles
    if (eastBoundInputState == HIGH && westBoundInputState == HIGH && occupied == false) {   // if train has moved print total axle count
      lcd.setCursor(11, 1);
      lcd.print(total_axles);
    }
    total_axles = 0;              // reset total axles
    occupied = true;                    // train has moved
    axle = 0;                     // clear axle count
    rev = 0;                      // clear wheel counter

    if (eastBoundInputState == LOW and axleInputState == LOW and westBoundInputState == LOW and occupied == true) {  // if train has moved display total axles till next train arrives
      lcd.setCursor(11, 1);
      lcd.print(total_axles);
    }
  }

  state = digitalRead(axlePin);          // get input state of wheel counter sensor
  if (lastState == LOW && state == HIGH && occupied == false)    // only count on a LOW-> HIGH transition
  {
    axle = (rev++);                         //calculates number of axles
    lcd.setCursor(11, 1);
    lcd.print(axle);                        // display axle count
  }
  lastState = state;                        // remember last state
}

Please follow the advice given in the link below when posting code, in particular the section entitled 'Posting code and common code problems'

Use code tags (the </> icon above the compose window) to make it easier to read and copy for examination

In this case, please also use auto-indentation by typing ctrl-T in the IDE before uploading. It's a monolithic (one piece) sketch, hard to follow the code blocks and decent indentation will help a lot with that.

HI,

I reposted it using the guidelines above. I hope this makes it easier to read?

Thanks....

Thanks. I'm looking at it. One small improvement you can carry forward forever, why not name things for what they are instead of assigning them numbers, e.g.

int axlePin = 2;                 // IR axle counter connected to Digital pin 2
int leftPin = 4;                 // IR train detector left connected to Digital pin 4
int rightPin = 6;                 // IR train detector right connected to Digital pin 6
int axleInputState;
int leftInputState;
int rightInputState;

instead of:

int IR2Pin = 2;                 // IR axle counter connected to Digital pin 2
int IR1Pin = 4;                 // IR train detector left connected to Digital pin 4
int IR3Pin = 6;                 // IR train detector right connected to Digital pin 6
int Input2;
int Input1;
int Input3;

Also you should use boolean variables instead of 0 and 1 for logical variables:

bool moved = false;                  // is train in sensor range
...
    if (moved == true) {             // if there's a train reset screen to "0"

instead of:

int moved = 0;                  // is train in sensor range
...
    if (moved == 1) {             // if there's a train reset screen to "0"

This uses a single '&' which is a bitwise and operator, not the right choice here.

if (Input1 == LOW & Input2 == LOW & Input3 == LOW & moved == 1) {  // if train has moved display total axles till next train arrives

To avoid confusion, use the modern 'not', 'and', and 'or' instead:

if (Input1 == LOW and Input2 == LOW and Input3 == LOW and moved == 1) {  // if train has moved display total axles till next train arrives

If lastState is HIGH when the 1st axle should be counted, it gets lost. You should 'lastState' explicitely set to LOW when a train is detected and counting has to start.

...
    if (moved == 1) {             // if there's a train reset screen to "0"
      lcd.setCursor(11, 1);
      lcd.print("0    ");         // set screen to "0"
    }
    moved = 0;                    // reset train moved
    lastState = LOW;  //<<<<<<<<<<<< you can insert here
  }
  else {                          // train not detected
...

...or find out why lastState wasn't set to LOW when the last pulse of the previous train passed...

I just tried that and as soon as a train hits the first sensor the count just started counting at warp speed??

Thanks for the programming advice. I will try to remember to do that!

It seems that the 'idle state' of axelPin is HIGH. But why then query the positive edge?

The reason I did that was so it would only trigger once per wheel count. I tried other ways but as soon as the first wheel hit the sensor the count just started. It was kinda my way of doing a latch.

Well, I don't really understand. But of course I don't know how your axle counter really works.

I think you've combined too many tests in some of the conditional blocks. You need a more top down structure. I know that sounds vague, but I've seen it so many times. Once you clearly define the problem and the program states, coding becomes fairly obvious. You already have a one-variable state machine, you might just need to fix it, or you might need to add additional states.

When I try to identify the problem in a piece like this, I often can't follow the program logic because it doesn't have the kind of self-evident, problem coherent structure that I'm used to.

Then I ask, what are you trying to do, and more or less re-write the entire thing.

Naming the variables well is something you really need to do if you are presenting to other people to read. For example, what does "moved" mean in the context of a train? I think it's not really "moved" but maybe "present" or "occupied". As you read, it sinks in better if it speaks for itself.

Yes You're correct! I took your advice and updated the variables to make more sense. The last code I wrote was back in the mid 90's for a data collection system in a steel mill. I guess I should take a refresher course! :frowning:
As far as my logic this is what was in my head:

  1. Is there a train coming either east or westbound?
  2. If not, show last axle count till one shows up.
  3. if yes, reset total axle count.
  4. count the axles
  5. is the train gone?
  6. if not keep counting.
  7. if yes the save last count, display it, clear everything to set up for next train.

I did try something that does work. I just have to print (totalaxles + 1) but I think that's kinda cheating.

Normally it's not a good idea to edit original posts. It makes the replies seem like nonsense. In this case, you did a good job I think, so we'll not dwell on that.

I was thinking, your axle counter can be defined in the same way as a simple push button. It goes active, inactive, needs debouncing, and needs an edge detection.

So why not have a look at switch code? There are digital examples with the IDE that show how to properly read a switch. That would get you a working axle counter.

You are "gating" the pulses with an occupancy detector. That has to be integrated with the pulse logic, but should be no big deal.

I believe this viewpoint should simplify your programming logic.

Coming, or already in the axle counter? See the difference in semantics?

You have some "monkey business" with a strange variable called 'rev'. It seems to do nothing, really, as its value always is assigned to "axle" anyway. Notice I haven't had a chance to update the names, I downloaded your sketch earlier, and still have it open. :slight_smile: Not sure what it's called now...

This goes back to the pushbutton switch analogy I made. You can find many examples of working code to do this, written for pushbuttons. It would be easy to adapt for your sensor input. Just throw it out and start from scratch.

The definition in reply #14 is immensely useful, thanks.

I'll have to look into that! Never thought about it that way.
The "REV" statement to me was just thinking of counting each wheel as a revolution. Each wheel would be one REV and I just keep adding them together.

Well, the actual detection interval might be less than one wheel revolution. Also the wheels might rotate more than one revolution between wheel detection events. I think it's a red herring. It's really only detection pulses that you need to count.