UltraBorg - Precision servo control with ultrasonic module support
This is our web based interface for controlling your UltraBorg from a phone or browser.

This example provides web-based access to UltraBorg using a web browser on both phones and desktops.
This allows you to both read ultrasonic distances and move servos via a Raspberry Pi.
We would also suggest you tune the servos using the Tuning GUI, but this is entirely optional.
Make sure your Raspberry Pi is connected to your router before running the scripts.
This example should work with both WiFi and Ethernet based connections.
First find out what your IP address is using the
It should be 4 numbers separated by dots, e.g.
We will need this to access the controls, so make a note of it.
Next run the script using:
Wait for the script to load, when it is ready it should say:
Once loaded enter your IP address in the address bar
You should be presented with sliders for each servo, some current servo position readings, and some distance readings.

To move a servo simply tap or click on the new position you want.
You can also drag the slider up and down, the servo should move whilst you are dragging it.
The values at the bottom are the distance readings in mm. A value of None indicates either:
The last motor settings are displayed below the image.
Replace
We think sharing software is awesome, so we encourage others to extend and/or improve on this script to make it do even more :)
To assist that we have uploaded the files as a self-contained project on GitHub:
https://github.com/piborg/ultraborg-web

This example provides web-based access to UltraBorg using a web browser on both phones and desktops.
This allows you to both read ultrasonic distances and move servos via a Raspberry Pi.
Getting ready
Before using this script you should make sure your UltraBorg is working with the standard examples.We would also suggest you tune the servos using the Tuning GUI, but this is entirely optional.
Make sure your Raspberry Pi is connected to your router before running the scripts.
This example should work with both WiFi and Ethernet based connections.
Downloading the code from GitHub
In a terminal run the following commands:cd ~ git clone https://github.com/piborg/ultraborg-web.gitAlternatively you can download the ubWeb.py script file as text here.
Running the code
This is easiest done via SSH over the network.First find out what your IP address is using the
ifconfig command.It should be 4 numbers separated by dots, e.g.
192.168.0.198We will need this to access the controls, so make a note of it.
Next run the script using:
sudo ~/ultraborg-web/ubWeb.pyWait for the script to load, when it is ready it should say:
Press CTRL+C to terminate the web-serverControlling your servos
Load your web browser on your phone or desktop.Once loaded enter your IP address in the address bar
You should be presented with sliders for each servo, some current servo position readings, and some distance readings.

To move a servo simply tap or click on the new position you want.
You can also drag the slider up and down, the servo should move whilst you are dragging it.
The values at the bottom are the distance readings in mm. A value of None indicates either:
- There is no ultrasonic module attached to that connector
 - The distance you are measuring is extremely close, within a couple of centimeters
 - The distance you are measuring is too far away, more than a few meters
 - If changing between None and a value the ultrasonic module is struggling to "see" the object
 
The last motor settings are displayed below the image.
Alternative options
There are some other URLs you can use to get different functionality.Replace
192.168.0.198 in the below addresses with your IP address:http://192.168.0.198- Standard controls, allows servo control and reads distanceshttp://192.168.0.198/servo- Servo controls only, no distances are displayedhttp://192.168.0.198/distances- Gets the distance readings without the servo controls, refreshes at a regular intervalshttp://192.168.0.198/distances-once- Single set of distance readings, you may need to force-refresh to get new values
Additional settings
There are some settings towards the top of the script which may be changed to adjust the behaviour of the interface:webPort- Sets the port number, 80 is the default port web browsers will trydisplayRate- The number of times per second the web browser will refresh the distance readingssliderHeight- The height of the sliders in pixels, adjust this if the sliders are too tall or too short
Auto start at boot
To get the web interface to load on its own do the following:- Open the Cron table using 
crontab -e - Add a cron job to the bottom of the file using the following line:
@reboot sudo /home/pi/ultraborg-web/ubWeb.py - Save the file
 - Close the file
 
