Evolution of a Blog

This blog has evolved as I have as a maker. It starts at the beginning of my journey where I began to re-tread my tires in the useful lore of micro electronics and the open-source software that can drive them. While building solutions around micro-electronics are still an occasional topic my more recent focus has been on the 3D Printing side of making.

Thursday, October 11, 2012

Socket Communications (PHP to Python)

The first phase of this project is to get the 'bot working as a rather expensive RC toy with a web application accessed via my iPad being the control panel.


The idea here was one of simple expediency.  One of the other languages that I have decided to learn is PHP.   I have a forms development environment (FB4PHP) that makes it easy to build interactive applications (though it is still a beta product).  The thought was that the robot could periodically reach out via http and grab commands from the server where my little micro app was running.

Unfortunately the world of the internet is a world of latency and occasionally commands were taking way to long to get to the 'bot.   So naturally, this being an educational project, I decided that socket to socket communications would be just the trick!   The server process will run on the RPi on the back of the 'bot and the web application will be the client.  

It did turn out to have much improved latency over the http solution but I still have some tuning to do.   Luckily we have proximity detection for when a stop command just doesn't make it in time!

Here are the code snippets for each side of the conversation (first the server running in Python on the RPi):

    #  Create and open the socket will be listening on
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.bind(("192.168.1.95", 5555))
    server_socket.listen(5)

    # Inside processing loop we wait for a connection
    client_socket, address = server_socket.accept()
    # First get the size of the package we will be getting...
    packageLength = client_socket.recv(1)
    if len(packageLength) == 0:
        break
    packageLength = ord(packageLength)


    # ... then get the package itself
    controlCommand = client_socket.recv(packageLength)
    if controlCommand == "Terminate":
        break


    ... 
    ... DO SOME STUFF 
    ...

    # Respond to the client with current telemetry and status

    client_socket.send(chr(len(sendTelemetry)))
    client_socket.send(sendTelemetry)

    client_socket.close()


Then the client running in PHP on a server in the cloud:

    function CommunicateWithRobot($Commands) {
        // Create a TCP/IP socket.
        $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
        if ($socket === false) {
            return "Error: socket_create() failed: reason: " .
                socket_strerror(socket_last_error());
        }
        // Connect to the server running on the 'bot
        $result = socket_connect($socket, '99.99.99.99', '5555');
        if ($result === false) {
            return "Error: socket_connect() failed.\nReason: ($result) " .
                socket_strerror(socket_last_error($socket));
        }
        // Write two transmission, one of the size of the package,
        // and the second the package itself
        socket_write($socket, chr(strlen($Commands)), 1);
        socket_write($socket, $Commands, strlen($Commands));

        // Get the response from the server - our current telemetry
        $resultLength = socket_read($socket, 1);
        if (strlen($resultLength) == 0) {
            return "Error: emptyness passed back from server";
        }
        else {
            $Telemetry = socket_read($socket, ord($resultLength));
        }
        socket_close($socket);
        return $Telemetry;
    }

Tuesday, October 9, 2012

How Much Power is Needed?

This is supposed to be a 'bot that can scurry around the floor so obviously it needs to be battery powered.   The Rover comes with an integral six battery pack but that will not be enough so I am dedicating it to power the motors and sensors (other than the compass which I am powering from the Arduino's 3.3v power supply. 

The Raspberry Pi message boards indicate that the RPi runs at around 2.5 or so watts.   To this I have to add the  overhead of the USB hub, a wireless card, a webcam, and the Arduino.   Due to some power supply issues, namely a wimpy regulator that is supplying power to all the smart stuff, I have not been able to measure actual current draw from the RPi yet but I was able to see that all the rest of the stuff was drawing about 200-230 mA.

Assuming that the 2.5 watts (or 500 mA) is accurate, and adding 240 mA for the USB hub and the stuff it powers, I will burn 750 mA or 3.75 watts at 5v.   My battery pack for the smart stuff is the same as for propulsion so with old technology rechargeable batteries I would have between 600 and 1000 mAh at 7.2v or 4.3 to 7.2 watt hours.   Assuming that I lose 20% of my power to translation from 7.2v to 5v that leaves me with from 3.5 to 5.8 watt hours.   Burning 3.75 watts means from 0.9 to 1.5 hours of run time.

