Diddiborg & Ultraborg over Web

Hi

I am new to coding, and need some help with my new project.

Want to run Diddiborg with Ultraborg over Web. Have coppy/Paste code I found on this website. Having trouble getting the code free of buggs. Need help to get ahead, nice if somone coud loock at this.
Have been strugling back and forth, can not find the fault.

The problem is that web page is read twice.

GloerThoren

Attachments: 
piborg's picture

I think you have the bulk of the code correct, it is just the HTML under elif getPath == '/': which has a few of mistakes:

  • The line self.send(httpText) transmits the completed page to the browser, it should only be called after all of the httpText += lines
  • The </body> and </html> lines should only appear once in the HTML data at the end for any page
  • There are a few lines left over from the end of the section which makes the sliders, these are unneeded and may cause the page to display strangely

All that really needs to be changed is to remove lines 289 to 294, then it should work as intended.

I have attached a modified copy which has these lines removed for you already, plus I have moved the watchdog line outside of the if / elif / else block to make it a bit clearer that it happens every time.

gt213296@gmail.com's picture

Hi man

Tanks for the updated script. found some more cool stuff, added "save photo" button from diddi-web.
Now I want to make the diddy semi-/fully auto.
Would have liked some running bit for the motors . Trying to get criteria for semi automated security control of the motors . Have not figured out which bit that can be used for this. The goal is to get diddiborg to override manual controll when it comes near obstacles, dont want to bump in to things . Has written criteria from line 205 -

Have also problem with the new adde switch "semi auto" and "auto mode"
Want them to be on/off togle buttons.

Nice if you can give comments on what is written .

piborg's picture

That is a really cool idea, I do not think I have heard about anyone making a semi-automatic mode like this so far.

What we need to do is create another thread in the script.
This is so that the code can run on its own, not just when the button is pressed.

I have added a mode value with 3 options which controls how movement is done.
This is read by the new thread, which will perform the checking or movement at a regular interval.

The new thread has been called AutoMovement, it performs the necessary tasks depending on the mode:

  • Manual mode
    Checks every second to see if the mode has changed only
  • Semi-automatic mode
    Runs the checks you added twice per second
  • Automatic mode
    Will run some logic five times per second, so far does nothing

The HTTP responses now check these modes and change them as needed to provide the toggle functionality.
Motors are turned off when exiting auto or semi-auto mode.
I have also prevented user movement in auto mode, the stop button should both stop the motors and switch to manual mode.

Hopefully this will allow you to carry on developing the auto and semi-auto modes :)

gt213296@gmail.com's picture

Hi
Thanks for all your help . Cool that you like the project.
When I run the script I get an error .

pi@raspberrypi ~ $ sudo ~/metalborg/metalWeb.py
Loading UltraBorg on bus 1, address 36
Found UltraBorg at 36
UltraBorg loaded on bus 1
Loading PicoBorg Reverse on bus 1, address 44
Found PicoBorg Reverse at 44
PicoBorg Reverse loaded on bus 1
Setup camera
Setup the stream processing thread
Wait ...
Start the stream using the video port
Setup the watchdog
Setup the automatic movement
Traceback (most recent call last):
File "/home/pi/metalborg/metalWeb.py", line 546, in
autoMovement = AutoMovement()
File "/home/pi/metalborg/metalWeb.py", line 167, in __init__
super(Watchdog, self).__init__()
TypeError: super(type, obj): obj must be an instance or subtype of type

piborg's picture

Apologies, I made a slight mistake in the script (line 167) and used the wrong name!

What I wrote was super(Watchdog, self).__init__(),
It should have been super(AutoMovement, self).__init__().

I have fixed this line and re-attached the script.

gt213296@gmail.com's picture

Exception happened during processing of request from ('192.168.0.17', 54348)
Traceback (most recent call last):
File "/usr/lib/python2.7/SocketServer.py", line 295, in _handle_request_noblock
self.process_request(request, client_address)
File "/usr/lib/python2.7/SocketServer.py", line 321, in process_request
self.finish_request(request, client_address)
File "/usr/lib/python2.7/SocketServer.py", line 334, in finish_request
self.RequestHandlerClass(request, client_address, self)
File "/usr/lib/python2.7/SocketServer.py", line 649, in __init__
I want to show time and dat in metal web
I godt tis message.

File "/home/pi/metalborg/metalWeb.py", line 552, in handle
httpText += 'Local Time: %.0f %%, %.0f %%' % (datetime.datetime())
TypeError: Required argument 'year' (pos 1) not found
----------------------------------------

gt213296@gmail.com's picture

Found it, this web side have a tutorial
Real time changing Clock showing date and time

