Wheel Direction of Rotation Detector
d. bodnar  revised 04-04-2015

Introduction
Recently a fellow garden railroader asked me if I could come up with a way for a passenger car to determine which way it was going so that appropriate lights could be illuminated.  The car is an observation car (from USA Trains, I believe) that is designed to illuminate exterior lights one way when it is moving forward and a different way when running backwards.

This is easily accomplished if the train operates from standard DC track power.  Unfortunately he was running either DCC or constant track power with radio control so that the polarity on the track always remains the same.  The same issue would arise when using battery power.

He had built a mechanism that threw a switch one way when running forward and the other way when going backwards but it put a good bit of drag on the car.  He was looking for a solution that produced little or no drag.  He had also considered a modification to the coupler so that a forward pull could be detected but that option was not practical with the couplers that he was using.

After giving this some thought (over a few days and a few bike rides!) I came up with a method using three magnets and a reed switch.  If the magnets are mounted as shown below the sequence of pulses that the reed switch produced was different based on direction of travel.

This diagram shows how the magnets are mounted on the wheel.  Three magnets are placed.  Two are 60 degrees apart and the third is 120 degrees away from the second, leaving a 180 degree gap between the third and first magnet.

When the wheel turns in a clockwise direction the pulses are short, long, medium, short, long medium.  When going counter clockwise they are short, medium, long, short, medium, long.  By determining which pulse is the long one and comparing that to the one that follows it the direction can be figured out.

Test Rig
This mechanism was used for testing.  The small motor (upper left) turns the wheel either clockwise or counter clockwise.  The DPDT switch (circled in black) determines the direction or rotation.  The magnets (two of the three are circled in red) pulse the reed switch (circled in yellow).

Circuit
The reed switch is mounted next to the magnets on the wheel.  There is a duplicate switch on the prototype board that can be used for testing.  The yellow LED lights as the wheel turns and detects magnets.  One red LED lights if the wheel turns in one direction and the other red LED lights if it turns the other way.  After a 5 second period without any magnet detection passes both LEDs are turned off.

The diagram shows the circuit with an Arduino Uno but just about any Arduino should work.  This is especially important if space is an issue.

Software -1
Initial testing used the example program that comes with the Arduino called "Button".  It simply blinks an LED when a button (or reed switch) closes.  I attached one wire from the reed switch to pin 2 on the Arduino.  A 10K resistor pulled that pin low by going to ground.  The other wire from the reed switch connected to + 5 volts. 
/*
  Button
 
 Turns on and off a light emitting diode(LED) connected to digital  
 pin 13, when pressing a pushbutton attached to pin 2. 
 
 
 The circuit:
 * LED attached from pin 13 to ground 
 * pushbutton attached to pin 2 from +5V
 * 10K resistor attached to pin 2 from ground
 
 * Note: on most Arduinos there is already an LED on the board
 attached to pin 13.
 
 
 created 2005
 by DojoDave <http://www.0j0.org>
 modified 30 Aug 2011
 by Tom Igoe
 
 This example code is in the public domain.
 
 http://www.arduino.cc/en/Tutorial/Button
 */

// constants won't change. They're used here to 
// set pin numbers:
const int buttonPin = 2;     // the number of the pushbutton pin
const int ledPin =  13;      // the number of the LED pin

// variables will change:
int buttonState = 0;         // variable for reading the pushbutton status

void setup() {
  // initialize the LED pin as an output:
  pinMode(ledPin, OUTPUT);      
  // initialize the pushbutton pin as an input:
  pinMode(buttonPin, INPUT);     
}

void loop(){
  // read the state of the pushbutton value:
  buttonState = digitalRead(buttonPin);

  // check if the pushbutton is pressed.
  // if it is, the buttonState is HIGH:
  if (buttonState == HIGH) {     
    // turn LED on:    
    digitalWrite(ledPin, HIGH);  
  } 
  else {
    // turn LED off:
    digitalWrite(ledPin, LOW); 
  }
}

 

Logic Analyzer Test
I connected my Saleae Logic Analyzer to the output LED on the Arduino and made the following observations.

Wheel clockwise rotation
Note that the large period (1) has a short one (3) to the right and a medium length one to the left (2).

Wheel counter clockwise rotation
Note that the large period (1) has a medium one (2) to the right and a short length one to the left (3).

Note that the two pulse patterns are surely different.  All that remains is to write a computer program for the Arduino to differentiate between them.

Software -2
The first program that I wrote measures the length of each pulse gap and sends it to the serial terminal:
/*
 Sample sketch to show pulses from reed switch on wheel to determine if the wheel turns CW or CCW
 three magnets on wheel at 1/6ths - two together and one two away
 
 NOTE: this version gives a good result and definitely discriminates between forward and backward
 now to do the math and get it to report properly!
 
 */