Power for the 'Brain'
New technology NiCd are supposed to deliver from 2000 to 2450 mAh which would yield run times of 3.6 to 4.4 hours which would be a huge improvement.   I have some on order (with a new power regulator) but for now have been using some worn out, old technology, NiCds and they don't really give me squat (though that may also be the wimpy power regulator that I am using since I fried my better one).

Here is a spreadsheet that calculates this stuff.

If you are doing ANYTHING with an RPi you will need to worry about power.  I can testify that it is VERY sensitive to how you feed it.

Friday, October 5, 2012

Current Robot Glamour Shot

I am posting this blog in retro fashion but thought that I would fast forward to the current moment and show a picture of how the robot is looking right now.
As of October 4th, 2012
There has been a fair amount of work to get to this point but there is a LOT more left to do.   In general, at this point, the hardware is largely integrated though there are still a number of things either not working 100% (compass accuracy) or not at all (encoders and current overload detection).

From a software perspective it is even more bleak as all the robot does at the moment is go backwards and forwards under command from its web interface!   It is sensitive to collision avoidance, it does upload images, and it can do a rudimentary mapping scan, but there is much more undone than done!

Wednesday, October 3, 2012

Triple Axis Magnetometer (e.g. Compass)

If one's 'bot is going to know where it is going to need to know in what direction it is traveling.   Enter the HMC5883L Triple Axis Magnetometer.  I am not sure why I got this particular device as all I need is one axis!   In any case it is another cool little device if only I could get it to work properly.   More on that later.

The device has four connection points, two of which are for 3.3v power.  I am using the power from the Arduino though I could have used it from the Explorer PCB.   The other two connection points are for the "I2C / TWI" interface used by the compass.  This is a serial type of interface that can have multiple devices like HMC5883L on the same circuit connected back to the Arduino.   The two connections are labeled SCL (clock) and SDA (data) on both the compass and on the Arduino.

Once the compass is connected you need to use the "Wire" interface to talk to it.   I scoured the internet for sample code and pulled the below from it for my purposes.   Note that what is shown is an excerpt from my  Python to Arduino Interface library so it will not run as shown but still gives you the idea.

There is a really good article on the Love Electronics website that discusses this device in detail.

Now a little about my current issue with this device...I can't get it to act like a sane compass.   You would think that west and east (90 and 180) would be on the same axis but this does not seem to be the case.   I can deal with the advertized accuracy of the compass (+/- 2 degrees) but I am seeing deviations from actual compass bearings of dozens of degrees.   At times it even flips by 90 or more degrees.