http://www.plus2net.com/javascript_tutorial/clock.php

gt213296@gmail.com's picture

Hello

I have been strugling, have conected xloborg, lock like temp reading is static +3C
I dont know if i have the corect code. Line 223.

Do you have a script for reading degrees on x and y axis.
Is it posible to import a pygame script to be visual in web? Tinkig abot XLoLevel script I foun on yor web side.

Have tried to connect time , temp , clock & compass against web . Struggling to get it . Very grateful if someone could help me started . Wants to collect al information in a frame.

GT

Attachments: 
piborg's picture

There is a strange character on the temperature reading line at the moment:
print '%+02d°C' % (XLoBorg.ReadTemperature())
it should be:
print '%+02d°C' % (XLoBorg.ReadTemperature())
this explains the "C" in the result.

To get the correct temperature you will need to work out what the offset is.
What you want to do is get another temperature sensor which is already calibrated and mesure the temperature where the small black chip is and from the XLoBorg itself at the same time.

An easy way to do this is to place the Raspberry Pi with XLoBorg in a room with a nice stable temperature.
Place a normal thermometer next to the Raspberry Pi and give them both a while (say half an hour) to match the room temperature.

The difference between the two temperatures should be used as the offset.
Set this value before reading temperatures like this: XLoBorg.tempOffest = 25

If you want the pitch and roll from the accelerometer then I think this code will do what you want:

# Get the accelerometer readings
ax,ay,az = XLoBorg.ReadAccelerometer()
# Calculate pitch and roll in radians
pitch = -math.atan2(ax, math.sqrt(pow(ay,2) + pow(az, 2)))
roll = math.atan2(ay, az)
# Convert to degrees
pitch = pitch * 180.0 / math.pi;
roll = roll * 180.0 / math.pi;
print pitch, roll

The diagram below shows which angles are pitch, roll, and yaw.



"Yaw Axis Corrected" by Yaw_Axis.svg: Auawisederivative work: Jrvz (talk) - Yaw_Axis.svg. Licensed under CC BY-SA 3.0 via Commons - https://commons.wikimedia.org/wiki/File:Yaw_Axis_Corrected.svg#/media/Fi...

Unfortunately I do not think the pygame code will work via the web, it is designed to run on the machine displaying the screen.

To get your values shown in the web browser you need to add some new entries into the httpText variable.
The easiest place to add this is to the "distances-once" page where it currently reads these values:

# Build a table for the values
httpText = '<html><body><center>'
httpText += '<table border="0" style="width:50%"><tr>'
if distance1 == 0:
    httpText += '<td width="25%"><center>None</center></td>'
else:
    httpText += '<td width="25%%"><center>%04d</center></td>' % (distance1)
if distance2 == 0:
    httpText += '<td width="25%"><center>None</center></td>'
else:
    httpText += '<td width="25%%"><center>%04d</center></td>' % (distance2)
if distance3 == 0:
    httpText += '<td width="25%"><center>None</center></td>'
else:
    httpText += '<td width="25%%"><center>%04d</center></td>' % (distance3)
if distance4 == 0:
    httpText += '<td width="25%"><center>None</center></td>'
else:
    httpText += '<td width="25%%"><center>%04d</center></td>' % (distance4)
httpText += '</tr></table>'
httpText += 'Heading: %.0f°<br />' % (heading)
httpText += 'Temperature: %+02d°C<br />' % (XLoBorg.ReadTemperature())
httpText += 'Time: %s<br />' % (localtime)
httpText += '</center></body></html>'
self.send(httpText) 

You may also need to make the "distances" frame larger, which you can do by increasing the value for height on this line:
httpText += '<iframe src="/distances" width="100%" height="200" frameborder="0"></iframe>\n'
in the elif getPath == '/': section.

gt213296@gmail.com's picture

Thanks for your time helping me out.
This gets me further in the project.

gt213296@gmail.com's picture

Hi again

Issue no.1
The code for pitch and roll works fine as long as Diddy standstill.
Issue occurs when the the motors are running, signals are contaminated, there is not much vibrations generated. So I'm guessing that signals are contaminated of vibration/eletric/magnetic interferens.
Anny coments on the issue?

I'm guessing that signal also must be average calculate over a short time before they will give any sence. This is way over my skills of coding and mapping. Maby i put my hands on the issue in the future :)
XLoborg is anyway a cool instrument to play with even if it did not fit perfectly with what I envisioned me.

Issue no.2
Need some inputt how to make the "semi auto" function. I dont se how to solver the code.
Is it posible to copy some parts from the manual driving code and implementate it in the Semi Mode part?

