Watch while I pull another race clock system out of my hat.
A couple of years ago Rapha gave me the mechanical race clock that I helped build for them 5 years ago and its been laying around in my pile since then. Three weeks ago Chrome bags called me up and asked if they could take it with them on a 4 month promotional tour. There wasn’t time to build out new hardware but I told them if they bought the open sprints sensor hardware we could work something out.
The first week was spent paring the device down from 4 bikes to two bikes. Two of the driver boards had issues and the shaft on the smallest dial was bent so it is a good thing they only wanted 2. The bushings on the last two dials needed to be cleaned up since they were binding. This is not my strength but I was able to figure it out.
While I was waiting for the sensors I built a test rig like the one that I just threw away from a pair of cds mounted on motors driven by a trusty L293D This time I put it in a box so that it might survive between use.
Once we got the open sprints gear we got down to the fun part.
I had done a sensors only setup for Chrome several years ago. Getting the setup to work using serial and ruby on the Macintosh was really painful and I can see pretty clearly why they have since dropped the support for the old software and gone with a flash based software written by a third party called Goldsprints_FX. Since flash doesn’t talk to serial devices the sensors on the arduino based open sprints are proxied to a network connection using serproxy or “tinkerproxy”. I could not find the source code for tinker proxy so I went with the serproxy.
Proxying the Proxy.
What I needed to do was to intercept the sensor values from the opensprints arduino based hardware and the go and reset values from the goldsprints software. I started looking at the source code for the serproxy and making a network tap, then I experimented with socat. Both of which seemed too complicated. Finally I went back to the python in a nutshell book and mangled the socket in and out samples into a very simple proxy. The proxy passes data from 5330 (which is where serproxy is sending the serial data) to and from 5331 which is where goldsprints_fx expects the serproxy to be.
import socket import re isocket = socket.socket(socket.AF_INET,socket.SOCK_STREAM) osocket = socket.socket(socket.AF_INET,socket.SOCK_STREAM) isocket.connect(('localhost',5330)) isocket.settimeout(0.2) print "Connected to serproxy" osocket.bind(('',5331)) osocket.listen(1) try: while True: connection,address=osocket.accept() if not connection: break print "Connected From", address connection.settimeout(0.2) while True: try: receivedData = connection.recv(8192) except socket.error: receivedData=None if receivedData is not None: # print ">"+re.escape(str(receivedData))+">"+str(len(receivedData)) isocket.sendall(receivedData) for chunk in receivedData.split('\x00'): if len(chunk)>0 and chunk[0] == 's': print "(RE)SET RACE !!!" if len(chunk)>0 and chunk[0] == 'g': print "GO !!!" if len(receivedData)==0: print "ISSUE WITH CLIENT" try: sentData = isocket.recv(8192) except socket.error: sentData=None if sentData is not None: #print "<"+re.escape(str(sentData))+"<"+str(len(sentData)) connection.sendall(sentData) for chunk in sentData.split('\x00'): #if len(chunk.rstrip('\r')): # print "<",re.escape(chunk),len(chunk) if len(chunk) > 4 and chunk[0]=='0' : done1=int((float(chunk[3:])/1566.0) * 100.00) print "Bike # 1 is ", str(done1), "percent done" if len(chunk) > 4 and chunk[0]=='1' : done1=int((float(chunk[3:])/1566.0) * 100.00) print "Bike # 2 is ", str(done1), "percent done" if len(sentData)==0: print "ISSUE WITH SERPROXY" finally: connection.close() osocket.close() isocket.close()
The print statements which are commented out above allowed me to see both sides of the conversation while picking out the pieces that I needed. This works pretty well at getting the data.
$./serproxy.py Connected to serproxy Connected From ('127.0.0.1', 61571) (RE)SET RACE !!! (RE)SET RACE !!! GO!!!!!!!!!! Bike # 1 is 0 percent done Bike # 2 is 0 percent done ... Bike # 1 is 100 percent done Bike # 2 is 100 percent done (RE)SET RACE !!! $
To make this drive the clock required retooling the clock code a bit and configuring serproxy. Since the opensprints uses the old ftdi based arduino the name of the device on the Macintosh is unique to the does not change. I bought a pair of sparkfun “red boards” and also added proxies for the clock and the test rig. Now the code to talk to the clock is the same as talking to the other network sockets. On top of that I can telnet to the test rig and set the “bike” speeds.
Once this was working I also modified the opensprints basic_msg code to provide more frequent updates since the clock is rather jerky when it gets new positions every 250ms. Cutting it to 50ms eliminates most of that, as you can see from the video below.
The current version of the code starts serproxy and Goldsprints_fx and kills them when done it is posted at https://github.com/suspect-devices/bikewedge/blob/master/RetoolingForOpenSprints/bin/bikewedge.py. The entire project including the 3 arduino sketches, serproxy source code and configuration file and the bikewedge proxy can be found at https://github.com/suspect-devices/bikewedge