Mike's stuff‎ > ‎

Using the SRF02 Ultrasonic Ranger with Python (pyserial)

posted 3 Oct 2010, 13:16 by Michael Strand   [ updated 5 Oct 2010, 01:37 ]
I recently purchased an SRF02 Unltrasonic Rangefinder along with a USB-I2C communications module. The SRF02 uses an I2C bus to communicate, the USB-I2C module appears on the PC as a serial port and provides an interface to the I2C bus.

I am new to playing with this kind of thing, but I have been wanting to get into some basic robotics as a hobby, so this is my starting point. I didn't know anything about the I2C bus and found it a bit confusing at first. Some guidance by a friend at work  (thanks Ben) soon had me getting some results.

The results however didn't make sense and it took a bit of experimenting to figure out. I didn't find much information about using Python to do this so I thought I would post my basic Python program here in the hope that it may help another newbie get started. Download py_SRF02.py

Created on Oct 2, 2010

@author: michael
import serial, time, os

#set up the serial connection
ser = serial.Serial(

#start the main loop
while True:
    #basic error handling, occasionally the device fails to respond. This keeps the 
    #loop running.
        #send the 5 byte command to start a ranging ping.
        #The 1st byte 0x55 is the start command for the USB-I2C module. 2nd byte 0xE0
        #is the address of the SRF02 on the I2C bus. 3rd byte 0x00 is the address 
        #register of the SRF02 that we are writing the command to. 4th byte 0x01 is 
        #the number of bytes in the command we are about to send. 5th byte 0x51 is 
        #the command to tell the SRF02 to initiate a range in cm.
        #wait for the range to finish
        #this 4byte command tells the SRF02 to send the result to the I2C bus for 
        #reading. The first byte 0x55 is the start command for the USB-I2C module. 
        #2nd byte 0xE1 is the SRF02s address plus one, this tells it that we are 
        #reading. 3rd byte 0x02 is 
        #the register thats holds the data we want to start reading from. 4th byte 
        #0x02 is the number of bytes to read. In this case we want 2bytes, the range 
        #high byte and the range low byte.
        #read 3 bytes. Why 3 when we only requested 2? The USB-I2C device returns a 
        #byte first to tell of success or fail
        s = ser.read(3)
        #clear the screen so we are not repeating up the screen
        #first check for a successful read response, first byte will be 1 if successful. 
        #Then print the second byte which is the range high followed byte the 3rd which 
        #is range low. Combine to get the actual range, we do this because each register 
        #is one byte of 8 bits, this only allows numbers to go to 255, this would give 
        #255cm on the low byte. But suppose we are measuring a range of 280cm, the low 
        #register maxes out at 255. We put a 1 in the high byte register to represent 
        #256 and the low byte starts again and counts to 24. So we can simply combine 
        #the high and the low bye h*256+l to get the range so in the example 1*256+24=280
        if ord(s[0]) == 1:
            print ord(s[1]), 'high'
            print ord(s[2]), 'low'
            print ord(s[1]) * 256 + ord(s[2]), 'range in cm'
            print 'error reading device'
        #slow the loop down a bit to give our eyes a chance to read the response
    #handle errors, the second "except" here takes all errors in it's stride and allows
    #the loop to continue. I did this because every now and again the USB-I2C device 
    #fails to respond and breaks the loop. By having a blanket error handling rule you 
    #could no longer interrupt the loop with a keyboard interrupt so I had to add an 
    #"except KeyboardInterrupt" rule to allow this to break the loop.  
    except KeyboardInterrupt:
        print ' Exiting...Keyboard interrupt'
        print 'unexpected error'

Michael Strand,
5 Oct 2010, 01:37