Do you know a code to monitor the wifi signal on RPI, cool to have it wissible on the screenshowing %.

Nice if u can help me out.

Tanks

Attachments: 
piborg's picture

I think the problem with the pitch and roll values is due to the movement itself.

Basically the accelerometer measures how much force is applied to the DiddyBorg, by gravity in this case.
When you move the DiddyBorg around it also sees the force applied by the motors to move him along.

What you could do is figure out what the XLoBorg reading is when he is moving on a level surface compared with being stationary.
You can then subtract this difference from the readings you get normally when he is moving.

The semi-auto mode is an interesting problem, it will probably take a bit of time to figure out how best top do things.

What I would suggest is that you store the values which are set in the elif getPath.startswith('/set/'): section into some global variables (like we have done for movementMode) so you can use them again in the semi-auto code.
The values you will want to copy are driveLeft and driveRight.

The wireless signal I can definitely give you code for.
First we will need to add the os module to the import list:

import os

Then when you want the WiFi signal strength we can do the following:

# Get the WiFi signal strength using iwconfig
wifiQuery = os.popen('iwconfig wlan0|grep Link|cut -d"=" -f2|cut -d" " -f1')
wifiResult = wifiQuery.readlines()
wifiQuery.close()
# See if we are connected
if len(wifiResult) == 0:
    # Not connected
    wifiStrength = 'Not connected'
else:
    # Connected, the value we get here will be a division, e.g. 5/20
    wifiResult = wifiResult[0] # Get the text
    wifiResult = wifiResult[:-1] # Remove the '\n' symbol from the end
    wifiValues = wifiResult.split('/')
    if len(wifiValues) == 2:
        # Get the percentage from the values
        wifiPercent = (float(wifiValues[0]) / float(wifiValues[1])) * 100.0
        wifiStrength = '%.0f %%' % (wifiPercent)
    else:
        # Unexpected, show the returned text instead
        wifiStrength = wifiResult
# Send our value here (currently prints the text for testing)
print wifiStrength
gt213296@gmail.com's picture

I did this test with Diddi on top of a box, wheels hanging freely.
Regulated speed from 0 to 100%. The reading jumped +/- 14¤ at max speed.
I did several tests, checked that XLoBorg was fastened well. Also tried with some soft materal under TreBorg to elemenate smal vibrations. Same problem no change.

Have you tested XloBorg on a mashine?

gt213296@gmail.com's picture

Got the new Diddy Borg baseplate today have begun slowly to modify the rower.
The plan is to assemble all rower electronics on the bottom baseplate.

On the plate which is mounted in the middle I mount battery and any other alektronikk for future project.
Has a set up for second raspberry PI2 with Surveillance cam with pan til functions lying on my desk.

I will post pictures of the procject in this tread.

Images: 

Thanks to the useful post in http://forum.piborg.org/thunderborg/examples/users I have amended scripts from http://forum.piborg.org/comment/4102#comment-4102 above to add touch mode /touch, and added keyboard control (arrow keys) to /hold mode, also jpeg quality for streaming can be changed

Attachments: 

Your code above for getting wireless signal is very useful - can you perhap suggest where in the web ui code I could put it so I get a regularly refreshed value showing signal strength on screen all the time I am using the web ui?

piborg's picture

You could add this to the distance reading values as they are already updated regularly.

First I would add the signal strength as a callable function towards the top of the script:

def WiFiSignalStrength():
    # Get the WiFi signal strength using iwconfig
    wifiQuery = os.popen('iwconfig wlan0|grep Link|cut -d"=" -f2|cut -d" " -f1')
    wifiResult = wifiQuery.readlines()
    wifiQuery.close()
    # See if we are connected
    if len(wifiResult) == 0:
        # Not connected
        wifiStrength = 'Not connected'
    else:
        # Connected, the value we get here will be a division, e.g. 5/20
        wifiResult = wifiResult[0] # Get the text
        wifiResult = wifiResult[:-1] # Remove the '\n' symbol from the end
        wifiValues = wifiResult.split('/')
        if len(wifiValues) == 2:
            # Get the percentage from the values
            wifiPercent = (float(wifiValues[0]) / float(wifiValues[1])) * 100.0
            wifiStrength = '%.0f %%' % (wifiPercent)
        else:
            # Unexpected, show the returned text instead
            wifiStrength = wifiResult
    # Return the value
    return wifiStrength