const int buttonPin = 2;     // the number of the pushbutton pin
const int ledPin =  13;      // the number of the LED pin

// variables will change:
int buttonState = 0;         // variable for reading the pushbutton status
int minusCount = 0;
int plusCount = 0;
int firstPlusFlag =0;
int firstMinusFlag = 0;
int countValue[6];
int y=0;
void setup() {
  Serial.begin(115200);
  // initialize the LED pin as an output:
  pinMode(ledPin, OUTPUT);      
  // initialize the pushbutton pin as an input:
  pinMode(buttonPin, INPUT);     
  Serial.println("forward / backward wheel test - 3-29-15");
}

void loop(){
  buttonState = digitalRead(buttonPin);
  if (buttonState == HIGH) {     
    plusCount = plusCount + 1;
    firstMinusFlag=0;
    if (minusCount !=0)    {
      Serial.print("cnt ");
      Serial.println(minusCount); 
      countValue[y]=minusCount;
      y=y+1;
    }
    minusCount = 0;
    digitalWrite(ledPin, HIGH);  
  } 
  else {
    minusCount = minusCount + 1;
    plusCount = 0;
    digitalWrite(ledPin, LOW); 
  }

  if (y==6){
    int big=0;
    for(int y=0;y<6;y++){
      Serial.print(countValue[y]);
      Serial.print("  ");
      if (countValue[y] >= big){
        big = countValue[y];

      }
     //// countValue[y]=0;
    }
    Serial.println("");    
    Serial.print("big = ");
    Serial.println(big);
    big=0;
    y=0;

    // }
  }
}
Working Code - lights Red LED based on direction of travel - the wheel has to turn a bit more than one revolution
to detect the direction of travel.  Version  Wheel_CWorCCW_v_2_0_Working
/*
 Sample sketch to show pulses from reed switch on wheel to determine if the wheel turns CW or CCW
 three magnets on wheel at 1/6ths - two together and one two away
 
 */

const int buttonPin = 2;     // the number of the pushbutton pin
const int ledPin =  13;      // the number of the LED pin
const int ledDirection =  8;
const int ledDirection2 =  7;
// variables will change:
int buttonState = 0;         // variable for reading the pushbutton status
long minusCount = 0;
int firstPlusFlag =0;
int firstMinusFlag = 0;
long countValue[12];
long maxCountValuePosition=0;
long maxCountValuePosition2=0;
int direction =0;
long big=0;
int y=0;
unsigned long time;
void setup() {
  Serial.begin(9600);
  // initialize the LED pin as an output:
  pinMode(ledPin, OUTPUT);      
  pinMode(ledDirection, OUTPUT); 
  pinMode(ledDirection2, OUTPUT); 

  // initialize the pushbutton pin as an input:
  pinMode(buttonPin, INPUT);     
  Serial.println("forward / backward wheel test V1.4x  - 3-30-15");
  time = millis();
}

void loop(){
  if(millis()-time >= 5000){  // if 5 seconds passes without a movement clear both LEDs
    digitalWrite(ledDirection, LOW);
    digitalWrite(ledDirection2, LOW);
  }
  buttonState = digitalRead(buttonPin);
  if (buttonState == HIGH) {     
    firstMinusFlag=0;
    if (minusCount !=0)    {
      Serial.print("cnt ");
      Serial.print(y);
      Serial.print(" = ");
      Serial.print (minusCount); 
      Serial.print(" ");
      countValue[y]=minusCount;
      if (countValue[y] >= big){
        big = countValue[y];
        maxCountValuePosition=y;
      }
      y=y+1;
    }
    minusCount = 0;
    digitalWrite(ledPin, HIGH); 
    time=millis(); 
  } 
  else {
    minusCount = minusCount + 1;

    digitalWrite(ledPin, LOW); 
  }
  if (y==3){
    Serial.println("");
    for (int x=0;x<3;x++){
      Serial.print(x);
      Serial.print("  ");
      Serial.println(countValue[x]); 
    }
    Serial.println("");
    Serial.print("big = ");    
    Serial.print(big); 
    Serial.print("  pos = ");    
    Serial.print(maxCountValuePosition);    
    Serial.print("  ");
    big=0;
    y=0;
    countValue[3]=countValue[0];
    direction = (countValue[maxCountValuePosition]/countValue[maxCountValuePosition+1]);
    Serial.print("  Ratio = ");
    Serial.println(direction);
    if (direction >=4 ){
      digitalWrite(ledDirection, HIGH);
      digitalWrite(ledDirection2, LOW);

    }
    else{
      digitalWrite(ledDirection, LOW);
      digitalWrite(ledDirection2, HIGH);

    }
  }
}

O-Scale with Hall Sensor
The photo below shows a test rig for O-Scale.  There are three small magnets (circled in yellow) that stimulate a small Hall Effect Sensor (circled in red).

