Train Lap Counter
d. bodnar 4-10-2021 -
5-12-2022
![]() |
This model train lap counter is designed to increment a counter
each time the train passes a sensor.
The counter can easily go into the 10's of millions of counts even
if your loco may not be up to the challenge! In addition the counter will ignore the sensor for 20 seconds after a train is detected to prevent additional counts should there be gaps between cars. The count will be retained if there is a power failure and restored when power is back.
|
Hardware The display is made up of 2 four digit LED matrix displays. They are available from Amazon and other sources. They use a MAX7219 controller and can be addressed by using an Arduino library.
The sensor is also available from Amazon. The unit has 3 wires that need to be connected, +5 volts (red), ground (black) and sensor output (yellow). The small screw (circled in green) can be used to adjust the range of the sensor. The red LED (yellow circle) lights when an object is detected. An Arduino Pro Mini provides the control of the unit and is connected to the sensor and display. There are two LEDs, one red and one blue, that give status information - this is explained below. |
Schematic![]() |
Setup Place the sensor next to the track at a distance that is sure to trigger it, an inch or two works well. If necessary adjust the sensitivity with the screw on the back of the sensor. Make sure that the black, red and yellow wires go to the black, red and white wires, respectively, on the extension cables. Apply power to the unit and run the train past the sensor. When the count number is incremented you will see the blue LED begin to flash. It will flash for about 20 seconds indicating that it will not react to the sensor for that time. Once the LED stops flashing the unit is ready to detect again. The red LED will light if power is lost. It indicates that the current count has been written to memory. Note that the display will be readable for a time after the power is lost.
|
Troubleshooting If the counter stops incrementing is the display is off the unit can be reset by pressing the small button on top of the Arduino (circled in yellow in this photo). The top of the clear enclosure can be removed by removing the two pieces of black tape that hold it on.
|
Software// This Version working for power failure & EEPROM read/write--- // d bodnar 4-09-2021 #include <EEPROM.h> //unsigned long storage; //create an unsigned long variable to store our result #include <MD_Parola.h> #include <MD_MAX72xx.h> #include <SPI.h> #define HARDWARE_TYPE MD_MAX72XX::FC16_HW //unsigned long nn = 0;//227654321; unsigned long TestValue = 31888; // was -1 max of 32767 for integers String thisString = String(TestValue); #define MAX_DEVICES 8 #define CLK_PIN 13 #define DATA_PIN 11 #define CS_PIN 10 const int IRSensorPin = 2; // the number of the IR sensor pin int buttonState; // the current reading from the input pin int lastButtonState = HIGH; // the previous reading from the input pin int reading = 0; int Power = 0; long PowerVal = 0; int LED_red = 5; int LED_blue = 4; MD_Parola P = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES); void setup(void) { pinMode(LED_red, OUTPUT); pinMode(LED_blue, OUTPUT); P.begin(); Serial.begin(9600); pinMode(IRSensorPin, INPUT); TestValue = EEPROM_readlong(0x00); //read it and put it in our newly created unsigned long variable Serial.print("From SETUP - TestValue=="); Serial.println(TestValue); //print it to check Serial.println(" d. bodnar 4-9-2021 V3.999"); digitalWrite(LED_blue, LOW); } void loop(void) { check_power_fail(); reading = digitalRead(IRSensorPin); if (reading != lastButtonState) { digitalWrite(LED_blue, HIGH); Serial.println("just before increment"); TestValue = TestValue + 1; if (reading != buttonState) { // TestValue = TestValue + 1; digitalWrite(LED_blue, LOW); for (int longPause = 0; longPause <= 125; longPause++) { // 125 about 20 seconds digitalWrite(LED_blue, LOW); delay(100); check_power_fail(); digitalWrite(LED_blue, HIGH); delay(100); } digitalWrite(LED_blue, LOW); TestValue = TestValue - 1; } lastButtonState = reading; } Serial.print("TestValue = "); Serial.println(TestValue); thisString = String(TestValue); int howLong = thisString.length(); thisString = "" + thisString; for (int xx = 0; xx <= (10 - howLong); xx++) { thisString = " " + thisString; } P.print(thisString); delay(200); } // read double word from EEPROM, give starting address unsigned long EEPROM_readlong(int address) { //use word read function for reading upper part unsigned long dword = EEPROM_readint(address); //shift read word up dword = dword << 16; // read lower word from EEPROM and OR it into double word dword = dword | EEPROM_readint(address + 2); return dword; } void check_power_fail() { PowerVal = analogRead(Power); Serial.print("PowerVal = "); Serial.println(PowerVal); if (PowerVal <= 500) { digitalWrite(LED_red, HIGH); EEPROM_writelong(0x00, TestValue); //write unsigned long number u want Serial.println("Wrote Test Value to EEPROM"); } else { digitalWrite(LED_red, LOW); } } //write word to EEPROM void EEPROM_writeint(int address, int value) { EEPROM.write(address, highByte(value)); EEPROM.write(address + 1 , lowByte(value)); } //write long integer into EEPROM void EEPROM_writelong(int address, unsigned long value) { //truncate upper part and write lower part into EEPROM EEPROM_writeint(address + 2, word(value)); //shift upper part down value = value >> 16; //truncate and write EEPROM_writeint(address, word(value)); } unsigned int EEPROM_readint(int address) { unsigned int word = word(EEPROM.read(address), EEPROM.read(address + 1)); return word; } |
Two large panels are joined together in this lap counter. Mounting holes are indicated by the red arrows.
The back of the board shows the circuitry.
Two holes in the acrylic cover allow access to the reset button which is at the end of the pencil - a toothpick or pencil can be inserted in the hole in the acrylic to do a reset should that ever be needed The lower hole, at the end of the small screwdriver, can be used to adjust the brightness. It is normally set to a medium or low brightness. Note that using high or maximum brightness will cause the unit and power supply to run hot.
|
TrainCounterEEPROM--POT-LEDdisplayP10-v1-8 // This Version working for power failure & EEPROM read/write--- // d bodnar 4-09-2021 /// working well 7-2-2021 #include <SPI.h> #include <DMD2.h> #include <fonts/Arial_Black_16.h> //#include <fonts/Droid_Sans_12.h> //#include <fonts/Droid_Sans_16.h>// not bad //#include <fonts/Arial14.h> //#include <fonts/SystemFont5x7.h> // small //#include <fonts/Droid_Sans_24.h> // too high const unsigned long COUNTDOWN_FROM = 518812; unsigned long counter = COUNTDOWN_FROM; SoftDMD dmd(2, 1); // DMD controls the entire display DMD_TextBox box(dmd, 0, 1); // "box" provides a text box to automatically write to/scroll the display #include <EEPROM.h> unsigned long TestValue = 12340;/// 3877206; // was -1 max of 32767 for integers String thisString = String(TestValue); const int IRSensorPin = 2; // the number of the IR sensor pin int buttonState; // the current reading from the input pin int lastButtonState = HIGH; // the previous reading from the input pin int reading = 0; int Power = 0; // pin a0 to show when power is lost long PowerVal = 0; int LED_red = 5; int LED_blue = 4; //MD_Parola P = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES); int potValue = analogRead(A1); //POT on pin a1 to set brightness void setup(void) { pinMode(LED_red, OUTPUT); pinMode(LED_blue, OUTPUT); // P.begin(); Serial.begin(9600); pinMode(IRSensorPin, INPUT); TestValue = EEPROM_readlong(0x00); //read it and put it in our newly created unsigned long variable Serial.print("From SETUP - TestValue=="); Serial.println(TestValue); //print it to check Serial.println(" d. bodnar 5-10-2022 V1.8"); digitalWrite(LED_blue, LOW); dmd.setBrightness(25); dmd.selectFont(Arial_Black_16); // dmd.selectFont(Arial14); // dmd.selectFont(Droid_Sans_24); dmd.begin(); box.print(" Ready"); } void loop(void) { potValue = analogRead(A1) / 4; //POT // Serial.print("pot="); Serial.println(potValue); dmd.setBrightness(potValue); check_power_fail(); reading = digitalRead(IRSensorPin); Serial.println(reading); int flag = 0; if (reading != lastButtonState) { digitalWrite(LED_blue, HIGH); box.print(" TRAIN "); delay(1000); Serial.println("just before increment"); Serial.println(TestValue); thisString = String(TestValue); // if (flag == 1) { //// box.print(thisString); // flag=1; // } if (reading != buttonState) { box.print(" "); box.print(thisString); TestValue = TestValue + 1; digitalWrite(LED_blue, LOW); for (int longPause = 0; longPause <= 15; longPause++) { // 125 about 20 seconds digitalWrite(LED_blue, LOW); delay(100); check_power_fail(); digitalWrite(LED_blue, HIGH); delay(100); } digitalWrite(LED_blue, LOW); // TestValue = TestValue - 1; } lastButtonState = reading; } Serial.print("TestValue = "); Serial.println(TestValue); delay(200); } // read double word from EEPROM, give starting address unsigned long EEPROM_readlong(int address) { //use word read function for reading upper part unsigned long dword = EEPROM_readint(address); //shift read word up dword = dword << 16; // read lower word from EEPROM and OR it into double word dword = dword | EEPROM_readint(address + 2); return dword; } void check_power_fail() { PowerVal = analogRead(Power); Serial.print("PowerVal = "); Serial.println(PowerVal); if (PowerVal <= 500) { box.print("Power -- "); digitalWrite(LED_red, HIGH); EEPROM_writelong(0x00, TestValue); //write unsigned long number u want Serial.println("Wrote Test Value to EEPROM"); } else { digitalWrite(LED_red, LOW); } } //write word to EEPROM void EEPROM_writeint(int address, int value) { EEPROM.write(address, highByte(value)); EEPROM.write(address + 1 , lowByte(value)); } //write long integer into EEPROM void EEPROM_writelong(int address, unsigned long value) { //truncate upper part and write lower part into EEPROM EEPROM_writeint(address + 2, word(value)); //shift upper part down value = value >> 16; //truncate and write EEPROM_writeint(address, word(value)); } unsigned int EEPROM_readint(int address) { unsigned int word = word(EEPROM.read(address), EEPROM.read(address + 1)); return word; }
|