Then update the /distances-once table to include the extra value.

        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())
            wifi = WiFiSignalStrength()
            
            # Build a table for the values
            httpText = '<html><body><center><table border="0" style="width:70%"><tr>'
            if distance1 == 0:
                httpText += '<td width="20%"><center>None</center></td>'
            else:
                httpText += '<td width="20%%"><center>%04d</center></td>' % (distance1)
            if distance2 == 0:
                httpText += '<td width="20%"><center>None</center></td>'
            else:
                httpText += '<td width="20%%"><center>%04d</center></td>' % (distance2)
            if distance3 == 0:
                httpText += '<td width="20%"><center>None</center></td>'
            else:
                httpText += '<td width="20%%"><center>%04d</center></td>' % (distance3)
            if distance4 == 0:
                httpText += '<td width="20%"><center>None</center></td>'
            else:
                httpText += '<td width="20%%"><center>%04d</center></td>' % (distance4)
            httpText += '<td width="20%%"><center>%s</center></td>' % (wifi)
            httpText += '</tr></table></body></html>'
            self.send(httpText) 

hi I'm interested in using the wifi readings but am not using any distance sensors how/where would this part of the script go. I'm struggling to understand this part

if getPath.startswith('/distances-once'): should I replace 'distances-once' with wifi?

piborg's picture

Yes, you can take the /distances-once code, remove the UltraBorg parts and make a /wifi-once version.

Since there is only one value to show we can remove the HTML table and simplify the code like this:

        if getPath.startswith('/wifi-once'):
            # Display the WiFi signal strength
            httpText = '<html><body><center>'
            httpText += WiFiSignalStrength()
            httpText += '</center></body></html>'
            self.send(httpText)

You will also need to add the def WiFiSignalStrength(): code in full from above.

To make this work you need the rest of the framework which reads this page regularly to have it shown on the screen. This is more complicated. This section replicates the /distances logic as /wifi. Put simply it re-reads the /wifi-once above at a regular interval and displays the result in a flicker-free way.

        elif getPath == '/wifi':
            # Repeated reading of the WiFi strength, 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 readWiFi() {\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("readWiFi");\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>Failed reading WiFi (not running?)</center>";\n'
            httpText += '   }\n'
            httpText += '  }\n'
            httpText += ' }\n'
            httpText += ' xmlhttp.open("GET","wifi-once",true);\n'
            httpText += ' xmlhttp.send();\n'
            httpText += ' setTimeout("readWiFi()", %d);\n' % (displayDelay)
            httpText += '}\n'
            httpText += '//--></script>\n'
            httpText += '</head>\n'
            httpText += '<body>\n'
            httpText += '<body onLoad="setTimeout(\'readWiFi()\', %d)">\n' % (displayDelay)
            httpText += '<div id="readWiFi"><center>Waiting for first distance reading...</center></div>\n'
            httpText += '</body>\n'
            httpText += '</html>\n'
            self.send(httpText)

Now there should be a /wifi page with a self-updating reading. All that needs to be done is to add it into the / and /hold pages. This is just a single line:

httpText += '<iframe src="/wifi" width="100%" height="200" frameborder="0"></iframe>\n'

It can be placed anywhere in the page, we put it just below the <input id="speed" line, which is where the speed slider is.

Hi, thanks for your help wth thewifi code. I think I've got every thing in the correct place however when it comes timw to run it I gat a SyntaxError: invalid syntax on line 199 ,elif getPath =='/wifi/':
I've tried various things, altered indent, rewording added parenthesis, no luck please help.

Attachments: 
piborg's picture

This is a simple one to fix :)

The order of the blocks is not important, but the first one must start with if and the rest should start with elif. The else should always be the last one:

if condition1:
    #code for 1
elif condition2:
    #code for 2
elif condition3:
    #code for 3
else:
    #code if no conditions are met

elif is Python's shorthand for "else if".

Thanks that helped I no longer get errors and the code runs. However, always a however! However once the page is loaded there is only a script Path:"/wifi-once" identify with wifi.
I've attached copy of the script any help would be appreciated.

Images: 
Attachments: 
piborg's picture

It looks like you are missing the /wifi-once section from above (see here).

Many thanks. Works a treat.

Evening , I amended the code and it loaded and ran, thanks. Today I tried it again hoping to work out the range of my wifi adapter. When I open http:// 192.168.x.x/ I get a recurring error , broken pipe. video freezes.
I then switch over to /hold or /stream and it runs as normal. The only difference is I don't have
httpText += '\n' in the /hold or/stream pages
Ive attached the error as it appeared on terminal. Also a copy of code I am running.
Hope you can help.

Attachments: 
piborg's picture

The problem is that you have two if sections for processing the URL:

if getPath.startswith('/cam.jpg'):

and

if getPath.startswith('/wifi-once'):

