1

As noted in earlier posts, I am building a model railroad crossing gate signal, complete with gate arms, flashers and sound. Although I haven't settled on exactly how I am going to implement detection of the train entering the zone, determining when its completely passed the entry sensor, tripped the exit sensor and completely passed the exit sensor to re-open the gate, I am researching using RFID for this now.

My thought is to have an RFID card in every car of all my rolling stock. Therefore, I want to read all the ID's of the cars as they go buy and for this particular need, always store the latest ID in a variable so that once the whole train has passed by, the last ID in the variable SHOULD be the ID of the last car in the train. I would also have a counter incrementing for each unique ID read so I can use that as a secondary check for the exit routine (check that the exit RFID reader gets the same car count as the entry reader). The exit reader would also match its exitTag to the entryTag from the entry reader.

I am implementing a state machine to control all this functionality but the problem lies in the RFID reading, I need a way to determine when the reader has read all the train cars tags. I thought the easiest way to do this was to use the elapsedMillis library and monitor for some interval where no card was read (two seconds? that would be way more than the time between two train cars) and once met, change state.

That is proving more problematic than I thought. Here is my latest attempt. I also think it might be better if the tag reading method only returned the last tag ID AFTER ITS DETERMINED NO MORE TAGS TO BE READ so that the code in the case statement is not the code that's running continuously.

Note: I am using the Adafruit RFID shield and Mifare Classic 1k S50 IC 13.56MHz RFID cards.

Any thoughts appreciated

case CrossingZoneState::CROSSINGZONE_RFIDDETECTED:
    if (crossingZoneEntered == true)
    {
        // only thing we do here is monitor and identify the RFID tags of each car to determine last cars tag id
       //well, this doesnt work
        if (timeElapsedBetweenCardReads <= readerInactiveInterval) //only read cards that are continuous, once interval is surpassed change state
        {
            //do all the tag reading in here    
            tag = IndentifyTag(entryRFIDReader);
            readCount += 1;

            if (tag != priorTag)
            {
                tagCount += 1;
            }
            priorTag = tag;
            timeElapsedBetweenCardReads = 0;   // reset the counter to 0 so the counting starts over...
        }
        // need to figure out how to determine no more cards are being read and change state and crossingZoneEntered back to false
    }

    if (crossingZoneExited == true)
    {

    }
    break;

IdentifyTags method:

uint32_t IndentifyTag(Adafruit_PN532 rfidReader)
{
    uint8_t tagID[] = { 0, 0, 0, 0, 0, 0, 0 };  // Buffer to store the returned UID
    uint8_t tagIDLength; // Length of the UID (4 or 7 bytes depending on ISO14443A card type)
    uint32_t rfidCardID = tagID[0];
    boolean success = rfidReader.readPassiveTargetID(PN532_MIFARE_ISO14443A, &tagID[0], &tagIDLength);
    if (success) {
        // Display some basic information about the card for testing
        Serial.println("Found an ISO14443A card");
        Serial.print("  UID Length: "); Serial.print(tagIDLength, DEC); Serial.println(" bytes");
        Serial.print("  UID Value: "); rfidReader.PrintHex(tagID, tagIDLength);

        if (tagIDLength == 4)
        {
            // Mifare Classic card 
            rfidCardID <<= 8;
            rfidCardID |= tagID[1];
            rfidCardID <<= 8;
            rfidCardID |= tagID[2];
            rfidCardID <<= 8;
            rfidCardID |= tagID[3];
            Serial.print("Mifare Classic card #");
            Serial.println(rfidCardID);
        }
        Serial.println("");
    }
    return rfidCardID;
}

Full switch statement:

switch (state)
{
case CrossingZoneState::CROSSINGZONE_CLEAR:
    // only thing we do here is monitor the zone entry sensor
    if (digitalRead(entrySensorPin) == HIGH) {
        crossingZoneEntered = true;
        state = CrossingZoneState::CROSSINGZONE_ACTIVATEGATES;
    }
    break;
case CrossingZoneState::CROSSINGZONE_ACTIVATEGATES:
    // Do these once: turn on the flashers, put down the arms and turn on the crossing gate bells
    if (didOnce = false)
    {
        // turn on flashers
        // turn on sound
        // lower gate arms
        didOnce = true;
        state = CrossingZoneState::CROSSINGZONE_READENTRYRFID;
    }
    break;
case CrossingZoneState::CROSSINGZONE_READENTRYRFID:

    // only thing we do here is monitor and identify the rfid tags of each car to determine last cars tag id
    // need to figure out how to determine no more cards are being read and change state 
        if (timeElapsedBetweenCardReads <= readerInactiveInterval) //only read cards that are continuous, once interval is surpassed change state
        {
            //do all the tag reading in here    
            entryTag = IndentifyTag(entryRFIDReader);
            readCount += 1;

            if (entryTag != priorTag)
            {
                entryTagCount += 1;
            }
            priorTag = entryTag;
            timeElapsedBetweenCardReads = 0;   // reset the counter to 0 so the counting starts over...
        }
        else {
            //change state now that entry rfid tag reader is finished
            state = CrossingZoneState::CROSSINGZONE_OCCUPIED;
        }

    break;
case CrossingZoneState::CROSSINGZONE_OCCUPIED:
    // only thing we do here is monitor the zone exit sensor
    if (digitalRead(exitSensorPin) == HIGH) {           
        state = CrossingZoneState::CROSSINGZONE_READEXITRFID;
    }
    break;
case CrossingZoneState::CROSSINGZONE_READEXITRFID:
    if (timeElapsedBetweenCardReads <= readerInactiveInterval) //only read cards that are continuous, once interval is surpassed change state
    {
        //do all the tag reading in here    
        exitTag = IndentifyTag(entryRFIDReader);
        readCount += 1;

        if (exitTag != priorTag)
        {
            exitTagCount += 1;
        }
        priorTag = exitTag;
        timeElapsedBetweenCardReads = 0;   // reset the counter to 0 so the counting starts over...
    }
    else {//check tags and count now that entry rfid tag reader is finished
        if (entryTagCount == exitTagCount && entryTag == exitTag)
        {
            state = CrossingZoneState::CROSSINGZONE_DEACTIVATEGATES;
        }
    }
    break;
case CrossingZoneState::CROSSINGZONE_DEACTIVATEGATES:
    // Do these once: turn off the flashers, raise the arms and turn off the crossing gate bells
    if (didOnce = true)
    {
        // turn off flashers
        // turn off sound
        // raise gate arms
        didOnce = false;
    }
    break;
case CrossingZoneState::CROSSINGZONE_ERROR:
    boolean success = writeToFileOnSD(modelRRErrorLogFile, "ModelRRXingErrorLog.txt", "error string");
    break;
}
  • What kind of RFID reader and tags are you using? What exactly happens when you try your current code? – TisteAndii May 5 '16 at 0:31
  • I am using the Adafruit RFID shield and Mifare Classic 13.56 Hz cards. @TisteAndii ... I can read the tags but there a number of problems. 1. I run the train by the reader, (there are three cars with rfid tags) it reads the first tag 3-5 times but doesn't read the rest. I get why it might read a tag more than once as it passes by, and I handle that in IndentifyTags(), but its not reading tags 2 & 3 – dinotom May 5 '16 at 0:39
  • I thought about using a while loop, i.e. in psudecode while(Im reading tags) {//put the tags in a variable and increment the counter} and once im not reading tags (the elapsedtime is more than the inactive interval) then move on – dinotom May 5 '16 at 0:46
  • Provide the whole switch statement. What exactly is crossingZoneEntered and crossingZoneExited? What do they represent in terms of your train's movements? Is this code for the entry reader or the exit reader? – TisteAndii May 5 '16 at 1:20
  • @TisteAndii...Full switch added to original post – dinotom May 5 '16 at 9:46
1

Here is a solution I found on one of the forums, I modified/simplified it to suit, but it does what you are after.

I use a cheap MP3 player and small amp that runs all the time with the speaker output switched by a baby relay controlled by the UNO R3 and IR sensors from Ebay, you should be able to convert this for use on your player.

It runs the 4 steps you described.

code attached. (could not figure out how to get code into a box-very confusing..please enlighten me,,,,and what is a back tick??)

#include <Servo.h>

Servo gate1servo;// create servo object to control crossing gate 1
//ASSUMPTION Gate is down at position gatedownposition and up at position gateuppostion  
Servo gate2servo; // create servo object to control crossing gate 2
unsigned long previousMillisSERVOSTART=0; //FLASH Millis() setup
int intervalSERVOSTART = 1000;
unsigned long previousMillisFLASH=0;  //FLASH Millis() setup
int intervalFLASH = 250;
int gateup  =  120;  // servo position for gate up
int gatedown = 30;
int gatepos; // variable to store the servo gateposition
int gp = 120;  // servo position for gate down
int sensor1 = 5; // IR sensor pin Assumption== Pin goes LOW when train detected
int sensor2 = 6; // IR sensor pin Assumption== Pin goes LOW when train detected
int led1 = 10;  // Led 1 pin first alternating flasher
int led2 = 11;  // Led 2 pin first alternating flasher
int gates_started = 0; // this says if the crossing is active

int entering_sensor = 5; //this will tell which sensor was triggered first
int leaving_sensor = 6; // this will tell which sensor shows train leaving
int flash_state = 0;

void setup() {
   Serial.begin(9600);
  gate1servo.attach(3);// attaches the servo on pin 3 to the servo object
  gate2servo.attach(4);// attaches the servo on pin 4 to the servo object
  gate1servo.write(gp);//start assuming no train
  gate2servo.write(gp);//start assuming no train
  pinMode(sensor1, INPUT);  
  pinMode(sensor2, INPUT);
  pinMode(led1, OUTPUT);
  pinMode(led2, OUTPUT);  
  digitalWrite(led1, LOW);// Start with all flashers off
  digitalWrite(led2, LOW);
}

void loop() {
    if ((digitalRead (sensor1)==LOW)&& (gates_started==0)) {//read sensor 1, if LOW ...do it
        gates_started = 1;
        leaving_sensor = sensor2;
    }
    if ((digitalRead (sensor2)==LOW)&& (gates_started==0)) {//read sensor 2, if LOW ...do it
        gates_started = 1;                      
        leaving_sensor = sensor1;
    }
    unsigned long currentMillisFLASH = millis();                                                      // get current time stamp, only need one for all if-statements
    if ((unsigned long)(currentMillisFLASH - previousMillisFLASH) >= intervalFLASH) {// time between button presses
       flash_state = ~flash_state;
       digitalWrite(led1, flash_state);                                           //toggle flash_state
      digitalWrite(led2, ~flash_state);
      previousMillisFLASH = currentMillisFLASH;
  }

  unsigned long currentMillisSERVOSTART = millis();                                                     // get current time stamp, only need one for all if-statements
  if ((unsigned long)(currentMillisSERVOSTART - previousMillisSERVOSTART) >= intervalSERVOSTART) {      // time between button presses     
    for(gp = gatedown ; gp <= gateup ; gp++) {                                                          // goes from gatedown  degrees to gateup degrees      
      gate1servo.write(gp);                                                                             // tell servo to go to gateposition in variable 'gateposition'
      gate2servo.write(gp);

  previousMillisSERVOSTART = currentMillisSERVOSTART;
 }
 Serial.println(gp);
}
}
  • @Doug...Thanks for this. I edited your code input. All code lines need 4 spaces in from left margin and it will format properly. Ill test this later today or tomorrow. – dinotom Jul 17 '16 at 11:30
0

Your algorithm is the probably the problem. Use this entry case instead:

case CrossingZoneState::CROSSINGZONE_READENTRYRFID:

    entryTag = IndentifyTag(entryRFIDReader);
    if (!entryTag) {   // zero is usually returned if no card is detected
        readCount += 1;   // seems u want to count number of reads

        if (entryTag != priorTag) {
            entryTagCount += 1;
            priorTag = entryTag;  // update priorTag
        }
        timeElapsedBetweenCardReads = 0;   /// reset the counter when u read ANY tag; negate the effects of reading a card more than once

    }
    // only check timeout if you didnt read ANY tag at all
    else if (timeElapsedBetweenCardReads >= readerInactiveInterval){
        //change state now that entry rfid tag reader is finished
        state = CrossingZoneState::CROSSINGZONE_OCCUPIED;
    }

    break;

Something similar for the exit:

case CrossingZoneState::CROSSINGZONE_READEXITRFID:
    exitTag = IndentifyTag(entryRFIDReader);
    if (!exitTag) {   // zero is usually returned if no card is detected
        readCount += 1;   // remember this variable is shared with the other case

        if (exitTag != priorTag) {
            exitTagCount += 1;
            priorTag = exitTag;  // update priorTag
        }
        timeElapsedBetweenCardReads = 0;

    }
    else if ((timeElapsedBetweenCardReads >= readerInactiveInterval) &&
             (entryTagCount == exitTagCount) && (entryTag == exitTag)){
        //change state now that entry rfid tag reader is finished
        state = CrossingZoneState::CROSSINGZONE_DEACTIVATEGATES;
    }
    break;

And in the OCCUPIED case:

case CrossingZoneState::CROSSINGZONE_OCCUPIED:
    // only thing we do here is monitor the zone exit sensor
    if (digitalRead(exitSensorPin) == HIGH) {           
        state = CrossingZoneState::CROSSINGZONE_READEXITRFID;
        timeElapsedBetweenCardReads = 0;   // reset the counter to prepare for exit case
        priorTag = 0;  // just in case
    }
    break;
  • I'm on vacation. I'll test when I return next week. Thanks for the answer. – dinotom May 9 '16 at 16:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.