Skip to main content

Abject, disorientated programming

If we're honest, we've been feeling a little exhausted lately.  It's been a bit too hectic, with all the stuff that Rosie's been up to.  Don't get us wrong.  We want her to be happy, energetic and excitable... but also to be a lot more careful.  You know, not to destroy our house from top to bottom.

But it's also sort of our own fault too.  Rosie is... erm... (how can we be polite about this..?) a little... wide.  After all, let's not kid ourselves.  She is (for now) still just a plastic box from the local supermarket.  Of the cheapest variety.  So while she has one ultrasonic distance sensor attached to her front - being positioned in the middle of her very un-aerodynamic, flat body - its coverage isn't exactly great.  In fact, we've concluded: it's actually terrible.  And that's why we think she has table legs for breakfast, or wrestles gnomes in garden centres, or chooses to play tag with the toilet brush (yuck!).  All very silly robot pastimes.  So silly, in fact, that she needs to be upgraded.

So we've decided.  Like cheeseburgers.  Chocolate bars.   Scoops of ice cream.  Two of something is almost always better than one (just ask your dentist).  With two HC-SR04 ultrasonic distance sensors, Rosie should have more information about the world around her.  She should know a lot more.  She might even be able to detect incoming objects, better.

Therefore, let's double up on our ultrasonic-ness.  Make our ultrasound code, well... more sound.  And dial up the volume of our sound waves in the name of Pi-ence.  And just maybe, it'll be just what prompts Rosie to find a better hobby.  One that doesn't involve broken tables, angry garden centre gnomes, and a dangerous liaison with a very filthy toilet brush.

You will need to have these:

  • Raspberry Pi 3 (kitted out with wheels), and Raspbian running on SDHC card
  • Computer from which you are connecting to the Raspberry Pi remotely
  • 2 × HC-SR04 ultrasonic distance sensors.  How many?  Dos.  Deux.  二.  두.  Oh, did we mention we need two of these? 
  • Some resistors, soldering iron, solder, cables and heat shrink if you want to take this seriously.  Very seriously.  More on these things later.

You will need to have done these things:

  1. I’ve got Pi(3) brain
  2. Why-Fi-ght the Wi-Fi?
  3. Hurrah-ndom inventions
  4. Don't reinvent the eel
  5. Achoo! Crash-choo! Episode I
  6. Achoo! Crash-choo! Episode II
  7. Web of Pies
  8. Hello supervision, goodbye supervision

You will need to do this:

  • Attach a second HC-SR04 ultrasonic distance sensor to the Pi's GPIO pins, using a voltage diver circuit if required (it's probably the case)
  • Modify the Python code to create a class for the ultrasonic distance sensor 
  • Adapt the code to make use of the class, and second sensor
  • Challenge the robot to crash into something

What do you get after all this?

In this episode, we briefly return to the mystical land of electronics.  Mainly, it's because unlike our first HC-SR04 ultrasonic distance sensor that plugged directly into designated GPIO pins on the RasPiRobot V3 motor controller board, with the second one, we're very much on our own.  Oh yes, and it's also an excuse to solder some stuff together using a soldering iron.

