Timing, RFID, and radio-controlled cars

Timing, RFID, and radio-controlled cars

In addition to reading and writing tags, the Yocto-RFID-15693 module API allows you to program a callback that is called every time a tag enters or leaves the antenna field. And these callbacks are time-stamped. It's only a short step from there to using this feature to build a stop-watch application, a step we didn't hesitate to take.

So we got out our cardboard remote-controlled car, based on an old HPI Micro RS4 chassis with a few improvements. This thing can go much faster than it's humanly possible to control on an obstacle-laden floor, and we'd like to know the best time it's possible to achieve around a floor without hitting a furniture leg.

It may not look like much, but down there it's a Micro-RS4 chassis with a brushless motor and 3S lipo battery
It may not look like much, but down there it's a Micro-RS4 chassis with a brushless motor and 3S lipo battery


The principle

Remember that with ISO-15693 RFID technology, it's the reader that powers the RFID tags via its antenna. At constant power, the larger the antenna, the greater the volume in which a tag is potentially detectable, but the less "dense" the irradiated energy. Similarly, the larger the tag, the more energy it can capture.

The idea, then, is to stick the largest RFID tag possible on the roof of the car and build a gantry that to hold a large antenna above the ground. All you have to do is pass under it for the car to be detected. We chose a large MaxiPCB HF RFID Antenna from Metratec, giving us a detection area of around 30x30cm. The antenna is connected to a Yocto-RFID-15693, which in turn is connected to the control computer via a USB cable. The tag is a self-adhesive RFID label measuring 80x50mm.

The various components
The various components


The antenna and the Yocto-RFID-15693 are housed in a hastily constructed cardboard gantry. The upside of using cardboard is that it's cheap, and if the car bangs into it, it's unlikely to cause irreparable damage.

The completed gantry
The completed gantry



Programming

The code is relatively simple, starting with the creation of a small car class, which stores a car's RFID ID and calculates its lap time. This class contains only a constructor and a method to be called each time the car passes under the gantry.

class car:
  lastSeen     = 0
  bestLapTime  = -1
  tagid        = "unknown"
  lapcount     = 0
  carsCount    = 0  # static variable
  index =0
  def __init__(self,rfidtag,timestamp):
    self.lastSeen    =  timestamp
    self.tagid       =  rfidtag
    car.carsCount    =  car.carsCount+1
    self.index       =  car.carsCount
    print("(%s) car %d - starting first lap"%(self.tagid,self.index))

  def lap(self,timestamp):
    if  (timestamp-self.lastSeen)<3 : return
    currentlaptime =  timestamp-self.lastSeen
    self.lastSeen = timestamp
    self.lapcount=self.lapcount +1
    print( "(%s) car %d - lap %d - %.2f sec  " %
       (self.tagid,self.index,self.lapcount, currentlaptime),end="")
    if (currentlaptime<=self.bestLapTime) or (self.bestLapTime<0) :
      print("best lap!")
      self.bestLapTime = currentlaptime
    else:
      print("+%.2f"%(currentlaptime-self.bestLapTime))


Note that we make sure that at least three seconds elapse between each detection, to avoid the car being detected several times if the RFID link is not very stable around the antenna.

We then code a small callback that is called each time the car's RFID tag passes under the antenna. Each car has its own car object stored in an associative array indexed by its tag identifier. When the callback is executed, if the tag is already listed, the lap() method of the corresponding object is called, otherwise a new car object is created.

cars = {}
# called automatically each time a tag enter or leave the RFID field
def tagEvent(source,  timestamp, event, id):
  global cars
  if event!="+" : return;  # we need Tag arrival only
  if id in cars:
     # if car is known, updates its lap time
     cars.get(id).lap(timestamp)
  else:
     # otherwise creates new one
     cars[id] =   car(id,timestamp)



The main part of the code simply initializes the API, finds the RFID reader and sets up the RFID callback. The main loop does nothing except call YAPI.Sleep and YAPI.UpdateDeviceList from time to time.

print("Hello..");

# uses USB device
errmsg = YRefParam()
if YAPI.RegisterHub("usb", errmsg) != YAPI.SUCCESS:
    sys.exit("init error" + errmsg.value)

# finds the RDIF reader
reader = YRfidReader.FirstRfidReader()
if reader is None: sys.exit('no RFID reader connected')

# makes sure the reader can detect tag as fast as possible
reader.set_refreshRate(100)

# tagEvent will be automatically called each time
# a tag is detected or lost
reader.registerEventCallback(tagEvent)

# main loop, doing nothing but calling YAPI.Sleep and
# YAPI.UpdateDeviceList from time to time
n= 0
while True:
  YAPI.Sleep(10,errmsg)
  n=n+1
  if n>20 : n=0
  if (n == 0): YAPI.UpdateDeviceList(errmsg)



The only subtlety is to set the refresh rate to maximum. Remember that, internally, the Yocto-RFID-15693 detects tags by taking an inventory of those within antenna range. Hence the importance of doing as many inventories per second as possible. When there are no tags in the vicinity, the module can perform a maximum of 75 inventories per second. With one tag, it's 55 per second, with two tags, it's 42...

Still with speed in mind, it's essential to resist the temptation to read or write the RFID tag on each pass. Not only is there a good chance that the car will leave the field before the transaction is completed, but it's also important to remember that while the Yocto-RFID-15693 is performing a read/write operation with a tag, it's not taking inventory.

For the same reasons, avoid using the ColorLedCluster and Buzzer functions hosted on Yocto-RFID-15693, as they may slow down the module.

Enhancement

As the car is equipped with an FPV camera, we soon wanted to see our lap times from the car. To do this, we recovered the screen we'd built a few years ago. This screen is a set of 8 Neopixel tiles with 8x8 rgb LEDs wired in series. They are controlled by a Yocto-Color-V2, itself driven by a PoE-powered YoctoHub-Ethernet. Which means you can place it just about anywhere you like, it's just a question of Ethernet cable length.



LED display visible from the car


And does it work?

It works furiously well, in all the time we've been playing with it, the gantry hasn't missed a single pass. On the other hand, seeing your lap time clearly displayed at each pass encourages you to drive faster and faster, which usually ends up in a crash.

  



Limitations

The system has a few limitations. Not only is the best accuracy you can hope for around 0.02 seconds, which is probably a bit limiting for racing, but this accuracy can potentially be affected by the Yocto-RFID-15693's clock drift, especially if the module has just been plugged in. If you want to chase milliseconds, wait a few minutes after plugging in the Yocto-RFID-15693, while the control computer synchronizes the module's clock speed.

The timing code is designed to work with an arbitrary number of cars, but while the gantry is plenty wide enough for a Micro-RS4 to pass through, which is roughly the size of a shoe, it's a little narrow for two cars to pass abreast. So we wondered if we couldn't make a wider gantry using several Yocto-RFID-15693s equipped antennas setup side-by-side. We tried it, and to no great surprise, it didn't work well. Tag detection is definitely too unreliable.

Conclusion

In just a few hours and a few lines of code, we've cobbled together a timing system that's more than capable of measuring the lap time of a remote-controlled car moving at several meters per second. Now the really tempting question is whether it also works with a mini-quadcopter. :-)

Finally, if you're interested in the system's source code, you can download the complete Python code, including the part that controls the screen.

Add a comment No comment yet Back to blog












Yoctopuce, get your stuff connected.