Category Archives: Orienteering

Python Code for Interfacing to Sportident Stations

Sportident Primer

Sportident is a system commonly used for timing in e.g. the sport of orienteering. One part of the system is battery powered stations placed at the start, finish and control posts along the course. The other main part is small RFID “cards” the athletes carry with them. The cards store information, like time and code number, from the stations they come in contact with. At the end of a race, the data in the cards is read out and processed by some piece of software to create the results list.

Sportident station BSF8. Common type without cable.

The stations come in different versions. The most common type (often used at control posts) has no other means of communication than the inductive RFID link, while others have e.g. a serial port (not sold anymore) or USB. The wire-free stations can be connected to a computer by inductively coupling them via a ferrite rod to a station connected to a computer.

Sportident station BSM7. This type with serial port is not sold anymore. Today USB is used instead.

There is non-volatile memory inside each station and this stores station configuration information, as well as a log of which cards have punched at the station and at what time.

Sportident provides a gratis program called Sportident Config+ that can manage the Sportident hardware, by e.g. upgrading firmware, setting operating mode, setting time, reading out backup memory, reading and configuring cards etc. While the program is generally quite complete in its functionality, it has a rather inefficient GUI for doing the same operation on many stations in a row, like setting up all stations to have synchronized clocks and the same operating time. Or reading out the backup memories of many stations. Such operations require a lot of clicking for each station and small mistakes can cause undesirable results.

Python Code

The communication protocol used over the USB/serial port is partially documented in a non-maintained document not generally available, although it is possible to get a copy of this document by asking Sportident for it. I found more information about the protocol and the configuration data of the stations in the source code of a program called sireader.py which I found on Github. This code was developed by Gaudenz Steinlin, Simon Harston and Jan Vorwerk in 2008-2015 and the purpose primarily seems to have been to read out the information of cards inserted into a station connected to a computer.

I wanted to do more than this however. First, I wanted to be able to read out the backup memory of many stations and store the information in separate CSV files with minimal user intervention for each station. Second, I wanted to have a streamlined way of preparing many stations for an event. This involves synchronizing the clocks, clearing the backup memory, setting the operating time (time before the station goes to sleep) and a few other settings.

Some of this was quite straightforward to do using the code in sireader.py, but some of it required more investigation of the communication protocol by means of a logic analyzer as well as the addition of several new methods in the main class of the code. I also had to study the raw configuration data of stations, which consists of 128 partially documented bytes, and compare it between stations in different modes to be able to get to the settings and information I wanted.

The result of all this is currently an extended version of the python code, which I call sireader2.py since it is not necessarily 100% compatible with sireader.py and it also contains a lot more functionality and documentation of a few more parts of the Sportident station configuration data.

I have also written scripts which use sireader2.py to do what I set out to do, namely to read backup memories and normalize station settings.

All of the code is freely available under GPL 3 at the following github repository:

https://github.com/per-magnusson/sportident-python

Hur man uppdaterar batteristatus i en SI-enhet efter batteribyte

This post is in Swedish, but the same information is available in English on the last page of this PDF from Sportident.

Om man har bytt batteri i en Sportident-enhet så är det en bra idé att tala om för enheten att det nu sitter ett nytt batteri med full kapacitet i enheten så att den kan försöka hålla reda på hur mycket kapacitet som återstår. Som bekant så använder man programmet  SPORTident Config+ för att programmera SI-enheter, ställa tiden, uppdatera firmware mm, men tyvärr så kan man i vanliga fall inte komma åt funktionen för att ställa om batteristatusen. Kanske anser Sportident att funktionen är för riskabel att ha allmänt tillgänglig, men det går ganska lätt att slå på den. Så här gör man:

Högerklicka på genvägen till SPORTident Confg+ på skrivbordet. Då dyker en meny upp som ser ut något i stil med detta:

Menyn som kommer upp vid högerklick på Config+ikonen.

Välj alternativet “Egenskaper” längst ned. Nu dyker följande fönster upp:

Dialogrutan med egenskaper för genvägen.

Tricket man behöver göra är att lägga till texten −−service sist i rutan “Mål:”. Enklast görs detta på följande vis:

  • Tryck på pil höger så att den blåmarkerade texten avmarkeras och markören hamnar sist i rutan.
  • Tryck mellanslag.
  • Skriv in −−service (dvs två minustecken och sedan ordet service utan några mellanslag). Så här bör det se ut:

Flaggan −−service har lagts till.

  • Tryck OK för att stänga rutan.

Om man nu startar SPORTident Config+ från den ändrade genvägen så finns plötsligt den nya menyn Service högst upp:

Service-menyn har dykt upp.

Om man väljer alternativet “New device battery” så får man upp följande:

Fönstret för att uppdatera batteristatus

På en BSF8 med vanligt litet batteri (1/2 AA) så är det normalt 1000 mAh man ska mata in och om man precis satt in ett nytt batteri så ska såklart Usage stå på 0%. På en BSM7 (med sladd) med batteri i AA-storlek är det normalt 2000 mAh i batterikapacitet.