We can learn here, that a HC-SR04 ultrasonic distance sensor outputs a 5 volt electrical signal on its 'echo' pin when the sensor detects that a pulse sent via the 'trigger' pin was returned.  But we also learn that Raspberry Pi GPIO pins - when configured as an input pin - expects electrical signals back at 3.3 volts.  That doesn't sound very good?  No, it's the perfect recipe for some zapped circuitry (or so we're told).  And that's why we need to use two Resistors to create a Voltage Divider Circuit to convert the 5V output from the sensor (echo) into 3.3V.  Time to solder some resistors to wires and pretend to know a whole lot more about electronics.

Then, once we've connected our sensor (together with the home-made voltage divider circuit) to our spare GPIO pins, we want to modify Rosie's Python code to make use of it.  But before we do, we want to abandon RasPiRobot V3 motor controller board libraries' own function to interact with the sensor, and create our own.  And when we do, we'll use a dash of Object Orientated Programming in Python, using Classes.  This way, whether we have 1 distance sensor on our Rosie, or 1 million on Rosie v2100 Mark III Mars Explorer Edition, we can reuse our single class for each Instance of our sensor.

So that's how the theory goes.  Two ultrasonic distance sensors + more Python code = ∞ of Rosie happiness.  Let's put this into practice and see what happens.

This is simply too much detail:

Who wants more cables?  Better still, who wants random bits of electronic circuity attached to their robots?  If your answers to these questions are emphatic yeses, it's time to proceed to our next stage.

Thankfully for us, back when people were still wearing top hats and waistcoats, some very clever people worked out what the relationship is between very scientific stuff; like electrical power (measured in Watts - W), voltage (measured in Volts - V), current (measured in Amps - A) and resistance (measured in Ohms - Ω*).

*Yes, that is actually the symbol for a measure of electrical resistance.  We didn't make this one up.

You may have heard of some equations, like Voltage (V) = Current (I) × Resistance (R), otherwise known as Ohm's Law.  Georg Ohm's important discovery pretty much underpins all of electronics, so we might as well know about it.

V = I × R

How does this help us with Rosie?  It doesn't.  So let's move onto another boring equation.  We want to reduce our 5V 'echo' output down to 3.3V.  There's an equation that helps us with this too:

Vout = Vin × R2 / (R1 + R2)

The principles are helpfully explained here (and yes, Ohm's Law makes an appearance yet again).  In short, we want two resistors (a R1 and a R2) that if we connect together, provide us with a voltage of 3.3V at the point at which they connect.  As explained here, a 1kΩ resistor for R1 and a 2kΩ resistor for R2, provide us with a Vout value which is exactly two thirds of Vin.  So the 5V Vin, helpfully becomes, approximately, 3.3V Vout.  Just what we need.  Thanks Mr. Ohms.  You really are the reason why we're getting on grandly with our Raspberry Pi.

Now we could create this voltage divider circuit in lots of different ways.  It could be built in to the circuit board, like it is for the dedicated ultrasonic distance sensor pins on the RasPiRobot V3 motor controller board.  Or we could use, for example, a breadbord (nothing to do with bread, of the wheat variety).  But you wanted to solder some stuff, right?  Sure.  We did too.  So we simply soldered one end of the 1kΩ resistor to the cable attached to the HC-SR04 ultrasonic distance sensor's 'echo' pin, then soldered the 2kΩ resistor to the other end of the 1kΩ resistor.  The remaining end of the 2kΩ resistor was then soldered to the ground (0V) cable.  Crude.  Yes.  But quite effective, we found.

Where's the 3.3V output then, I hear you ask?  It's where the two resistors meet, and this is where we need to solder a new cable for our actual 'echo' pin that we connect down to the Pi's GPIO pin.  All the joints were finally covered in heat shrink, and come to think of it, so were the resistors too.

And here's an important public service announcement:

Soldering can be dangerous.  Like many things that are dangerous, it involves heat, and some tools (but not stunt bikes, or lethal animals... except later, a Python).  So don't do it by yourself if you've never done it before, or don't know what you're doing.
And if you're what others consider a child, always have an adult there to help you.  This way, you can laugh at their mistakes, rather than be in trouble for making them.

Great.  We now have random electronic bits hanging out of our plastic box.  And we think we've learnt some useful things about electricity.  What else do we need to do?

We did promise you some Python action in this post.  So here it is.

So far, we used the RasPiRobot V3 motor controller board's own library for our distance checking.  This made sense, as we were using the GPIO pins on the motor controller board for the HC-SR04, and we only had one of it.  That way, we were able to use the rrb3 module's get_distance() function to obtain our distance readings from the connected sensor.  Simples.

...But now that we need to use GPIO pins that are not on the motor controller board, and on the Pi itself, we ought to create our own bit of code to do the same thing.  We also now intend to have two sensors, but we may decide to have more in the future.  We might even stop using the RasPiRobot V3 motor controller board someday.  So creating reusable Python code that represents what a HC-SR04 sensor is and does seems like something that will save us time in the future.  We could then keep using the same bit of code, for each sensor we attach, using any supported GPIO pin of our choosing.

Aha!  We must be talking Object Orientated Programming?  We sure are.  Many programming languages support it.  Python does too.  And, surprisingly, we were actually already using it when we were doing this with our code to create specific instances of a RasPiRobot V3 motor controller board, or a RGB LED (aka 'the squid').  Only, we were using code that others had created for us.

rr = RRB3(9.0, 6.0)
rgb = Squid(16, 20, 21)

Object Orientated Programming (OOP) allows the coder - yes, that's now you - to define and use... guess what... Objects.  That's why it's called Object Orientated Programming, and not Meerkat Orientated Programming, as there are no meerkats to be seen during programming.  And how do we describe an object?  An object probably does something (OOP calls these functions, Methods).  Or it has probably got some attributes (Instance Variables).  These blueprints for Objects are called Classes.  And using a class, we can create many Instances of that object.  Confused?  Let's call on some furry friends to help out.

...And for some reason, we still have meerkats in our heads.  So here's an example of a meerkat class:

class Meerkat:
    def __init__(self, name, age, fur_colour):
        self._name = name
        self._age = age
        self._fur_colour = fur_colour
    def grow_older(self):
        self._age += 1

During initialisation of a specific instance of a meerkat (using this class as a template), we give our meerkat a name, age and fur colour.  These will be unique to each instance of a meerkat.  Meerkats can also grow old (really, they do - they don't stay cute forever), so we have a method (basically a function in the class) that adds one year to that meerkat's age.  We can now create many imaginary meerkats, and make them grow older, together.  Pretty pointless, we realise.

m1 = Meerkat("Bob", 2, "Brown")
m2 = Meerkat("Charleen", 5, "Golden")

We made some imaginary meerkats - christened Bob and Charleen - grow one year older using Python code.  Let's move swiftly on *move on people, nothing to see here* and actually do something useful with OOP and classes.

So what does a distance sensor need to be able to do?  Take temperature?  No - check distance, of course.  And all distance sensors need to do just that, so let's make those bits of Python code methods.  What else do we need to know about our distance sensors to use them?  The Raspberry Pi GPIO pins each uses for 'echo' and 'trigger' signals are pretty important, so that we know which pins to send our 'trigger' signal to, and from which pins we expect back the 'echo' signal.  Like the meerkats, perhaps we can provide a meaningful description too for our instances of the sensor, like about where they are positioned.  These can be our Instance Variables.

Let's give this a go.  First, we'll create a DistanceSensor class, which has been largely stolen borrowed from the bits relevant to the distance sensor of the RRB3 class.  It consists of a special '__init__' method which allows us to initially assign out our GPIO pins, and description, followed by a bunch of other methods to send a trigger to the sensor on the 'trigger' pin, and wait for a response on the 'echo' pin.  If you look closely, there are other stuff that happens in the class, to work out the distance to the nearest object using the speed of sound, and the time difference between the trigger and echo signals, but let's pretend that we understand all that for now.

class DistanceSensor:
    def __init__(self, sensor_description = None, GPIO_PIN_TRIGGER = None, GPIO_PIN_ECHO = None):
        self.sensor_description = sensor_description
        if (self.GPIO_PIN_TRIGGER == None) or (self.GPIO_PIN_ECHO == None):
            raise ValueError("Values must be provided for both Trigger and Echo GPIO pins")
        gpio.setup(self.GPIO_PIN_TRIGGER, gpio.OUT)
        gpio.setup(self.GPIO_PIN_ECHO, gpio.IN)
        #...then all our other methods to do stuff with the sensor

So like our friendly meerkats Bob (m1) and Charleen (m2), in our existing check_distance() function, we now create two instances of a DistanceSensor class - _ds1 and _ds2.  For each, during instantiation, we set their unique descriptions (no, we don't actually have to call them Bob and Charleen), and GPIO pins they use for 'echo' and 'trigger'signals.  We've also wrapped the object instantiations using the try / except clauses.  It should allow us to capture any exceptions (errors) raised by our class.  In fact, from the code above, you'll notice that if no GPIO pin numbers are provided, the class will raise a 'ValueError'.  Our bit of exception handling will exit the program, as we don't want Rosie travelling around the world without her distance sensors working correctly.

    _ds1 = DistanceSensor("Front left HC-SR04", 18, 23)
    _ds2 = DistanceSensor("Front right HC-SR04", 19, 26)
except Exception as e:

Whenever we want to get our distances from our ultrasonic sensors (_d1 and _d2), we can now do this to assign their recordings to our two variables, _distance_fl_cm and _distance_fr_cm:

_distance_fl_cm = _ds1.get_distance()
_distance_fr_cm = _ds2.get_distance()

...And because we want to change Rosie's alert level when either of the two sensors notice something interesting (and not necessarily both), we have changed the if statements to take into consideration this logic.

if ((_distance_fl_cm < _DISTANCE_DANGER_CM) and (_distance_fl_cm_last < _DISTANCE_DANGER_CM)) or ((_distance_fr_cm < _DISTANCE_DANGER_CM) and (_distance_fr_cm_last < _DISTANCE_DANGER_CM)):
    #...code to change alert level to critical, if not already
elif ((_distance_fl_cm < _DISTANCE_WARNING_CM) and (_distance_fl_cm_last < _DISTANCE_WARNING_CM)) or ((_distance_fr_cm < _DISTANCE_WARNING_CM) and (_distance_fr_cm_last < _DISTANCE_WARNING_CM)):
    #...code to change alert level to warning, if not already
elif ((_distance_fl_cm >= _DISTANCE_WARNING_CM) and (_distance_fl_cm_last >= _DISTANCE_WARNING_CM)) or ((_distance_fr_cm >= _DISTANCE_WARNING_CM) and (_distance_fr_cm_last >= _DISTANCE_WARNING_CM)):
    #...code to change alert level to normal, if not already

We've incorporated all this into our existing code, and named it ''. It looks a little something like this:

And that's about it.  We're not changing any of our Flask code for web access, or the HTML, or CSS.  The end result is subtle, but we've actually done a whole load of things behind scenes to make our code more reusable.  And using Python Object Orientated Programming, we can make Rosie's application a lot more powerful.

Now, let's return to name some more of our imaginary meerkats...

Even more reading:

There is excellent information on the technical specs of the HC-SR04 sensor, and the voltage divider circuit required, on the ModMyPi website.  Highly recommended reading if you are intending to do some soldering of your own.
Official Raspberry Pi information on GPIO pins can be found here:
Python's official documentation on classes can be found here:
We're continuing to make use of the Monk Makes' RasPiRobot V3 motor controller board, and its libraries written by Simon Monk:


Popular posts from this blog

Tea minus 30

We're fast approaching Christmas time.  And if robots were to make one simple observation about the human species during the Christmas festivities, it's that they watch a lot of TV.  A LOT.  Often, accompanied by an inappropriate amount of greenhouse gas-producing food.  Stuff you don't normally eat during the remainder of the year - for good reason.

And most so-called shows on TV are boring to robots like Rosie.  After all, why watch a minor subspecies of the human race - celebrities - stumble awkwardly around the dance floor, dressed like a faulty, sparking circuit board?  Such branch of entertainment doesn't require robots to engage any of their proud circuitry.  Their processors remain idle.  Memory under-utilised.

But if robots are to be part of people's homes (and blend in), they need to look at least a little interested in some of this irrational nonsense.  Nobody likes a party pooper.  A killjoy.  And this is where a certain subgenre of TV entertainment co…

Beam me up, Rosie!

How do you get from A to B?

You can't, as As and Bs are just letters in the alphabet. But if A is your house, and B is a meerkat village at your favourite safari park, you'd probably use a device equipped with GPS.  Not to be confused with UPS, who will deliver you your chosen meerkat through the post. And why on Earth would Rosie Patrol need one? Precisely, it's because she is on Earth that she needs one. Because our planet is rather big. Big enough to get lost in. And we don't want to lose our friendly plastic boxes on wheels. And maybe, eventually when she's clever enough, she'll go and defeat baddies on her own. And return home afterwards for a well deserved Earl Grey tea.

Besides, why wouldn't we want to add another three letter acronym to Rosie Patrol's repertoire?
All superheroes need:One Raspberry Pi 3, running Raspbian OSComputer from which you are connecting to the Raspberry Pi Probably the most important bit: a GPS receiver thingmy. …

Hello supervision, goodbye supervision

The time has finally come, to let our precious Rosie go.  Wish her luck (yes, she'll really need it).  Press the big red button.  And watch her destroy / navigate (delete as appropriate) the terrain*.  All on her own.  Without your help.  Well, sort of.

*Somewhat scientific term... for your lounge

You see, cool robots are never meant to be controlled by us clumsy humans.  If they want to be clumsy, they can be clumsy on their own.  That way we can deny all responsibility when they crash into the neighbour's car.  Or, less importantly, take over the world.

And by the way, Autonomous stuff seems to be in fashion these days.  Self-driving cars are just around the corner (maybe quite literally).  Very clever computers also beat humans at chess, apparently.  After all, if robots only do what we tell them to do, they are going to need us.  They're going to be just like us.  Oh no.  We want them to work alongside us, to do things that we simply can't be bothered to do.  Like …