Description

When I had my first encounter with ultrasonic distance sensors - specifically the Parallax Ping))) - while helping the Cresta Blanca Girl Scouts robotics club prep for the local RoboGames Fire Fighting challenge the first thing I observed was that you really want more than three of these on your bot. Right, left and forward just isn't enough spatial awareness when you are navigating hallways and doors. Two on each of the four sides - eight total - would work much better. I also found that they use a pin each. And they cost $30. Each.
HC-SR04 sensors are much cheaper. These look similar and perform a similar function, but these guys use two pins each - one for trigger, the other for echo, and the Arduino needs to manage all the timing. This project handles all that, using I2C and one hardware interrupt pin.

Details

There are not enough pins on a basic Arduino to provide 2 pins each to 8 HC-SR04 units.

I2C bus would be the answer, but the bus itself is not fast enough for accurate echo measurement, and the only reference to an I2C library for these in the playground seemed to be broken. Time to write some code…

Wire the Trigger pins to PCF8574A expander pins. Also wire them to the control pins of 74HC125 tri-state buffers.

Wire the Echo pins to the inputs of the tri-state buffers.

Wire the outputs of the tri-state buffers to a hardware interrupt pin on the Arduino

Software

In idle state, the sensor trigger pin is held high. This is contrary to common practice, but works fine as the sensor is triggered by the falling edge, not by the suggested 10us pulse. In this high state the tri-state buffer output is at high impedance (off).

Pick a sensor to trigger

Via the I2C expander, drop the trigger pin. This starts the sensor's ping cycle and turns on the tri-state buffer so the echo state is now visible at the interrupt pi.

attach an interrupt to the pin to watch for the beginning of the echo pulse

the sensor does its thing and starts the echo pulse

the interrupt routine fires, notes the time in micros(), and attaches a new interrupt to watch for the end of the pulse

the sensor does its thing and ends the echo pulse

the interrupt routine fires, calculates the echo pulse duration in µs, saves it and then clears the interrupt.

at the end of the 50ms window, whether an echo is received or not, raise the trigger pin, disabling the tri-state buffer. Record an out-of-range value if no echo completion occurred.

The board is a class instance. Sensor data is kept in an array which is cycled through in 50ms steps

The user read functions default to mm, but you can use other units.

Call doSonar() every loop(). If it is time to poll the next sensor it will do that.

IMPORTANT: this will work best if your program is written as a "finite state machine". Google it if you don't know what that is. Essentially you want your loop() to never delay(). If you need to wait for something to happen or be a certain way for a period of time, save the end time in a variable and check each loop() cycle to see if it is time to end that state.

The boards now on Tindie!

NOTE: The OctoSonar library is for board rev 2.0+. Earlier boards used OR logic on the echo and the SonarI2C library. While the board has been proven electrically compatible with the 3.3v raspberry pi (without level shifters), the code out there to support it wasn't written by me but by someone else for the 1.0 boards. There is a tweaked version in my repo to update it for the 2.0 boards, but it's still not my code, and it definitely doesn't work for the X2 16 sensor board i.e. I am not supporting code on RPi at this time but you are welcome to write your own.

Move the "SonarI2C" folder into the "libraries" directory inside your Arduino sketchbook directory. You can view your sketchbook location by opening the "File" menu and selecting "Preferences" in the Arduino IDE. If there is not already a "libraries" folder in that location, you should make the folder yourself.

After installing the library, restart the Arduino IDE.

(note - the above instructions adapted from a Pololu readme)

HC-SR04 I2C Octopus "octosonar"

There was enough interest in these that I made up a small batch to sell on Tindie.

To test these without the headers attached, I made up a test-bed on an Arduino proto-shield. This tests all the power pins (the green LEDs), checks the outputs from the PCF8574A and feeds signals into the CD4078 and verifies the output.

There is a minor error on these boards in that the silkscreen "Name" layers are missing so it does not show what port is which. Refer to the image below.

IMPORTANT: There are no pull-down resistors on the Echo inputs. Jumper any unused ports from E to G or you will get very erratic results.

There was enough interest in these that I made up a small batch to sell on Tindie.

To test these without the headers attached, I made up a test-bed on an Arduino proto-shield. This tests all the power pins (the green LEDs), checks the outputs from the PCF8574A and feeds signals into the CD4078 and verifies the output.

There is a minor error on these boards in that the silkscreen "Name" layers are missing so it does not show what port is which. Refer to the image below.

IMPORTANT: There are no pull-down resistors on the Echo inputs. Jumper any unused ports from E to G or you will get very erratic results.

The v2 board is faster with normal HC-SR04, cutting off the 200ms no-echo units at 50ms, and doing the same with the bad units that never come back, but that doesn't address the blind spots at the corner of the robot or the hassle of handcrafting mounting brackets.

The code does support multiple boards but that's klunky.

I just fired off prototype designs to OSHPark for a 16 port board based on the PCF8575 and a little board to mount 3 HC-SR04 on each corner - with two at right angles on one side and a third at 45 degrees on the other. The idea is to mount it on standoffs.

The extra V and G pins on the connector blocks should allow the three sensors to be run from 2 4-pin cables by inserting them at right angles.

I made up a test jig and an arduino sketch to wiggle all the pins and watch them from another PCF8574.

I did run into some timing issues reading from the jig, but those went away when I turned off the Serial.println commentary. I'm thinking there is some weird interaction between the Wire and Serial libraries.

Build Instructions

The HC-SR04 ultrasonic sensor principles of operation are well documented on the internet. A good example is at ElecronicWings

Below is their diagram of how it works.

The Octosonar takes advantage of something that is not clear from this diagram. The 10us trigger pulse length is a minimum value. The trigger is actually on the falling edge (high to low transition). The sensor does not care if you keep TRIG high for much, much longer than 10us.

The Octosonar keeps the TRIG pin high when the sensor is not in use, and low when it is in use - by default 50ms "low" and 350ms "high" (750ms "high" on the OctosonarX2). It gives each sensor 50ms "low" to take its measurement and also uses that "low" signal to enable a dedicated tri-state buffer to pass the ECHO pin signal to a hardware interrupt pin on the Arduino.

The TRIG pins are driven by a pin-expander chip on the I2C bus, with each active pin being made "low" in rotation. While I2C does not give good enough timing accuracy for echo measurement it works fine for this selection rotation function. Timing is taken by hardware interrupt routines, so the start and end of the ECHO pulse can be measured accurately, even if your Arduino code was doing something else at the time.

The results are stored away for your code to retrieve later when it looks for it.

2

Software Installation

If you are using version 1.6.2 or later of the Arduino software (IDE), you can use the Library Manager to install this library:

In the Arduino IDE, open the "Sketch" menu, select "Include Library", then "Manage Libraries...".

Move the "Octosonar" folder into the "libraries" directory inside your Arduino sketchbook directory. You can view your sketchbook location by opening the "File" menu and selecting "Preferences" in the Arduino IDE. If there is not already a "libraries" folder in that location, you should make the folder yourself.

After installing the library, restart the Arduino IDE.

(note - the above instructions adapted from a Pololu readme)

3

Wiring Up the Octosonar

The connections on the Octosonar are clearly labelled. I'll try and run up a wiring diagram later, but for now the following list of connections should get you there.

To connect to Arduino

Octosonar Pin

Uno Pin Default

Description

5V

5V

5V supply for the HC-SR04 sensors. If you have a 5V Arduino (e.g. Uno) you can skip this connection and bridge the V1 solder jumper on the back to use the VCC suply for this purpose.

INT

D2

The hardware interrupt pin you select in your code.

SCL

A5

The I2C SCL pin or your controller.

SDA

A4

The I2C SDA pin or your controller.

VCC

5V

The voltage power used by your controller. This will be either 5V (Arduino Uno) or 3.3V (Raspberry Pi, Arduino Pro 328 - 3.3V etc). IMPORTANT: if you have a 3.3V controller, DO NOT SOLDER V1

GND

GND

Ground

Solder Jumpers on the back A0, A1, A2 allow you to change the I2C address from the default 0x38

The V1 solder jumper is used to connect the VCC and 5V paths together - which saves you a wire if VCC and 5V are the same thing on your controller.

To connect to HC-SR04, the eight connectors are marked VTEG matching the pins on the HC-SR04. If you are not using all the ports you can disable the inactive ports in software which will give more frequent polling of the active sensors. The ports are numbered S0 thru S7 (S was for "sensor") and are spaced to allow molex locking headers to be used.

The code I support is written for Arduino using the C/C++ code that comes with the Arduino IDE.

The device is compatible (depending on V1 solder jumper) with 3.3V or 5V logic, however a 5V supply is needed for the sensors.

In theory therefore any controller running on 3.3V and 5V will work, if the software is written for it.

The code is open source, feel free to translate it to any language or platform you like, or write your own from scratch.

Or run a slave arduino (or attiny) to drive the device using my code, and have your node.js system talk to it. There was a similar product on Tindie with the microcontroller built in. Seems to be gone now.

They are superior to HC-SR04 (or other trigger/echo sensors) because they do the time of flight calculations on board (plus have temperature compensation, which is a smaller increase in accuracy). eg. for a 697mm measurement I found trigger/echo measurements would overestimate by 25mm and would vary by 6mm. US-100 gave a measurement that exactly matched my tape measure and returned the same value every time (to mm accuracy).

Using this might make it easier for you to move your robot parallel to a wall using only a single device with a tuned PID - the variability in the results you get from HC-SR04 would likely make that impossible.

They *don't* seem to do time of flight calculations any different from the HC-SR04, (the controller has to measure the returned pulse length in the same manner) so can you verify where you saw that?

I would think they could be drop-in replacements for the HC-SR04.

The tuned-pid one-sensor approach only works if your robot is travelling forwards so it can tell if it is getting closer as it goes. I want the ability to line up to the wall while not rolling forwards. To do that I need two sensors on each side, spaced wide apart. For this it's more important that they give the same value as each other for the same distance than they give a number that matches a ruler.

Oh, that is interesting. I think my specific hardware solution is only going to work with the trig-echo mode, which matches the SR04. Unless it were possible to send the 9600 baud request messages by flapping the pin on the expander to fake 9600 baud. That's complicated and a lot of work for the Arduino which probably has other things to think about, but a fascinating idea.

HC-SR04 and US101 do the same thing - send pulse, wait for echo, calculate distance based on how long the echo takes to come back. The difference is in what calculates the distance (sensor vs. microcontroller) and how the communication works (timing magic vs. TTL serial).

With the HC-SR04 your MC sends a pulse (with some timing magic) then wait for the echo pin to go high, and from the elapsed time the MC calculates the distance. With the US-100 your MC sends a TTL serial message to trigger the measurement, and then gets a TTL serial response with the distance.

The important bit here is where the elapsed time measurement is being done. With US-100 it is done on the sensor itself, so is as accurate as we can get (plus we get temperature compensation too, which affects accuracy). With HC-SR04 and similar you rely on the MC to measure the difference - and this potential latency in the MC noticing the echo pulse can cause both inaccuracy (value calculated is greater than it actually is) and imprecision (measuring a fixed distance doesn't give a fixed result, due to the unpredictable latency).

US-100 are cheap enough that, for me, their improved accuracy and precision is well worth it. Currently US-100 cost £2.22/sensor vs. £0.99 for HC-SR04, so the difference in absolute terms is small.

I'm not sure I understand how multiple sensors help you line up to a wall better than a single (accurate and precise) sensor. Two sensors are needed to let you know if you are parallel certainly, but if you aren't parallel and want to get there then you need to move - at which point either one or two sensor setups should do the job. I'd bet improving your accuracy/precision would help a lot with that, hint hint :)

Anyway, your design certainly looks good, just suggesting some alternatives. I imagine you could do it with a handful of 555 ICs but I haven't touched them for two decades, so I'm not the one to tell you how to do that.

my initial google found a reference to the trig-echo mode on the SR-100. It was later I spotted the TTL/Serial mode. I agree that doing the time calculations in the module is preferred, as it saves the controller doing it and avoids timing conflicts on the controller. I think the Ping))) does things on-board too, but they are way expensive. So I do like that, but how would one address the issue of talking to lots of them? Those 74HC4051 analog MUX chips mentioned by the other guys would work, I think.

Take a look at my sonari2c library. I think I'm taking a different approach from the other libs out there in that I am using short hardware interrupt routines to record the timestamps of the start and end points of the echo pulse in microseconds. Parked next to a wall I get *very* stable results. As long as you are careful with your code, (e.g. don't write any other interrupt routines that run long) you should be able to maintain this accuracy.