Because of this the else section at the bottom is running when you go to the /cam.jpg URL after the image has already been sent.

The error itself is because the connection is closed after sending the image. When the else section tries to send more data it fails with "Broken pipe".

The fix is very simple, change the second if to elif instead :)

Thank you so much, everything I wanted is now working like a dream!!!

Just need to upgrade Picoborg Reverse to a Thunderborg and then I can show battery level as well :

Hi

Been playing about with the semi-auto mode and it works sometimes and stops robot when detected object less than x mm away, but other times it doesn't and often get error similar to below

any thoughts please

Failed reading ultrasonic #1 distance!
Exception in thread Thread-4:
Traceback (most recent call last):
File "/usr/lib/python2.7/threading.py", line 801, in __bootstrap_inner
self.run()
File "./MyRobotv4.py", line 223, in run
distance1 = int(UB.GetDistance1())
TypeError: int() argument must be a string or a number, not 'NoneType'

piborg's picture

The problem you are having is that the UB.GetDistanceX calls can return None. This happens when there is no sensor or it did not get a valid reading.

What you will need to do is detect if the reading is valid first before using it:

newDistance1 = UB.GetDistance1()
if newDistance1 is None:
    # Bad reading, skip or use the last value
    pass
else:
    # Good reading, go ahead and use
    distance1 = int(newDistance1)

Because in /touch or /hold mode it sends /off when key let up or button not pushed this then puts robot back in manual_mode thus turning off semi-auto every time. So think I need to change code so when not clicking button or pressing key it sets speeds to 0,0 rather than sending /off

Thoughts please?

piborg's picture

I think you have the right idea already :)

Sending motor speeds of 0 will make the robot stop, but avoids the rest of the off logic in the code.

Hi, for the semi-auto I also need to amend so if say front sensors less than x mm it stops BUT only if direction is going forward, as at the moment when you go backwards and front sensors are less than x mm it stops again.

So my question is how can I tell direction of travel?

piborg's picture

You can tell the direction of travel by the current motor speeds :)

You could either record the values globally when set, or read them using PBR.GetMotor1() and -PBR.GetMotor2().

excellent, thanks for replies.

/off issue sorted by changing motor speeds for onmouseup and ontouchend
changed sensor code to handle null reading

Can you give me an idea of coding logic to get direction using PBR.GetMotor1/2 etc, not great at Python and still trying to learn the code, basically just need to check if going forwards (which would include spin left and sping right I suppose) or backwards, then I will get it sorted

Once I have done all these improvements / fixes I will add to github and do pull request so everyone can make use of them :)

piborg's picture

This is the basic logic to get a single direction from the left / right motor power.

# Get raw power levels
driveRight = PBR.GetMotor1()
driveLeft = -PBR.GetMotor2()

# Get the average power of both motors
averagePower = (driveLeft + driveRight) / 2

# Determine our movement direction
if averagePower > 0:
    # Forwards
    direction = +1
elif averagePower < 0:
    # Reverse
    direction = -1
elif (driveLeft == 0) and (driveRight == 0):
    # Stopped
    direction = 0
else:
    # Spinning on the spot
    direction = 0

Spinning on the spot is an odd one, ideally the robot is not actually moving forward or backward, just rotating. You could set this to any of the +1, -1, or 0 values depending on what checks you would want to run.

thanks for reply

Getting error as below though::-

averagePower = (driveLeft + driveRight) / 2
TypeError: unsupported operand type(s) for +: 'float' and 'NoneType'

and keep getting

Failed reading motor 1 drive level!

piborg's picture

In that case it would be better to keep a note of the last set values instead.

Add a new value to the globals which will hold both left and right power:

global lastPower
lastPower = (0, 0)

Any function that changes the motor power (SetMotorX or MotorsOff) will need the global at the top:

global lastPower

Next anywhere the motors are set to new power levels store them:

lastPower = (driveLeft, driveRight)

and anywhere the motors are turned off store zeros:

lastPower = (0, 0)

Now you should be able to read the stored values instead of getting them from the board:

# Get raw power levels
global lastPower
driveLeft, driveRight = lastPower

# Get the average power of both motors
averagePower = (driveLeft + driveRight) / 2

# Determine our movement direction
if averagePower > 0:
        # Forwards
        direction = +1
elif averagePower < 0:
        # Reverse
        direction = -1
elif (driveLeft == 0) and (driveRight == 0):
        # Stopped
        direction = 0
else:
        # Spinning on the spot
        direction = 0

Thanks for that - will start testing it later today :)

Subscribe to Comments for &quot;Diddiborg &amp;amp; Ultraborg over Web&quot;