Mer information om andra saker att tänka på när man byter batteri finns i följande dokument från SportIdent:

Battery exchange in BSF7 and BSF8 stations

Fixing Sportident Thermal Printer Issues

The youth section of my orienteering club has a Sportident thermal printer connected to an SI station for use on trainings. This has worked fine for a few years, but for some reason it started printing garbage and I was asked to have a look at it.

I had never used the printer before, but I started troubleshooting. The SI station had old firmware, so I updated that using Sportident Config+, but this did not help. At first I did not get the printer to print anything, but that turned out to be because I had failed to realize that the SI logo was actually a hidden power button one has to press briefly to turn the printer on or off. After the printer was powered on, it did indeed print garbage when I tried to print the contents of an SI card.

The printer is a Martel MCP7830-242 printer with a serial port and I was able to find a manual for it. It turns out a customized manual is also available from Sportident. I suspected the problem might be that the printer was set up to use an incorrect baud rate and I first tried to set the SI station in its two possible states, namely 4800 and 38400 bauds, but that did not help. The manuals have instructions on how to change the baud rate on the printer and according to those instructions, one should keep the power button pressed a couple of seconds until the LED starts blinking, then one should press the button four times and wait for the LED to blink four times (this enters the baud setting mode), then press the button six times to select 4800 baud. Unfortunately this did not help and the printer still printed garbage.

I then decided to talk to the printer from a PC and I wrote a small Python script to try to send commands to it at different baud rates. After some fiddling around, the printer started responding, but at the unexpected baud rate of 600 bauds!

The Python code is shown at the end of this post in case someone is interested.

I retried the baud rate setting, but still with the result of getting the printer to accept 600 bauds. A note above the tables in the manual says one should do a single button press to select 19200 bauds, but the table says the button should be pressed five times. This made me try to press the button three instead of six times (4800 comes two steps after 19200 in the table) and after this, the printer started working at 4800! So the correct sequence for setting the baud rate to 4800 is this:

  1. Printer is off
  2. Hold down the button for a few seconds until the LED starts flashing.
  3. Press the button 4 times.
  4. Wait for the LED to flash 4 times.
  5. Press the button 3 times.
  6. Wait for the LED to flash 3 times.
  7. Done!

After I got it all to work I realized that one can check the mode of the printer in the following way:

  1. Printer is off
  2. Hold down the button for two seconds.
  3. The printer prints its status, including the baud rate.

If I had known this from the start I might have solved this much quicker.

Below is the Python code.


#! /usr/bin/python

"""
com-port-sender.py

Rev 0.1, 2016-09-28
Per Magnusson, Axotron, axotron.se/blog

This code is public domain and comes with absolutely no warranty. Enjoy!

Send some data on a serial port to test a Martel/Sportident MCP7830 printer.
"""

from __future__ import print_function
import serial
import serial.tools.list_ports
import sys
import time
import msvcrt
import struct


# Look for a COM port that might have a serial port adapter connected
portfound = False
ports = list(serial.tools.list_ports.comports())
for p in ports:
    print(p)
    print(p[1])
    if "Prolific" in p[1]:
        print("Found port on " + p[0])
        if not portfound:
            portname = p[0]
            portfound = True
            print("Using " + portname)
        else:
            print("Ignoring this port, using the first one that was found.")

if portfound:
    ser = serial.Serial(portname, 4800, timeout=0.1)
else:
    print("No good serial port found. Exiting.")
    time.sleep(10)
    sys.exit("No good serial port found.")


def to_printer(ser, data):
    for b in data:
        print("{:d} ".format(ord(b)), end="")
    ser.write(data)
    print("")

def send_ESC(ser):
    to_printer(ser, b'\x1b') # 27 == ESC

def send_NULL(ser):
    to_printer(ser, b'x\00')

def set_sweden(ser):
    send_ESC(ser)
    to_printer(ser, b'R5')

def set_mode_default(ser):
    send_ESC(ser)
    to_printer(ser, b'!\x00')

def reset_printer(ser):
    send_ESC(ser)
    to_printer(ser, b'\xff')

def reset_printer2(ser):
    send_ESC(ser)
    to_printer(ser, b'@')

def printer_self_test(ser):
    send_ESC(ser)
    to_printer(ser, b'\xfe')

def printer_move_fwd(ser):
    send_ESC(ser)
    to_printer(ser, b'J\x08')

def printer_print(ser):
    send_ESC(ser)
    to_printer(ser, b'd\x01')

def print_something(ser):
    to_printer(ser, b'abc\x0d\x0a\x0c')


#set_sweden(ser)
#printer_self_test(ser)
#reset_printer(ser)
#reset_printer2(ser)
#set_mode_default(ser)
#printer_move_fwd(ser)
#to_printer(ser, b'abc\x0d\x0a\x0c')
#printer_print(ser)

rates = [300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200]

for rate in rates:
    print('baud: {:d}'.format(rate))
    ser.setBaudrate(rate)
    print_something(ser)
    time.sleep(5)

ser.close()