Two adjustments to the software are needed.  See notes after the photo.  The first change is necessary as the Hall sensor is high when no magnet is sensed and low when a magnet is detected.  The sensor's output goes to pin 2 and is pulled high with a 10K resistor.  The sensor's other pins go to +5 volts and ground.

 buttonState = digitalRead(buttonPin);
  if (buttonState == LOW) {     /////////////////////////Change here for HALL
    firstMinusFlag=0;
 
 Serial.print("  Ratio = ");
    Serial.println(direction);
    if (direction >=2 ){    /////////////////////////Change here for HALL
      digitalWrite(ledDirection, HIGH);
      digitalWrite(ledDirection2, LOW);

 

PIC Schematic

PIC Basic Pro Code - version 1.0
I translated the Arduino code (written in "C") to Basic for the PIC 12F683 - The code is very similar to that of the Arduino
 
 ' d. bodnar 4-4-2015 Version 1.1
 'Sample sketch to show pulses from reed switch on wheel to determine if the wheel turns CW or CCW

Include "modedefs.bas"
ansel = 0
cmcon0 = 7
Serial_out      var gpio.0  'pin 7 'Serial Out 
LEDPulse        var gpio.2  'pin 5 hpwm 1 - Pulses when reed hit
LEDDirection1   var gpio.4  'pin 3 an3
LEDDirection2   var gpio.1  'pin 6 an1
ReedSW          var gpio.5  'pin 2 - reed switch
notUsed3        var gpio.3  'pin 4
Temp1           var word
Temp2           var word
b1              var byte
b2              var byte
'Serial does not work if next line is compiled 
'''OSCCON = %00100000 ' Set to 8MHz 
Serout serial_out,n9600,[13,10,13,10,13,10,"d. bodnar  Ver 1.1 + Wheel Direction",13,10]
Serout serial_out,n9600,[13,10,"8 MhZ 4-04-15",13,10]
gpio = %00100000        '5 is only input - others outputs

minusCount var word
firstPlusFlag var byte
firstMinusFlag var byte
countValue  var word [4]
maxCountValuePosition var word
maxCountValuePosition2 var word
direction var byte
big var word
y var byte
x var byte
SerPrintFlag var bit
SerPrintFlag=0
Top:

if reedSW=1 then
	firstMinusFlag =0
	if (minusCount <>0) then
   	  countValue[y]=minusCount
	  if (countValue[y]> big) then
		  big = countValue[y]
		  maxCountValuePosition=y
	  endif
	  y=y+1
     endif
    minusCount=0
    high ledpulse
   else
	minusCount=minusCount+1
	low ledpulse
endif

if y=3 then
   for x=0 to 3
if SerPrintFlag=1 then	  Serout serial_out,n9600,[ #countValue[x],"  "]
   next x
 if SerPrintFlag=1 then	  Serout serial_out,n9600,["  big= ", #big, " pos = ",#maxCountValuePosition," " ]
   	 big =0
	 y=0
	 countValue[3]=countValue[0]
	 direction=countValue[maxCountValuePosition]/countValue[maxCountValuePosition+1 ]
     if SerPrintFlag=1 then	Serout serial_out,n9600,["   Ratio = ",#direction ,13,10]
   if direction >2 then
		 high leddirection1
		 low leddirection2
	else
		 low leddirection1
		 high leddirection2
    endif
endif

Goto Top:  
Two Hall Sensor Schematic

PIC BASIC PRO - uses two Hall Sensors & one magnet
 
 ' d. bodnar 4-4-2015 Version 1.3 WORKING
 'Sample sketch to show pulses from reed switch on wheel to determine if the wheel turns CW or CCW

Include "modedefs.bas"
ansel = 0
cmcon0 = 7
Serial_out      var gpio.0  'pin 7 'Serial Out 
LEDPulse        var gpio.2  'pin 5 hpwm 1 - Pulses when reed hit
LEDDirection1   var gpio.4  'pin 3 an3
LEDDirection2   var gpio.1  'pin 6 an1
Hall1           var gpio.5  'pin 2 - reed switch
Hall2           var gpio.3  'pin 4
 
Serout serial_out,n9600,[13,10,13,10,13,10,"d. bodnar  Ver 1.3 + Wheel Direction",13,10]
Serout serial_out,n9600,[13,10,"8 MhZ 4-04-15",13,10]

gpio = %00101000        '5 and 3 are inputs - others outputs
   
FreeCount var word
low ledDirection1:low leddirection2
FreeCount=0

Top:
FreeCount=freecount + 1
 if Hall1 = 0 and freecount > 5000 then
   Freecount=0
   high LEDDirection1
   low LEDdirection2
endif
if Hall2 = 0 and freecount > 5000 then
  freecount=0
  high LEDDirection2
  low LEDdirection1
endif

goto top: