Wasting your and my time

I had a really interesting experience recently which I hope might enlighten others as much as it did me:

I was approached (via LinkedIn) by a recruiter from one of the big tech firms, as they put it "based on the work you've been publishing on image analysis and learning models".  I would have to admit that doing this kind of work for one of the holy trinity sounds like a dream job for a hardcore geek like myself.

So I went along late one afternoon (they kindly offered to meet with me at their premises in town at the end of the day to allow me to focus on my current job).  To be completely honest, I am happy where I am, but what's the harm in hearing what they have to say, right?  What they had to say was truly fascinating.

After the usual niceties, we got down to my professional history.  "I see you've been working in the technology industry since the early 1980's.  That must be a misprint right?"  I briefly explained the somewhat circuitous path that led me to the giants of hardware and software over the years, and how in almost 40 years of work, the best thing I have learned is that I will always keep learning.

"But you don't have a higher qualification in anything relevant.  Just years on the job?  So who really did the work you've been publishing?"

True, I am for the most part self-taught.  I would have thought it's a good character trait.  I can assure you that work I publish is my own, and I'll always credit any and all sources and assistance.

"Wow.  That's impressive.  But if I had known you were so old, I wouldn't have wasted my time contacting you"

At that point, I simply apologised for the obvious inconvenience I had caused the recruiter, and asked to leave.  I was sorely tempted to leave with some cheap shot about wasting my time, and vow never to touch their products and services again but why just inflame an already uncomfortable situation.

I can't change the way someone else thinks.  We can only change our own minds.  This experience did give me cause to reconsider my own personal biases, and I'm going to do my best to see past them in future.

Thank you for your time.

Resuscitating a Scooba


I am not 100% sure where this is going, but it's going to be a fun journey...


I own an iRobot Roomba 630 floor cleaner, and it works well enough for our house.  But I discovered the iRobot "Create" which is designed for teaching and hacking in general.  As you can imagine, repurposing our cleaning robot for my hobby might not sit too well.

Using the local social networks, I picked up an iRobot "Scooba" for nothing.  According to the previous owner, it won't charge and the power light glows red when you plug it in.

I downloaded the service manual from one of those manuals websites, and learned this is likely to mean that the battery has failed and won't hold a charge.  I figured I could probably re-pack it and things would be sweet.

As with so many of my projects, the problem was a little more complex than I expected.  The reason why it won't charge is that the battery was missing, rather than simply faulty.  Given it's a specialised package, this could prove to be expensive.

I then had the brainwave of making one myself and somehow hooking it into the existing wiring.  I'm not going to be mopping floors, so being water resistant isn't a high priority.

My first DIY pack consists of twelve 2500mAh "AA" cells as shown in the next photo.


Using a set of alligator clips I connected it to the battery terminals and got an error.  Then I re-checked the service manual, and there's a thermistor connected to the other two terminals.  Master Instruments make a replacement battery pack, and their catalogue says it's a 5K device.  So I strapped a 4K7 resistor across the thermistor terminals, and tried again.  Success!  For about one minute, then four beeps, meaning the battery is faulty.  What gives?


As a test, I replaced my battery pack with a 12V SLA battery and it charged perfectly, so it looks like the charger circuit is functional.  But why didn't my pack work?

I hooked it up again, this time with a multimeter to measure the charge current, and there was no current at all.  When I put my SLA battery on there, I got about one Amp, so my pack is obviously the problem.

Diving further into the manual, it describes a number of charging phases, including the sixteen-hour "recovery" mode which used 430mA to recover a long-discharged battery.  I looked up the specs for the cells I am using, and found that a constant 240mA should do the same time.

It took a while, but my pack eventually did start to charge, so one (or more) of my cells was really flat.  When I reconnected it, the charge process worked just fine.  Just for fun, I added a DS18B20, and fed the temperatures into Splunk (see below), and the temperature curve matched the service manual.

To make it compatible with the Scooba, I used a zip-tie to hold the thermistor against the centre-most cells, hoping this will give me a sufficiently accurate reading.  A decided to also retain the DS18B20 as "my" temperature sensor, and added a 10K/1K voltage divider so that a micro could be used to monitor the pack voltage.  This gives me visibility of the battery health outside of the reports I can get out of the serial interface on the robot.

For security, and short-circuit prevention, I wrapped the whole package in vinyl tape.  The finished result is shown below.  The single orange wire is my voltage divider output.

Because I don't have an original battery case, I still have to figure out the connections in a manner better than alligator clips.  More on that in the next installment.


OpenCV and CUDA - updated build notes

Yes, this comes up a lot, and if you're getting mysterious build breaks, maybe it's a mismatch between what version of gcc/g++ CUDA requires, your default compiler.

The symptoms are that cmake completes just fine (so it found a compiler, all the libraries, etc) but then you get a "failed to compile" error when you do a "make -j[number]"

The first thing to do is to run a single-instance make, because the errors will likely be less obtuse.  When I did this, I got:

"error: #error -- unsupported GNU version! gcc versions later than 8 are not supported!"

gcc --version told me it was version 9, so there's the problem.  But how to get around it?

  1. Make sure you have a suitable version of gcc and g++ installed (version 8) in our case.  On Ubuntu, you can do this with:
    "sudo apt install -y gcc-8 g++-8"
  2. Tell cmake about it when you do the pre-build with:
To wrap up, the cmake command line looks like this:

-D OPENCV_EXTRA_MODULES_PATH=../../opencv_contrib/modules \
-D HAVE_opencv_python3=ON \
-D PYTHON_EXECUTABLE=/usr/bin/python3 \

This command makes some assumptions I should point out:

  1. You have already installed and verified the pre-requisites.
  2. The opencv and opencv_contrib source trees have been fully expanded and start at the same level under your source.
  3. You created a build directory within the opencv source, and you're executing from within that directory.

Hope this helps you out!

Meerkat Evo

Evoluzione delle specie

Seems there's another Evolution 2020, so don't get confused

I published the plans and instructions for the original Meerkat around 2 years ago, and in that time Meerkat has done a lot of teaching work, not to mention being built quite a few times from my instructions.

But over the last while he's been sitting there not doing much because of what I thought was a dead motor, and because my day job needs full attention.  But not that long ago, I got inspired to give Meerkat a proper update.

In automotive parlance, this new version is an update of the existing model rather than an all-new model.  So I've decided to call the update "Meerkat Evoluzione 2020"

Back in the workshop again...
So what is the feature list for the reborn 'Kat?  How is this for a start?

  • Cliff detection.  Stairs are the enemy of most robots, so we'd better find them before we call off.
  • Power monitoring.  So Meerkat knows how much power is available.
  • Location recording, if not autonomous navigation.  This might be a big ask for a Mega256, but let's give it a try.
  • Radio telemetry.  Just like a real rover.

Running Repairs

But first things first, there is the matter of that dead motor.  I propped Meerkat up on a stand and ran it with no obstructions, so all motors will go forwards.  No matter what command was given, the motor didn't rotate.  Then I looked closely at the motor controller:

Did you spot the problem?  D4 is not lit up like all the others.  It's unlikely this is a bad LED, so either the L293 is broken, there's a bad connection or the GPIO line is dead.

Unfortunately, when I was trying to measure, I managed to short the 9V rail from the batteries to the 5V rail and did lots of nasty damage to the controller, so that was the end of the test process (and the controller.  There are some SMD diodes I don't have the tools to replace and they seem to be blown now.

Even worse is the fact that this particular board is no longer carried, leaving me to replace it.  I have a few boards on hand (motor drivers get damaged around me), but nothing that will drive four discrete motors.

It's not the end of the world, because I've learned that while being able to control the speed of each of Meerkat's wheels independently is nice, there is little, if any practical value in doing so.  From now on, we will be controlling the motors on each side from a single channel.  This simplifies the code, and reduces the amount of wiring I need as well.

I happened to have a Deek-Robot clone of the Arduino Motor Shield R3.  I am not sure if the genuine item is still manufactured, but it's a very simple L298N board, and many companies are producing them.
Changing to the shield controller did simplify the wiring, because now only power and motor wires are required, but it did mean changes were required to the software configuration.  The drive process is also slightly different, since this controller implements a "brake" function.  Fortunately because the code is written as a series of layers, only the physical control definitions needed to be changed (and then, not by much).  We also lost two analog ports (used for power monitoring) because the shield exposes the L298's output current sense functions.  Just move A0 and A1 to A2 and A3 respectively.  No biggie as the Mega2560 has plenty of analogue inputs anyway.

It did however, mess up the SPI functions for the SSD1306 display panel  The most obvious solution is to use an I2C display, so that's the path we will take.

Don't fall down the stairs

Meerkat usually runs around indoors and my house has a few flights of stairs which, for a robot is pretty life-threatening.  As it turns out, cliff detection is not too difficult to achieve.  I am using a simple Infrared reflection detector.  Usually these point straight ahead, but in this case, the board is angled 45 degrees down and mounted it at the front, underneath the sonar assembly on the bottom deck.  These boards have a sensitivity pot, which I manually set so that the LED turns off when Meerkat gets close to the edge of a stair.

A cliff event will cause an immediate stop and a turn randomly to left or right.  Why choose the direction at random?  Simply because it makes it more interesting to see how the internal logic deals with the situation.

Where am I, and where am I going?

Here's where it's going to get really interesting...  GPS receivers are really cheap and pretty easy to use.  Recently I published an article on a logging GPS speedometer, and it used so little memory I started to wonder if I could put simple heading control into the unit.

Just to be clear, this is not full navigation, rather it is a way to maintain a heading in an effort to reach a waypoint as defined in the code.  The overall goal in code is to reach a set of coordinates, within a reasonable margin of error.

Meerkat knows from a GPS reading where he is, and also where he needs to be.  The math is complicated, but given these two pairs of X-Y coordinates we can determine the heading that needs to be taken.

This link gives a great example

Meerkat will attempt to get on heading (within 2 degrees) and then move off.  If the GPS-reported heading deviates from the calculated heading, Meerkat attempts to steer back onto course.

In this mode, each minute Meerkat gets it position and recalculates the heading.

So what if we encounter an obstacle?  It's simple really, the existing steering logic kicks in and once we are back on the straight path, the heading correction will start again.  This software is very much a work in progress, so keep an eye on the repo for updates.

Doing the dirty work

Note:  In the photos you will see that I replaced my original Arduino Mega2560 with a WiFi-enabled model.  This is not part of the upgrade as described here and was done because on removing the original, I found it had a damaged electrolytic capacitor (C2) so rather than risk failure, I replaced the board with what I had on hand.  We are not using WiFi in this example anyway.

The process is easy enough.  Firstly disconnect the motor driver and motors including the servo.  Remove the motor PCB.  Unplug the ultrasonic module and display (if installed), then remove the Arduino board.  Leave the servo and ultrasonics in place, but disconnected

To avoid having to rewire the motors, I moved to Arduino board so that it is approximately 1cm from the back of the chassis.  This requires drilling new holes for mounting.  When this is done, re-install the Arduino.

Using a 3V battery, identify the motors on the right side (looking from the front) and connect these to the "A" outputs.  Likewise, the left side are connected to "B" outputs.


The servo does not plug straight into the motor shield, because the pinouts are not compatible.  The connections are:

 Servo Pin
Plug Position
Wire Colour
Shield Pin
Orange or Yellow
Brown or Black

Use three Male-to-Female jumper wires to make the connections.


Sonar is connected directly to the Arduino with TRIG on 52 and ECHO on 53.

At this point, load the new software release (from the meerkat-evo repository) and Meerkat should work just like it did before.  An anticlimax maybe, but it's reassuring to know nothing broke.

In Conclusion

As with the evolution race cars, I have put together  a kit of parts to upgrade your existing Meerkat to full Evoluzione 2020 specification.  The kit contains:
  • Compatible dual motor control shield (brand may vary, but they are tested for compatibility)
  • Optical sensor for cliff detection
  • ACS712 current sensor
  • Voltage divider
  • GPS receiver
  • NRF24L01 transceiver (long-range configuration)
The upgrade kit does not include the Arduino or LCD display.  Unlike the race car, you don't send me back the old parts.  Use them in another project.

P.S.  To the nice people at Ferrari, this is all in good humour.  I am not for one moment comparing Meerkat to your products.

Seriously, WHY would you do that? - Part 4

Episode 4 - "We have liftoff"

Last time, I got the RX2600 started up and failed to install OpenVMS because of a suspect hard drive.  From the screen shot above, you might observe that I have gotten OpenVMS installed and running on the machine.

There were a bunch of issues.  The hard drive supplied with the system was bad, and it was not the original device if the butchered screws on the caddy were anything to go by.  The seller did offer to replace it for me, but since I have a box of replacements, I didn't take him up on the offer.  Needless to say, if I want another RX, I know where I will look first.

The other curious issue is that the DVD drive is staggeringly slow.  It's not just OpenVMS, but also on RedHat.  Since it's not something you use much once the system is installed, HP didn't put much emphasis on it's performance.

I'm not going to take you through the whole installation process, that's what the OpenVMS manuals are for, but I will give you a few hints I found useful.

Boot the system with boot_ia64.efi which you will find in the "\EFI\VMS" directory of the DVD.  This should be "fs0:" in your EFI shell.

Wait a few minutes (no, really) and you get the installation menu.  Choose "Install" (the first option) and go make a pot of coffee.  You might have time to harvest and roast the beans too.

After an agonising wait you will get prompted whether to wipe or preserve the install disk.  Since mine was effectively a new disk, we want to choose "INITIALIZE"

You have to type INITIALIZE, so watch the spelling...

Now comes the first real test of your hardware.  You will be prompted for the disk to install onto.  I would suggest entering '?' first of all, and confirming your device list.  In my case it was "DKA0".  If you don't have a disk in an "online" state, there's a problem you need to solve first.

There is a choice of filesystems for the system disk.  Actually on Alpha or IA64 there's always a choice of filesystems when preparing a new volume.  ODS5 is the current standard, and unless you intend to run an old application, there's probably no reason to select the older ODS2 filesystem.

Next import question is whether to create or validate boot options.  This only appears on EFI (IA64) machines, and it's a really good idea to choose "Yes" for this one, because it creates an EFI boot manager entry:

Being lazy, not to mention inexperienced, I accepted the option to install default settings.

When it comes to entering the PAK details, at this stage you really only need to enter the values for OpenVMS.  You can paste the other commands in once the system is up and running.  It's a tedious process, but it has to be done.

After about an hour or so you will be greeted with the "installation is now complete" message.  Feel free to perform the victory celebration of your choice.

But your work isn't over.  The system needs to restart, and configuration needs to be done.  More on that next time!

Seriously, WHY would you do that? - Part 3

Episode 3 - "Are we there yet?"

Last time, we had the RX2600 plugged in and talking on the Management Processor (MP), and burned a copy of OpenVMS to a DVD.  Now let's get this thing running.  The green on black terminal mode is not required, but it does make things feel so much more serious.  I recommend it.

Of course, it's not as simple as it sounds!

My system had previously been configured with HP-UX as you can see from the Boot Manager menu.  With the DVD in the internal drive, I scrolled down (using the "v" key) and hit enter to boot it.  Wait a while (patience is a virtue...), you will get an OpenVMS startup message.  So far so good!

After a long time you get the installation menu for OpenVMS.  Wait a while again...

Alright!  Now, type INITIALIZE (Aussies, Kiwis, Brits etc, just tolerate the spelling) because this is an existing disk, and we're going to clobber it.

And this is why my problems began...

 DKB200 is online, and that matches the drive installed in slot 2 of the machine, so let's use it.

"medium is offline".  This is definitely not what I expected, and now I am stuck at this point.

Thinking this might be related to the old HP-UX  installation, I reset the EFI config which removed all the boot entries (including the DVD by the way!), and now if I try to boot from the OpenVMS DVD (using "fs0:\efi\boot\boot_ia64.efi") it sits there forever after choosing install.

Digging deeper I noticed a message when booting to the EFI shell.  While EFI detects the 73GB drive at SCSI 2 on the embedded controller, there was a message "failed to initialize", and I only had fs0: (The DVD) available.  Something is clearly not right here.

I had a couple of spare 73GB drives (but no caddies), so I slotted on in.  No more errors on the EFI shell, but still no luck with the boot.

So here's my theory at present:  The EFI config told OpenVMS there was a drive at DKB200, but the drive was knackered.  What I should have done was swap the drive out first, not wipe the config.  OpenVMS is also very tolerant of errors.  It just seems to keep on trying to find a suitable disk to install onto.  Presumably an installer might need to zone a Fibre Channel connection, or attach external storage.  By the pattern on the DVD LED it just seems to loop, but I could be wrong.

Also, I noticed that installation menu reports two U320 controllers (which seem to be in the PCI cage), but not the embedded controller, so now I don't know if I accidentally killed the EFI driver as well.

Next Steps

I'm going to download and re-init the SCSI drivers at the EFI level and see if this helps, also try installing another OS just as a sanity check.

More details as they come to hand

Seriously, WHY would you do that? - Part 2

It's here!

Last time, I wrote about my reasons for choosing the HP RX2600 to run OpenVMS.

Having gotten all the manuals, firmware and everything else, I set up the machine according to the instructions from HP, and nothing much happened.  Nothing on the monitor, even though all the docs said it should mirror the console.

Next step, connect a cross-over cable to the Management Processor (MP) serial port, and try again.  This time I get greeted by a login prompt.  The user needs to be "Admin", but I don't know the password...

No biggie, with the power on, hold in the reset button on the MP card and then you will be prompted to enter "p" to reset the accounts.  The manual says Admin (note the capital letter) should have no password, but the prompt on the console said it's "Admin", so that worked.

The Help command is your friend here.  First thing I did was to use the Command Menu (CM) then LC to configure the networking for the Management Processor.  Now my MP is on my network.  One small victory.

Here's a bit of fun, under the Command Menu, enter SO and reset the certificate.  You can find out who used to own your machine.

In my license application, I was given a login to download the binaries to install OpenVMS.  Burning the ISO to a DVD is the first step, but MacOS outsmarted itself and won't burn, because it can't read the image.  Windows didn't care.

I would also suggest you grab the OpenVMS installation guide from HPE.  There's a ton of steps involved, and it's easy to miss things which will bite you later.

Next Time: Installing OpenVMS on IA64

Wasting your and my time

I had a really interesting experience recently which I hope might enlighten others as much as it did me: I was approached (via LinkedIn) by ...