I am not sure that I can blame the device though.  My wife and I are living in a temporary rental while we navigate the painful English house buying process.  The place we are in is really quite nice and sits right on the Thames at Maidenhead.   Right on the Thames and next to the famous 'sounding arches' railroad bridge built by Brunel to host a major train line into London.    Major as in really busy and next to as in just about right under.    I am assuming the electromagnetic fields from the railroad are what is messing with my compass though I have not taken it afield to prove this.

    // I2C Arduino Library for a compass
    #include <Wire.h>
    // Reference the HMC5883L Compass Library
    #include <HMC5883L.h>
    // Store our compass as a variable.
    HMC5883L compass;
    int compassError = FALSE;


    // ***** Initialize Compass
    case 'I':
        outputLine = "0,";
        Wire.begin();
        compass = HMC5883L();
        compassError = compass.SetScale(1.3);
        if (compassError != 0) {
            if (debugOn == TRUE) {
                outputLine = "1,";
                outputLine += compass.GetErrorText(compassError);
                cmdSubInvalid = FALSE;
                break;
            }                   
        }
        compassError = compass.SetMeasurementMode(Measurement_Continuous);
        if(compassError != 0) {
            if (debugOn == TRUE) {
                outputLine = "1,";
                outputLine += compass.GetErrorText(compassError);
                cmdSubInvalid = FALSE;
                break;
            }                   
        }
        serialPrint(outputLine);
        cmdSubInvalid = FALSE;
        break;

    // ***** Compass bearing
    case 'B':
        xSum = 0;
        MagnetometerRaw raw = compass.ReadRawAxis();
        MagnetometerScaled scaled = compass.ReadScaledAxis();
        int MilliGauss_OnThe_XAxis = scaled.XAxis;// (or YAxis, or ZAxis)
        float heading = atan2(scaled.YAxis, scaled.XAxis);
        // Declination for London should be 24.73
        float declinationAngle = 24.73/1000;
        heading += declinationAngle;
        if(heading < 0)
            heading += 2*PI;
        if(heading > 2*PI)
            heading -= 2*PI;
        float headingDegrees = heading * 180/M_PI;

        int tempHeadingDegrees = int(headingDegrees);
        int tempHeadingDegreesF = (headingDegrees - tempHeadingDegrees) * 100;
        String outHeadingDegrees = String(tempHeadingDegrees);
        outHeadingDegrees += ".";
        outHeadingDegrees += String(tempHeadingDegreesF);

        outputLine = "0,";
        outputLine += outHeadingDegrees;
        if (debugOn == TRUE) {
            outputLine += ",Compass reading";
        }
        serialPrint(outputLine);
        cmdSubInvalid = FALSE;
        break;


More on the compass story here.

Sunday, September 30, 2012

Multiple Failures

The very first "on battery" run of the 'bot ended with multiple failures resulting in an upside down situation.   Don't you just hate it when that happens?   Proximity sensors did not stop the motors from trying to climb the cabinet.   Interestingly the 'bot had both the power and the traction to do so!   Latency killed the stop command that was sent from my control panel with the two adding up to the scene on the right!



Ultra-short Webcam Capture Script

I think that the below is the shortest possible Python script to capture and then save an image from a webcam:

    import cv
    capture = cv.CaptureFromCAM(-1)
    frame = cv.QueryFrame(capture)
    cv.SaveImage( "Test.png", frame)


The -1 on the second line works if you only have one webcam otherwise you will have to specify the one you want to capture from...and since they move around...you have to add logic for that as well.

This is the basics of what I need but I have been chasing a problem with the webcam on the RPi for a while.   Namely that the first image is exposed correctly and all subsequent images are extremely under exposed.   This persists until the RPi is rebooted or the webcam unplugged and plugged back in.

I have tried unbinding/rebinding the device drivers thinking this would accomplish the same thing as the unplug/replug but to no avail.  The webcam that I am using works on Ubuntu and another webcam that I borrowed from my wife works through multiple exposures on the RPi. 

Obviously I can get another webcam but I do have a problem that you can see to the right.   I seriously doubt that my wife will happily watch me disassemble her webcam for this project!

Saturday, September 29, 2012

Pan and Tilt

I added the optional Pan and Tilt Kit to give my range finder and webcam a place to live where the could be pointed on an arc covering the front of the 'bot.   The kit is designed as an accessory for the Explorer PCB and mounts nicely on it.   The pan and tilt mechanism is not a particularly highly engineered piece of gear but it works.   I had a little trouble assembling it as the tilt mechanism is somewhat fiddly. 

Wiring the servos is really pretty simple as the Explorer PCB has sockets for four servos on each side of the board.   Power to the appropriate pins on each of the sockets and the control pin back to the Arduino and we were done with the wiring.

The code is pretty simple but does need the servo library to make things happen.   

    #include <Servo.h>     // create servo object to control a servo     
    Servo myservo;         
    void setup()
    { 
        // attach servo on pin 24
        myservo.attach(24); 
    }
    void loop()
    { 
        // Move the servo to 10
        myservo.write(10);
        delay(1000);      
        // Move the servo to 120
        myservo.write(120);
        delay(10000);
    }

One of the challenges that I had was in getting the servos centered.   Make sure you have power them up and centered them BEFORE you mount them!   I also have a fair amount of servo chatter at various points on their arcs...a sign that I did not do a very good job of assembling the pan and tilt kit!