My thinking is that having sensors at the end of each side of the bot will help in multiple ways while negotiating tight spaces.

With that, knowing when my nose is next to an opening and my rear isn't will be useful, as well as other things like my front left is up against a wall but my front right isn't etc. Previous attempt had one front sensor and one on each side, and there really wasn't enough data to navigate intelligently.

Can't use the 74HC138? Hmm. Is the module not edge-triggered? Can't you just pull the trigger line low then back high to initiate a ping, while the inactive trigger lines all stay high? or does that make them ping continuously or something equally bad?

But you're right, I always forget the active low outputs of the '138 :-)

I had to look this up as I confess I'm not familiar with those. Looked up the datasheet and that would use 4 more GPIO pins on my robot build. 3 for addressing and one to send the pulse. I'm not counting the I2C pins as I'm already using them to talk to the display and to pass packet data instructions to the embedded atmega328 in the motor driver card. I'm a self confessed pin-miser with an I2C fetish and I don't know yet what I might want to add to those pins. The easiest way to put an LCD on an and Arduino based bot these days is a 1602 with a pre-installed I2C backpack, so they are very often already active in I2C use.

Using those chips would save a little cash - maybe 60c on parts per unit, and they do come in a narrower package than the PCF8574. Also running the echoes through one would protect from any rogue inputs from the sensors or floating inputs on open ports, though that isn't a problem I've seen yet (open ports on the 4078 need to be grounded.)

The trigger for the HC-SR04 is the falling edge of the signal on the trigger line, and the echo pulse is positive, with a duration equal to the round trip of the ultrasound. The trigger signal is not time critical, so I2C is no disadvantage. For accurate timing the echo signal somehow needs to come into a pin with an interrupt set on the rising and falling edges.

Another aspect of my design that I like is that I can daisy-chain another of these on the same interrupt pin and add another 7 units. I'm planning to add 4 - one on each corner where the robot has blind spots. Currently if it is at 45 degrees to a wall it can't see it.

Thanks for this comment, now I have a whole new breed of chips to learn about!

>Philips Corp. today announced it has filed patent infringement suits against eight more companies accused of violating its technology rights in I2C bus interconnects on semiconductors. The latest round of suits follows similar cases filed against six U.S. chip makers one year ago.

The lawsuit things seems to be against semiconductor manufacturers, and most of the chips I am using are genuine Philips (NXP) chips (though I also used a few Texas Instruments) chips. This is tiny scale hobby stuff. And that article was from 2001.... Not worried.

The robot is a work in progress with me learning as I go. It is a step up from the predecessor which had only 3 sensors, one at the front and one on each side near the front. I found with that to follow along a wall you had to move to figure out your angle i.e. "I am getting closer, I need to turn away". This is not very helpful if there is a wall up ahead. So I figured if I had two on each side mounted straight out, I could know not only how far, but at what angle a wall is - without moving. I'd also get an idea of whether my butt is clear of the opening before turning. This works as intended, but showed up the next challenge - blind spots on the corners.

At a 45 degree spacing you would pretty much be blind-spot free but none of them would be able to see the same wall at the same time, and I'd lose the parallax that allows me to statically adjust relative to flat surfaces.

However, the next step in the plan is to add 4 more sensors at 45 degrees at the corners to cover the blind spots, pretty much like you suggest. I'll just watch for these to have a range lower than the sensors on either side. When that happens I'll rotate out of the blind spot.

The Octosonar echoes can be daisy-chained (they are OR logic gates) so with two of them I can still run 15 sensors on 1 interrupt pin. I breadboarded this out early on. The challenge is mounting brackets. The more sensors you ping in rotation the longer it takes : 50ms each - 12 would take 0.6s - a long time for a moving robot. That's why the library lets you turn off the ones you care less about, like the ones at the back when moving forwards.

it wouldn't hurt to try :) seems like you still know you're angled to a wall when the side gets closer/ shorter ranges, so it would be possible to counter act that angle like a line follower does. But I'm just assuming here..

I want to know the angle to the wall without moving. The firefighter maze is a very confined space. Need the parallax of two sensors. That's why we have two eyes pointing forwards. I think 12 will be enough :-)

I just saw the video of the robot and have one question: why don't you angle the 8 sonar modules on your robot in 45 degrees? Seems like the arrangement of them isn't optimal to show the full capabilities of this project.