Going further
This is just a simple example of how a web interface can be made using Python on the Raspberry Pi to control a robot.We think sharing software is awesome, so we encourage others to extend and/or improve on this script to make it do even more :)
To assist that we have uploaded the files as a self-contained project on GitHub:
https://github.com/piborg/ultraborg-web
The source code
#!/usr/bin/env python
# coding: Latin-1
# Creates a web-page interface for UltraBorg
# Import library functions we need
import UltraBorg
import time
import sys
import threading
import SocketServer
# Settings for the web-page
webPort = 80                            # Port number for the web-page, 80 is what web-pages normally use
displayRate = 2                         # Number of times to read the ultrasonic readings per second
sliderHeight = 800                      # The number of pixels high to make the sliders
# Setup the UltraBorg
global UB
UB = UltraBorg.UltraBorg()              # Create a new UltraBorg object
UB.Init()                               # Set the board up (checks the board is connected)
# Class used to implement the web server
class WebServer(SocketServer.BaseRequestHandler):
    def handle(self):
        global UB
        # Get the HTTP request data
        reqData = self.request.recv(1024).strip()
        reqData = reqData.split('\n')
        # Get the URL requested
        getPath = ''
        for line in reqData:
            if line.startswith('GET'):
                parts = line.split(' ')
                getPath = parts[1]
                break
        if getPath.startswith('/distances-once'):
            # Ultrasonic distance readings
            # Get the readings
            distance1 = int(UB.GetDistance1())
            distance2 = int(UB.GetDistance2())
            distance3 = int(UB.GetDistance3())
            distance4 = int(UB.GetDistance4())
            # Build a table for the values
            httpText = '<html><body><table border="0" style="width:100%%"><tr>'
            if distance1 == 0:
                httpText += '<td width="25%%"><center><h2>None</h2></center></td>'
            else:
                httpText += '<td width="25%%"><center><h2>%04d</h2></center></td>' % (distance1)
            if distance2 == 0:
                httpText += '<td width="25%%"><center><h2>None</h2></center></td>'
            else:
                httpText += '<td width="25%%"><center><h2>%04d</h2></center></td>' % (distance2)
            if distance3 == 0:
                httpText += '<td width="25%%"><center><h2>None</h2></center></td>'
            else:
                httpText += '<td width="25%%"><center><h2>%04d</h2></center></td>' % (distance3)
            if distance4 == 0:
                httpText += '<td width="25%%"><center><h2>None</h2></center></td>'
            else:
                httpText += '<td width="25%%"><center><h2>%04d</h2></center></td>' % (distance4)
            httpText += '</tr></table></body></html>'
            self.send(httpText)
        elif getPath.startswith('/set/'):
            # Servo position setting: /set/servo/position
            parts = getPath.split('/')
            # Get the power levels
            if len(parts) >= 4:
                try:
                    servo = int(parts[2])
                    position = float(parts[3])
                except:
                    # Bad values
                    servo = 0
                    position = 0.0
            else:
                # Bad request
                servo = 0
                position = 0.0
            # Ensure settings are within limits
            if position < -1:
                position = -1
            elif position > 1:
                position = 1
            # Set the new servo position
            if servo == 1:
                UB.SetServoPosition1(position)
            elif servo == 2:
                UB.SetServoPosition2(position)
            elif servo == 3:
                UB.SetServoPosition3(position)
            elif servo == 4:
                UB.SetServoPosition4(position)
            # Read the current servo positions
            position1 = UB.GetServoPosition1() * 100.0
            position2 = UB.GetServoPosition2() * 100.0
            position3 = UB.GetServoPosition3() * 100.0
            position4 = UB.GetServoPosition4() * 100.0
            # Build a table for the values
            httpText = '<html><body><table border="0" style="width:100%%"><tr>'
            httpText += '<td width="25%%"><center><h2>%.0f %%</h2></center></td>' % (position1)
            httpText += '<td width="25%%"><center><h2>%.0f %%</h2></center></td>' % (position2)
            httpText += '<td width="25%%"><center><h2>%.0f %%</h2></center></td>' % (position3)
            httpText += '<td width="25%%"><center><h2>%.0f %%</h2></center></td>' % (position4)
            httpText += '</tr></table></body></html>'
            self.send(httpText)
        elif getPath == '/':
            # Main page, move sliders to change the servo positions
            httpText = '<html>\n'
            httpText += '<head>\n'
            httpText += '<style>\n'
            httpText += ' input[type=range][orient=vertical]\n'
            httpText += ' {\n'
            httpText += '  writing-mode: bt-lr; /* IE */\n'
            httpText += '  -webkit-appearance: slider-vertical; /* WebKit */\n'
            httpText += '  padding: 0 0;\n'
            httpText += ' }\n'
            httpText += '</style>\n'
            httpText += '<script language="JavaScript"><!--\n'
            httpText += 'function SetServo(servo, position) {\n'
            httpText += ' var iframe = document.getElementById("setPosition");\n'
            httpText += ' position = position / 100.0;\n'
            httpText += ' iframe.src = "/set/" + servo + "/" + position;\n'
            httpText += '}\n'
            httpText += '//--></script>\n'
            httpText += '</head>\n'
            httpText += '<body>\n'
            httpText += '<table border="0" style="width:100%%;"><tr>'
            httpText += ' <td width="25%%"><center>'
            httpText += '  <input type="range" min="-100" max="100" value="0" orient="vertical" style="width:100%%; height:%dpx" onchange="SetServo(1, this.value);" />\n' % (sliderHeight)
            httpText += ' </center></td>'
            httpText += ' <td width="25%%"><center>'
            httpText += '  <input type="range" min="-100" max="100" value="0" orient="vertical" style="width:100%%; height:%dpx" onchange="SetServo(2, this.value);" />\n' % (sliderHeight)
            httpText += ' </center></td>'
            httpText += ' <td width="25%%"><center>'
            httpText += '  <input type="range" min="-100" max="100" value="0" orient="vertical" style="width:100%%; height:%dpx" onchange="SetServo(3, this.value);" />\n' % (sliderHeight)
            httpText += ' </center></td>'
            httpText += ' <td width="25%%"><center>'
            httpText += '  <input type="range" min="-100" max="100" value="0" orient="vertical" style="width:100%%; height:%dpx" onchange="SetServo(4, this.value);" />\n' % (sliderHeight)
            httpText += ' </center></td>'
            httpText += '</tr></table>'
            httpText += '<iframe id="setPosition" src="/set/0/0" width="100%%" height="100" frameborder="0"></iframe>\n'
            httpText += '<br /><center><h2>Distances (mm)</h2></centre><br />\n'
            httpText += '<iframe src="/distances" width="100%%" height="100" frameborder="0"></iframe>\n'
            httpText += '</body>\n'
            httpText += '</html>\n'
            self.send(httpText)
        elif getPath == '/servo':
            # Alternative page with only servo control, move sliders to change the servo positions
            httpText = '<html>\n'
            httpText += '<head>\n'
            httpText += '<style>\n'
            httpText += ' input[type=range][orient=vertical]\n'
            httpText += ' {\n'
            httpText += '  writing-mode: bt-lr; /* IE */\n'
            httpText += '  -webkit-appearance: slider-vertical; /* WebKit */\n'
            httpText += '  padding: 0 0;\n'
            httpText += ' }\n'
            httpText += '</style>\n'
            httpText += '<script language="JavaScript"><!--\n'
            httpText += 'function SetServo(servo, position) {\n'
            httpText += ' var iframe = document.getElementById("setPosition");\n'
            httpText += ' position = position / 100.0;\n'
            httpText += ' iframe.src = "/set/" + servo + "/" + position;\n'
            httpText += '}\n'
            httpText += '//--></script>\n'
            httpText += '</head>\n'
            httpText += '<body>\n'
            httpText += '<table border="0" style="width:100%%;"><tr>'
            httpText += ' <td width="25%%"><center>'
            httpText += '  <input type="range" min="-100" max="100" value="0" orient="vertical" style="width:100%%; height:%dpx" onchange="SetServo(1, this.value);" />\n' % (sliderHeight)
            httpText += ' </center></td>'
            httpText += ' <td width="25%%"><center>'
            httpText += '  <input type="range" min="-100" max="100" value="0" orient="vertical" style="width:100%%; height:%dpx" onchange="SetServo(2, this.value);" />\n' % (sliderHeight)
            httpText += ' </center></td>'
            httpText += ' <td width="25%%"><center>'
            httpText += '  <input type="range" min="-100" max="100" value="0" orient="vertical" style="width:100%%; height:%dpx" onchange="SetServo(3, this.value);" />\n' % (sliderHeight)
            httpText += ' </center></td>'
            httpText += ' <td width="25%%"><center>'
            httpText += '  <input type="range" min="-100" max="100" value="0" orient="vertical" style="width:100%%; height:%dpx" onchange="SetServo(4, this.value);" />\n' % (sliderHeight)
            httpText += ' </center></td>'
            httpText += '</tr></table>'
            httpText += '<iframe id="setPosition" src="/set/0/0" width="100%%" height="100" frameborder="0"></iframe>\n'
            httpText += '</body>\n'
            httpText += '</html>\n'
            self.send(httpText)
        elif getPath == '/distances':
            # Repeated reading of the ultrasonic distances, set a delayed refresh
            # We use AJAX to avoid screen refreshes caused by refreshing a frame
            displayDelay = int(1000 / displayRate)
            httpText = '<html>\n'
            httpText += '<head>\n'
            httpText += '<script language="JavaScript"><!--\n'
            httpText += 'function readDistances() {\n'
            httpText += ' var xmlhttp;\n'
            httpText += ' if (window.XMLHttpRequest) {\n'
            httpText += '  // code for IE7+, Firefox, Chrome, Opera, Safari\n'
            httpText += '  xmlhttp = new XMLHttpRequest();\n'
            httpText += ' } else {\n'
            httpText += '  // code for IE6, IE5\n'
            httpText += '  xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");\n'
            httpText += ' }\n'
            httpText += ' xmlhttp.onreadystatechange = function() {\n'
            httpText += '  var div = document.getElementById("readDistances");\n'
            httpText += '  var DONE = 4;\n'
            httpText += '  var OK = 200;\n'
            httpText += '  if (xmlhttp.readyState == DONE) {\n'
            httpText += '   if (xmlhttp.status == OK) {\n'
            httpText += '    div.innerHTML = xmlhttp.responseText;\n'
            httpText += '   } else {\n'
            httpText += '    div.innerHTML = "<center><h2>Failed reading distances (not running?)</h2></center>";\n'
            httpText += '   }\n'
            httpText += '  }\n'
            httpText += ' }\n'
            httpText += ' xmlhttp.open("GET","distances-once",true);\n'
            httpText += ' xmlhttp.send();\n'
            httpText += ' setTimeout("readDistances()", %d);\n' % (displayDelay)
            httpText += '}\n'
            httpText += '//--></script>\n'
            httpText += '</head>\n'
            httpText += '<body>\n'
            httpText += '<body onLoad="setTimeout(\'readDistances()\', %d)">\n' % (displayDelay)
            httpText += '<div id="readDistances"><center><h2>Waiting for first distance reading...</h2></center></div>\n'
            httpText += '</body>\n'
            httpText += '</html>\n'
            self.send(httpText)
        else:
            # Unexpected page
            self.send('Path : "%s"' % (getPath))
    def send(self, content):
        self.request.sendall('HTTP/1.0 200 OK\n\n%s' % (content))
# Run the web server until we are told to close
httpServer = SocketServer.TCPServer(("0.0.0.0", webPort), WebServer)
try:
    print 'Press CTRL+C to terminate the web-server'
    while True:
        httpServer.handle_request()
except KeyboardInterrupt:
    # CTRL+C exit
    print '\nUser shutdown'
print 'Web-server terminated.'
      
