Introduction: Ultrasonic Measuring Tape

Picture of Ultrasonic Measuring Tape

This time I will try something new. To make sure I don't forget about important steps of the build process, I will write the instructable while I am soldering, making measurements, locating software bugs. Hopefully this will allow everyone to follow what I have to say, and why I am saying it. Let's start with the stuff I put on the table now, at the very beginning.

What you're going to need
• 1 pcs. - plastic enclosure, reuse an old multi-meter casing, or buy one like mine
• 1 pcs. - arduino, with all of its pins used, but with an active I²C bus - I borrowed an Arduino UNO
• 1 pcs. - ultrasonic ranging module - mine is HC-SR04
• 2 pcs. - PIC with MSSP module - I will use PIC12LF1840T39A
• 1 pcs. - common cathode display driver - as I already used this in other projects, I chose MAX7219
• 1 pcs. - common cathode display - Just pick one randomly, the one used by me is similar to this one
• 2 pcs. - right angled tactile switches - I de-soldered a few like these from an old CD-ROM
• 6 pcs. - 4.7 K resistors for pulling up the I²C lines, tactile switch inputs, and display driver current set, like these
• 1 pcs. - PC with MPLAB and Altium on it - I own a dell inspiron N5110 laptop
• 1 pcs. - Laser printer, I have a Brother HL2132
• 1 pcs. - Copper board that's large enough to fit your design
• 1 pcs. - 5 V low-dropout regulator, I used LM7805 in TO-252 package
• X pcs. - FeCl etchant, laser printer, iron, solder, soldering iron, drill, pliers, screwdrivers, screws

It means a great help to have
• A caliper - I have the old-school version without digital display
• A logic analyzer - mine is Scanalogic 2
• Steady hands, ambition - I think I got both :)
• A cup of tea - I drink English Breakfast

Extra components needed because of the 3.3 V - 5 V compatibility issue you will read about later
• 1 pcs. - bipolar transistor, I used BCR562 which has in-built bias resistors
• 1 pcs. - 10 K resistor to pull up the TRIG line
• 1 pcs. - 3.3 V zener diode
• 1 pcs. - 1 K current limiting resistor for the zener
• 1 pcs. - 3.3 V low-dropout regulator, like this

Step 1: Why Would One Build Such a Device?

The whole idea of building such a thing came to my colleagues while they were working on a balancing robot. They thought it would be wise if the robot knew of eventual obstacles in its way, and the distance between the obstacle and the robot chassis. A widely spread method of distance measurement is the ultrasonic method. Interfacing such units (or anything else) to an Arduino is a childs play, however, if one is left without IO pins, there's some thinking that needs to be done.
We already had a motor controller, an LCD, a joystick and a Bluetooth transmitter hooked to the Arduino, but needed two more for opto-sensor feedback from the wheels. That left us with no pins for the ultrasonic ranging module. I think "no pins left" is the recurring sentence in every Arduino programmers nightmare, which - in most of the cases - leads them to surrender a functionality for the sake of another, or to abandon the project.
I was asked to design I²C interface for the HC-SR04, allowing the Arduino to ask for distance measurements via this two wire interface.

First thing I am going to show you is how to make an I²C sensor out of the HC-SR04. Once I'm done with that, I will go on with putting it into a custom project of mine: an ultrasonic measuring tape. My dad is an electrician, he needs precise measurements while working - my little project might be helping him with that.
There's probably going to be a lot of interesting ideas of where this sensor could be put, if you have a good one, share it!

Step 2: Designing a Board in Altium Designer

Picture of Designing a Board in Altium Designer
To be able to build the sensor board, you're going to need a breadboard, perforated board, or a PCB. Since I am using a surface-mount components to do the processing, I will make a custom PCB for it. But how to make a custom PCB? I have to make some PCB-s anyway, I'm going to describe my general PCB designing and making procedures in this step, then I will move on to our board in particular - in the next step.

How to draw the board shape into Altium Designer
When you create a PCB file in Altium Designer, it opens with a random PCB size, which - I am sure - is bigger than we need it to be. To size a board precisely, I will use lines ('P' then 'L') after I selected the keep-out layer tab down at the bottom of the Altium window. I draw the first two, then double click each of them, and give them the values I just measured. Then I draw two lines again, this time there's no need to re-size them, you just have to start at the end of the two lines you already have. You can double check the lengths by pressing 'Ctrl' + 'M', and clicking on the edges.
Press 'Ctrl' + 'A' to select everything, then go to 'Design' -> 'Board shape' -> 'Define from selected objects'. This will snap the board limits to the lines you just defined on the keep-out layer. Use 'ALT + D' -> 'S' -> 'D' to do this quickly.
I measured the distance of the hole from the edges, and placed them on the PCB as multi-layer pads.
I shot a short movie clip that presents the definition of a board shape, nothing special, just a basic 80 x 25 mm board.

Editing libraries to have custom components in them
In case anyone decides to use a component that isn't in his schematic or/nor footprint libraries, I made a short instructional screen snip, in which I add a BTM-112 module to my libraries. You can see how the schematic symbol and land pattern is built up based on the datasheet specs. I usually group the pins that belong together (power rails, ports, USB, UART, ... ), when making the schematic symbol. It simplifies my schematic later on.
I suggest to use a snap grid, and pin lengths (schematic symbol) of 2.54 mm, to get all the symbol edges and pin-ends on grid.

Creating schematics
I can't speak of this too closely, each schematic has its unique characteristics, components, buses, problems. Simple enough, placing a symbol is done by pressing 'P' -> 'P', which stands for Place Part. Then you have to dig down to your libraries to find the component you wish to add to the schematic file. Add them all, then press 'P' -> 'W' to Place Wires. To send the changes over to the PCB file, go to 'Design' -> 'Update PCB'.

Routing the wires and finishing the design phase
This is another thing you need to figure out yourself, the main key combo in PCB mode is 'P' -> 'T', this highlights the nets, making them easier to see. In addition, they move the un-routed line along, as you proceed with placing track segments. Do this for all your nets, then run an eventual design rule check, to look for design mistakes. Going into the design rules is way beyond the scope of my instructable, so I'll leave that out. If you have little or no knowledge about them, just use the DRC for checking against un-routed nets.
Create an Output Job file, and proceed with the settings explained here, to get a PDF file with your layout on it!

Cleaning a PCB and printing the artwork
Check out these photos I took before making some PCB-s for this, and some other projects:
DSC_0010 - Darrell Abbott - Flickr
These are two random boards I decided to etch for the sake of making an example. Both are break-out boards, one is for a school project (the SD card shaped), the other is the bluetooth module we just made a footprint + schematic symbol for. Through this instructable I already won myself a bluetooth module. I built it into a ball balancing robot, that will get its own instructable guide as soon as it starts up! Back to our stuff:
DSC_0013 - Darrell Abbott - Flickr
The two boards after the ironing was done, and the paper was rubbed off. Double check your traces, if there are scratches on the toner layer, there are pads missing - correcting can be done with etchant resistant marker.
Next step is to put this into FeCl, to take off the excess copper. Then the toner needs to be washed off, the board has to be cut to size. Check these images to see my "end products".
DSC_0018 - Darrell Abbott - Flickr
An SD card shaped PCB, meant to replace the IMP from my motor controller board. There is a pin header on the bottom layer, I am using jumper cables to connect it with the rest of my board. This could have been avoided, since I did have I²C brought out on pins on the motor control board itself, but I wanted to take advantage of the on-board 3.3V/1A buck. Through this break-out board I can reach the 3.3V power rail, I am using it to power the rest of the peripherals: bluetooth module, gyro, pull-up resistors.
DSC_0016 - Darrell Abbott - Flickr
The BTM-112 soldered on-board. You can see how much solder got on the left side? It is because I used solder wider in diameter, and wasn't able to ration it well. The right side was soldered with solder of 0.3 mm in diameter, that side looks way better, not to mention the solder joints, which sit better on the pads.
DSC_0015 - Darrell Abbott - Flickr
Group photo of the two boards.

So, now we know how to create something for nothing in Altium Designer. Let's prove it by designing and manufacturing the sensor board!

Step 3: Sensor Board Manufacturing Process

Picture of Sensor Board Manufacturing Process
Board size
I had free hands when it came to sizing, so I decided to design a PCB that matches the dimensions of the HC-SR04 board perfectly. Two quick measurements with the caliper, and you have your board size.

Drawing the schematics
Once we're done with the definition of this board size constraint, a schematic must me drawn. Mine looks as simple as it can get, just the PIC, the headers and a pull-up resistor on the MCLR pin. You might experience problems with programming, when this resistor is in place, if you do, just take it off and disable MCLR function on the reset pin.
You'll have to install pull-ups too, if this is the only I²C device you use. In my case, I already had the gyroscope on the bus, which has on-board pull-up resistors - I didn't had to worry about that.
If schematics are all done, go to 'Design' -> 'Update PCB document ....' to make the components visible in the PCB file. Arrange them as you wish, I just threw them on, and aligned them to the lines I drew on the keep-out layer, so they get centered perfectly. You can do this by selecting both, and pressing the 'A' key. A menu appears, which allows you to align horizontally or vertically the selected components. Click the operation you want, then click the component you wish to hold as a reference - in my case: the line on the keep-out layer. If everything's arranged, just route your signals, put on polygon pours.
Altium Designer offers a great tool for visualizing the board while you're working on it. 3-D view is activated by pressing '3', one can switch back to 2-D view by pressing '2'. My board looks like this in the 3-D Altium rendering:
I2C SR04 sensor board - Darrell Abbott - Flickr

Exporting to PDF
If you wish, you can export your layout to PDF. Don't forget to do a mirroring on top layer, if you use toner transfer method! I already wrote a walk-through about this in one of my previous instructables.
Here's what I got when I exported my layout into PDF:
I2C SR04 sensor board layout - Darrell Abbott - Flickr
The text is mirrored, because all this goes onto the top layer.

I used toner transfer method to make this board,(like most of my boards in the past few months) it's way quicker than the UV exposure method. The UV method is something I only use when it's absolutely necessary, when the traces are very thin or the pads are super small.
Long story short - get the toner to stick to the PCB copper side, then wait until it cools off. Go to the sink, put the paper+board under water, wait until the paper gets soaked, then gently remove the paper. There's going to be some paper left on the board, you can rub those off with your finger. Put your board in ferric chloride, and wait until the copper is eaten away, leaving the board with the tracks, pads and the keep-out lines only. Then take some nail polish solvent, and wash off the toner. You will have the finished PCB in your hands, which only needs drilling, and cutting, eventually. Here's what mine looks like.
101_0228 - Darrell Abbott - Flickr

Cut the board to size, then drill the holes. I drill 0.7-0.8 holes first, then go over those which need to be wider (pin headers, in this case). Give the PCB another sanding, then clean it of with water, or solvent. What is a good idea at this point, is to take off the right-angle pin header that's mounted on the HC-SR04 by default, and replace it with a simple vertical one. This way the board you made will get right behind, and in line with the HC-SR04 ultrasonic ranging board.
Populate your board with the components: headers, PIC, HC-SR04. You will have a sensor board that is ready to be programmed! Here's what mine looks like now, that I'm finished - from the top:
DSC_0156 - Darrell Abbott - Flickr

the bottom side of my sensor looks like this:
101_0232 - Darrell Abbott - Flickr

We have the hardware ready, next step is to clarify the things we will have to do on software side. This leads us to the biggest (not too big, though) problem of ours: enabling the PIC to talk through I²C.

Step 4: A Few Words About I²C Bus

Picture of A Few Words About I²C Bus
I know there's a lot of information about the I²C protocol out there on the web, still I will give you a short description, to make the instructable more complete.

Brief description of the bus
The I²C bus is an 8 or 10 bit, bidirectional serial communication protocol, having transfer rates of 100 KHz, 400 KHz, or recently 1 MHz (Fast mode plus). The bus consists of two bidirectional signals (usually named SCL - serial clock, SDA - serial data). These signals connect to every I²C device in our circuit we wish to communicate with.
Thhe SDA line is used for serial data transfer, the SCL line assures that the communication is in sync, in both directions. Besides these two, we also need the I²C master and the I²C slaves to share a common ground.

The pins of the devices connected to an I²C bus are open-collector, or open-drain. This means, that they can bring the floating communication line to the ground, and they can release it to rise to the level to which they're pulled up.
It is very important to understand, that the term 'release' doesn't mean a '1', it only means, that the master or slave isn't driving the communication line. Having said that, we need to make sure, that the line isn't getting low, when it isn't supposed to. This can be done by installing a pull-up to the power rail of the circuit. We don't have to (or must not) put a pull-up to every slave in the circuit, that would mean a parallel connection, which - if too many resistors were installed - wouldn't allow the pin to bring the line down.
The value of the resistor used doesn't need to be a given value, usually a few k resistor will work fine.

The I²C protocol is master-slave based. A master decides when the slave devices communicate, through some data sent on the I²C bus. In our case, the PIC needs to be configured as a slave, since the Arduino will be the Master in the circuit. A slave is usually a sensor, a memory chip, or anything that is able to extend the features of a microcontroller.
All transmission are triggered by the master, the slaves don't communicate anything, unless the master is asking them to do so. Correct I²C bus operation assumes, that a single slave address is recognized only by a single slave. When a slave is selected, all other slaves must remain silent.
The master is responsible of all kind of timings - in most systems (single-master) the clock line is manipulated only by the master. This means, the speed of the transmission is dictated by the I²C master of the circuit.

• Whenever a master wishes to start a transmission, it executes a start sequence on the I²C bus. A start condition occurs, when the SDA line is brought to low level while the SCL line is high.
• When the master wants to signal, that the transmission is over, it sends a stop condition, which is a rising edge on SDA while the SCL is high.
• Data gets transmitted in the time-frame between these two events. For a data bit to be valid, the SDA must not change value, while the SCL is high. If it does, the bit sequence is misinterpreted as a stop or start condition, and leads to communication failure.

In my case, data transmission is done in 8 bit groups. The data - following the big endian pattern - is clocked out with the most significant bit going first. After every 8-th bit gets clocked out, the slave must signal back for the transfer to be considered a successful data exchange. This signal is called an 'acknowledge', and it assumes, that the slave brings the SDA line down at the 9-th clock signal on the SCL line.

As I mentioned before, each slave must have a unique slave address. This slave address is 7 bits long, meaning, that a total of 128 devices can be attached to the I²C bus, without any kind of tampering with the circuit.
When it comes to addressing, the master sends out 8 bits, 7 of which are the slave address bits, the last one determining whether the master wishes to read or write in the slave devices internal registers. This last bit is known as the R/W bit, a '0' on this bitfield means a write, a '1' on this bitfield means a read.
For example, if one wishes to write to a slave device with a slave address of 0b1010000, he must send 0b1010000 after the first start condition. If a master wishes to read from the same device, the first byte after the start condition must be 0b10100001. These, turned into hexadecimal give 0xA0 and 0xA1. Datasheets normally give the slave address in hexadecimal format.

In some of the cases, the manufacturer allows the user to add more of the same chip to the I²C bus: this is achieved by allowing the user to control a number of slave address bits. For example, an I²C EEPROM usually has the lower 3 bits of its slave address brought out on pins. Pulling these high means a '1' at the corresponding in the slave address byte, pulling them to ground means a '0' at that location.

There are situations, where the master is just too fast for the slave, it wants to read faster, than the slave could handle. To avoid these kind of situations, the I²C slave device manufacturers can build in a feature, that allows the slave to pull the SCL line low, while they're busy executing the commands of the master. When the slave is done with measuring, reading, sensing, etc. - it releases the SCL line, allowing the pull-ups to take the line high. The master will not initiate another transfer, unless it sees the SCL line high. This is often referred to as 'clock stretching'.

Example of a START condition followed by a read command from slave 0xA3. The read operation is valid, we now this, from the ACK (acknowledge) signal sent by the slave.
I2cTransmitAndAck - Darrell Abbott - Flickr

I zoomed in a bit on the end of the same data transfer to capture two more signals, the STOP and the NACK. This bit pattern occurs at the end of a read sequence.
I2cNackStop - Darrell Abbott - Flickr

Unfortunately I wasn't able to capture clock stretching phases, but if I see one while working with the PIC I²C, I will make sure to show it to the readers.

So, we just learned this and that about the serial communications protocol we will use, now let's see it in action. First let me present you an example board, a PC tool and a microcontroller program.

Step 5: Eight EEPROM Chips on a Single I²C Bus? How?

Picture of Eight EEPROM Chips on a Single I²C Bus? How?
How to use eight EEPROM-s on a single I²C bus
I have a couple of 24LC128t EEPROM circuits in my drawers, I will put aside the main task a bit, and build up a test board, so I can show you some examples, waveform snips, behaviors. The processor which will read and write them will be an electric IMP, tucked in an April dev-board.
The datasheet of the EEPROM circuits I will be using can be found here.

As you can see on the pin diagrams, it has 3 of its slave addres bits brought out on pins 1, 2 and 3. If we connect A0 and A1 to ground, A2 to 5V, and look at Figure 5-1 of the dataheet, you can see how the first four bits are hard coded to 0101, the rest are controlled by the A0, A1, and A2 pins. If I connect the pins the way I said before, my slave address will be b1010100. The 8-th bit is the R/W bit.
Having three freely modifiable bits in the slave address allows you to connect 8 EEPROMS to the same bus without any conflicts. That means 8 times 16 = 128 kilobytes storage space. You can store variables, configuration words, images, anything you want. Our memory chip addresses will range from 0xA0 to 0xA7.
Interfacing other components is an easy stuff to do, once you have your I²C routines all worked out, working well. The datasheet always lists the registers that can be written or read. The same way you read from an EEPROM, you can read data from an I²C accelerometer, current measurement circuit and so on.

Designing and building a prototype/test board for the EEPROM test
Of course, I used Altium to design the board, then exported the layout as said before, ironed the artwork to a copper board, washed off the paper, threw the board into ferric chloride, sawed it, piled it and drilled it.
To make the interfacing easy, I used the exact dimensions of the April board, so my test board can snap into it real easy. Power is taken from the 500 mA DC-DC converter from the April, the SDA and SCL lines were traced to the I²C pins of the IMP. Since April is open source, the cool guys from the electric IMP uploaded the PCB files, I turned that into a step model, and included it in my board design - only on mechanical layer.
April and EEPROM board - Darrell Abbott - Flickr
The layout spaghetti wasn't hard to be routed, it took me about 30 minutes.

DSC_0039 - Darrell Abbott - Flickr
Board cleaned, artwork ready to be ironed

DSC_0046 - Darrell Abbott - Flickr
Ironing done, toner adhered to the copper, now we have to wash off the paper.

DSC_0049 - Darrell Abbott - Flickr
Paper starts to soak in some water, it is this point, where I start to rub off paper.
DSC_0051 - Darrell Abbott - Flickr
Paper is off, we can start etching
DSC_0055 - Darrell Abbott - Flickr
A snapshot taken before etching in ferric chloride. The black spots are corrections I needed to make after a quick visual check of the toner layer.
DSC_0026 (2) - Darrell Abbott - Flickr
And it's done. We can start drilling, cutting, and populating with components.
DSC_0160 - Darrell Abbott - Flickr
DSC_0164 - Darrell Abbott - Flickr
I don't solder the headers to the pads I don't need, it's easier to take it off in "case of fire".

Communicating with the monster memory we just created
Using the specific bit patterns with specific timings will allow you to access the memory of these I²C EEPROM-s with read or write procedures.

• If one desires to write data to a 24LC128t, his algorithm should follow this pattern:
1 - send start condition
2 - send slave address, with the 8-th bit low (signaling write) - 0xA8
3 - send slave register address, this is the "place" we want our data byte to be written. All register addresses and their names can be found in the device datasheet, along with their purpose.
4 - send the byte we wish to write on the address we specified before
5 - send stop condition

• If one desires to read from the 24LC128t, he/she should do something like this:
1 - send start condition
2 - send slave address, with the 8-th bit low (signaling write) - 0xA8
4 - send slave register address, from which data shall be read
5 - send start condition (restart)
6 - send slave address, with the 8-th bit high (signaling read) - 0xA9
7 - read data from bus
8 - send stop condition

I recorded some critical waveform segments, check them out! Everything is working as specified in the I²C specification.
I2cWriteHuge - Darrell Abbott - Flickr
This image depicts the writing of 0x88 to the address 0x00FF of the first EEPROM chip with 0xA0 slave address.

I2cReadHuge - Darrell Abbott - Flickr

Here you can see a read from 0x00FF address of the third EEPROM chip with slave address 0xA2. The returned data is 0xFF.

Let's see how I²C relates to our project in the next step.

Step 6: Configuring the PIC As an I²C Slave

Configuring the MSSP module of the PIC12LF1840T39A
In our case, we will need to configure the PIC12LF1840T39A to act as a slave device. First thing, that needs to be done, is to configure the MSSP module. The datasheet describes this procedure in detail, as well as the transactions.

1. Start bit detected.
2. S bit of SSP1STAT is set; SSP1IF is set if interrupt on Start detect is enabled.
3. Matching address with R/W bit clear is received.
4. The slave pulls SDA low sending an ACK to the master, and sets SSP1IF bit.
5. Software clears the SSP1IF bit.
6. Software reads received address from SSP1BUF clearing the BF flag.
7. If SEN = 1; Slave software sets CKP bit to release the SCL line.
8. The master clocks out a data byte.
9. Slave drives SDA low sending an ACK to the master, and sets SSP1IF bit.
10. Software clears SSP1IF.
11. Software reads the received byte from SSP1BUF clearing BF.
12. Steps 8-12 are repeated for all received bytes from the Master.
13. Master sends Stop condition, setting P bit of SSP1STAT, and the bus goes Idle.

Let's start writing our program. Aside of our usual configurations, like oscillator configuration, we will have to give values to the MSSP registers:

SSP1STAT = 0x80
1 = Slew rate control disabled for Standard Speed mode (100 kHz and 1 MHz)
0 = Disable SMBus specific inputs
0 = Indicates that the last byte received or transmitted was address
0 = Stop bit was not detected last
0 = Start bit was not detected last
0 = Write
0 = Address does not need to be updated
0 = Data transmit complete (does not include the ACK and Stop bits), SSP1BUF is empty

SSP1CON1 = 0x26
0 = No collision
0 = No overflow
1 = Enables the serial port and configures the SDA and SCL pins as the source of the serial port pins
1 = Disable clock
0110 = I2C Slave mode, 7-bit address

SSP1CON2 = 0x00
0 = General call address disabled
0 = Acknowledge was received
0 = Acknowledge
0 = Acknowledge sequence Idle
0 = Receive Idle
0 = Stop condition Idle
0 = Repeated Start condition Idle
0 = Clock stretching is disabled

SSP1ADD = 0x88
1000100 = 7-bit slave address
0 = Unused

SSP1MSK = 0x0F
Slave address masking, don't look for match in the first four bits

So these are the settings we need to apply to our PIC to make it work as we expect. This all goes into the initialization routine, before our infinite loop.
Once this is done, we have to write our write, read, acknowledge, negative acknowledge, idle, start and stop routines. It's always a nice thing to build something from scratch, but if someone worked with I²C before, and already has his favorite/optimized routines - just use those. I followed the instructions from the datasheet, even though I had already written I²C functions a while ago. I started from scratch for the sake of the instructable.

Step 7: Implementing the I²C Routines

Picture of Implementing the I²C Routines
Writing these shouldn't be a hard thing to do, the datasheet sequence can help a lot, forums can help a lot.
It is very important to remember, that we're talking about a slave device here, let's keep that in mind.
If you decide to write functions for this, these are the functions you should implement.

void AckI2C( void )
This function is responsible of setting the ACK bit, and sending it. Acknowledge is sent for every byte, that was successfully received by our slave
void IdleI2C( void )
Makes the bus go idle
void CloseI2C( void )
Closes the I²C bus, no further transmission is made, not until an OpenI2C() is called
unsigned char ReadI2C( void )
Reads the bus, and returns the value it receives
unsigned char DataRdyI2C( void )
Signals a complete data transaction
unsigned char WriteI2C( unsigned char )
Writes a byte to the I²C bus
void OpenI2C( unsigned char, unsigned char )
Opens and configures the MSSP module for I²C communication

As I already said, I will be using the datasheet brief to make this work: I enable SSP1 interrupt, and treat everything I²C related in the interrupt service routine. Since this application isn't time critical, this won't give us too much problems. Check out my interrupt service routine that handles the I²C communication:
i2cisr - Darrell Abbott - Flickr

I made some communication snips with my logic analyzer:
i2cinterpic - Darrell Abbott - Flickr

Being happy with seeing this work, I sent the number 6500 through I²C to the other PIC, slashed into two pieces.
6500 is 1964h in hexadecimal, this is what we should see when the progressbar fills up. And it's a match!
i2c6500 - Darrell Abbott - Flickr
Spot on again! I had to cut these in two pieces, to make them easier to see, there are tens of microseconds between the two data transmissions.
i2c6500m - Darrell Abbott - Flickr

A packet-like representation of the data exchange:
i2cpacket - Darrell Abbott - Flickr
An image of the board displaying the stuff we command him to:
DSC_0315 - Darrell Abbott - Flickr
The PICKIT is supplying current to the circuit. I remember reading about it, that the maximum current that it can provide is around 30 mA. I say, that's a pretty good consumption for a circuit with 32 LED-s!

This done, we have a PIC, that responds to I²C commands. The new peripheral which we just created will be used in our ultrasonic measurement tape.

Step 8: The PIC and the Ultrasonic Sensor

Picture of The PIC and the Ultrasonic Sensor
Another crucial element in our system is the interpretation of signals coming from the range sensor.
The HC-SR04 has four pins:

GND - common ground
This pin connects to the ground pins of the schematic

VCC - power rail
This pin must be tied to the power line of our circuit, which - in my case is provided by a 5V low drop-out regulator.
The regulator will take some heat, as the battery is a 9V square shaped one. This means a 4V drop, so we need to make sure we don't draw too much current through it. I haven't used a heatsink to protect it, the processors and the MAX7219, and the rest of the components shouldn't reach 200mA in current consumption. Since the LED-s are multiplexed, the LED current is taken into account only once.

TRIG - trigger pin
The user has to provide a microsecond long impulse on this pin, then wait for the echo to arrive on the echo pin. Note, that echo is high in the first tens of milliseconds, make sure you wait that out at initialization.
This pin is asserted, a timer is started, counting is stopped when the echo pulse is detected. From the number in the counter register we know the time needed for the sound to get back from an eventual obstacle. We know the speed of sound - we can calculate the distance!

ECHO - echo pin
This pin is used to detect the reflected sound waves. I connected it to a pin that has interrupt-on-change capability. A rising edge stops the previously mentioned counter, and saves it into a variable, which gets processed shortly after. For more info about IOC, check the datasheet of the processor.

A picture of my sensor, bottom side.
DSC_0157 - Darrell Abbott - Flickr

Top side of the sensor, with the transmitter on the left, and the receiver on the right.
DSC_0158 - Darrell Abbott - Flickr

Timing
The timing of the pulses is shown in the datasheet of this module. As mentioned before, you need to wait out the initial ~100 ms, then give 10 microsecond trigger pulses on the TRIG pin. Giving such an impulse results in an 8 cycle burst coming from the transmitter 'speaker'. Then, we need to wait for an echo to come. The echo is captured by the receiver, and transformed to an impulse of variable length on the ECHO pin. The length of this echo impulse is directly proportional with the distance to the obstacle. The datasheet gives a simple formula to calculate this:

distance [cm] = t measured [microseconds] / 58

this will return the distance in centimeters. I only divide by 5.8, so the distance gets returned in millimeters. I will use four digits, that's the best for this range and resolution.
Let's take a look at this with a quicker eye, my logic analyzer:

How can we measure pulse length using a processor? With external interrupts. I will use a rising edge IOC to start a timer, and a falling edge to stop it. The data from the register tells us the time that passed between the two edges. We have that, we can calculate the distance, as instructed by the datasheet.

uhsfunc - Darrell Abbott - Flickr

There is a timer in the background, when it detects a rising edge it starts up. A falling edge will trigger its stop, we are using the value from the timer registers to figure out the time that elapsed between the two edges. With other words: we're using a timer as a counter. The variable PulseLength is updated in the interrupt service routine.
WASTE_10_US(); is a macro I wrote, it consists of enough "nop" assembly instructions to waste a total time of 10 microseconds.

Fitting the sensor into the enclosure
Lucky me, the HC-SR04 perfectly fits into the upper part of my enclosure. I drilled two holes, than widened them up, piled them until I was able to push the "speakers" through the holes. Because I was tight on space in that area, I had to solder the oscillator from the top side to the bottom side. The useless four pin pin header was also gotten rid of.

When powering this up together with my main board, I experienced something I think I should mention here. The next step is about low drop-out regulators.

Step 9: Those Foxy LDO Regulators

Picture of Those Foxy LDO Regulators
The HC-SR04 module needs at least 5 V to work, I only realized that after I had the PCB made, and everything, so some extra small boards were needed. Okay, let's say you have an LDO of 5 V, so theoritically you're all right with that. Out of safety reasons I decided to input only 5 V on the LDO input pin, and my output wasn't even near the needed 5 V, but somewhere around 4.6 V. That is not good, and will result in a significantly shorter detection range of around 60 centimeters. The thing is still accurate, but it only measures up to 60 centimeters.

A few words about LDO-s
Low drop-out regulators are probably the cheapest way for voltage regulation. Powered by a voltage input, they provide a stabilized rated output voltage on their output pins. From my experience, working with them is pretty straightforward, you just throw them into your design, add some filter caps eventually, and you're done.
The place where an LDO differs from standard linear regulators is the amount of drop-out voltage that is required to maintain a well regulated output. The drop-out voltage is the minimum voltage required across the regulator to maintain regulation. But what is the minimum voltage drop?

Standard regulators have a darlington NPN or PNP output stage as passing element.

AD41_05-FIG-02 - Darrell Abbott - Flickr

This means, that the gate-drive voltage meets two base-to-emitter drops before it gets to the output. Knowing, that these values are at least 0.6 - 0.7 V in value, we can see how easy those 1.5, 2 volt drops accumulate. Leaving a safe headroom of 2.5 V, as I said before will keep you away from any "interesting" hardware bugs you might meet otherwise. The typical construction of an LDO regulator is this:
AD41_05-FIG-01 - Darrell Abbott - Flickr

A typical application
Let's check out some details about the LDO chain I decided to use in my design. This is a very basic representation of a linear LDO regulator.
ldobasic - Darrell Abbott - Flickr
The input voltage is applied to a pass element, which is an N or P channel FET (can be a bipolar transistor too). The pass element operates in the linear region to drop Vin down to Vout. Vout is then measured by the error amplifier and compared to a reference voltage. Based on the difference between Vin and Vout, the error amplifier drives the pass element’s gate to the appropriate voltage to ensure that the output is at the correct voltage. If large current draws try to pull the Vout lower, the error amplifier will react by raising the gate driving signal - which means an increase in Vout also. Under normal operation an LDO acts like a resistor.
My setup has these parameters: Vin = 5 V, Vout = 3.3 V, Iload = 100 mA.
In this case, my LDO operates as a (5 - 3.3) / 0.1 = 17 ohm resistor. I've never tried this before, but I've read before, that in this case, the LDO could be replaced by a 17 ohm resistor. Of course, this would demand a VERY steady load current. In most situations wou won't see stuff like this, there are current oscillations resulted by LED-s or other stuff turning on, so the parameters will be unlikely static. You'll be better off sticking with the LDO, which has a feedback loop in it.

That is done, now you only have to take care not to mess up the pin-out, as they differ from package to package, and from supplier to supplier.
That's enough for a side explanation about LDO-s, let's move on with the real thing!

Step 10: Okay, Slave I²C Sensor Is Built, What Do I Do With It?

Picture of Okay, Slave I²C Sensor Is Built, What Do I Do With It?
Want to do more?
If you're done with the previous steps, you can sit back, and watch it work with an arduino. If you want something cool, keep on working to get a cool measuring "tape"!
A few years ago I was working with my dad on a house, that needed its whole electric wiring network remade. While we were working on that, we had to measure whether the wires connect, whether the mains is present on the wires, etc. We forgot to switch back the multi-meter to high voltage measurement position: BOOM. I threw the guts out, but kept the enclosure, I thought, maybe I will use it one day. Originally I wanted to build my measuring device into the enclosure of that broken multi-meter, but I couldn't figure out what to put in the empty circle in the middle, where the rotary switch used to be. I looked on the web for something similar, and found the BOS501B enclosure.
It has space for a 9 V battery, a few screw holes, and a slot for the 4 LED displays I will use.

PCB pre-work
Get the caliper again, make some measurements of where the holes are, where the display slot is, so you can figure out your boards size. The work phases are the same as with the previous boards. Check back to the video where I showed you how to shape the board!

Schematic
If I want more from my I²C sensor, (in my case to turn it into a measuring tool) I have to add a display to it and some user interface components, so my dad can control it. The easiest choice: use another identical PIC12LF1840T39A to do the I²C queries, and the SPI bit-banging for the display. I also added two buttons on the MCLR pins just in case I want some sort of input from the user. Here's my schematic, most of the components were added to my libraries during this project.
Schematic - Darrell Abbott - Flickr

The two big rectangles in the middle are the two processors, U1 is the display driver (MAX7219), the one in the top middle is the display itself. P? is actually an LDO, i just didn't bother with drawing a special schematic symbol for it now. On the bottom there are two buttons with two pull-ups, which will be used for user input, and the programmer headers for the two PIC processors.

PCB making
Transfer your changes to the PCB file, then place, replace, lock, route, until you're finished. Then create an "Output Job" file, add a "Documentation output" of the "PCB prints". Make sure it prints 1:1, you only enabled the layers you want and most important: make sure it won't be mirrored. Then tie it to a PDF output and generate the file by pressing F9.
I've had the bad luck to place the display on the top layer by mistake, so the one you see images of is actually the second iteration of this PCB. Now it should be okay. Version one even had some extra LED-s in it, but as the project evolved I realized I don't really need them. Let me show you some pics of my board!

right - Darrell Abbott - Flickrfront - Darrell Abbott - Flickrleft - Darrell Abbott - Flickr

A couple of details of the layout, in Altium, and in PDF format
PCB - Darrell Abbott - FlickrLayout - Darrell Abbott - Flickr

Solder up the components, plug it in, and start programming! I recommend having flux and solder wick at hand when soldering these smaller components, along with very thin solder. This way you have greater control over the solder amount and where it jumps to!

Aaaand SNAP!
Just when I was about to wire the thing together I came to realize the terrible thing, that just happened. Awful enough, I completely disregarded these two letters from the PIC ID number: "LF". This series of PIC processors work on 3.3 V, so at 5 V one will see smoke coming out of them instead of nice serial signals. So the 5 V regulator is off the table, from the PIC-s point of view, we need to add a 3.3 V regulator.
The 5 V regulator has to stick around, though, because the MAX232 of the ultrasonic sensor doesn't work with 3.3 V. I realized this, after I saw, that each time, the 10 microsecond trigger impulse was sent, a constant echo of 143.6 milliseconds is received.

Let's deal with this. The HC-SR04 board needs to be powered from 5 V, the trigger signal needs to be raised from the PIC 3.3 V to 5 V, the echo signal needs to be lowered from 5 V to 3.3 V. Let's design a simple level shifting mechanism, that fits in our enclosure:
levelshifter - Darrell Abbott - Flickr

Check out the schematics. Since each line is uni-directional, there's no need for complex level shifter IC-s, not even the for the cool & common double FET solution. This is simple enough, the 5 V signal is taken to 3.3 V by a zener diode voltage stabilizer circuit, the 3.3 V signal to 5 V switch is made with the help of a transistor and a pull-up resistor. Of course, this has to be small enough to fit in our enclosure.
levelshifterschematic - Darrell Abbott - Flickr
If you're done with this, you can move on as you would've done anyway. If you started off with a 5V tolerant PIC, you're lucky - you can skip this side project. Mine came out pretty small, it has around 18 x 15 millimeters. Unfortunately I wasn't able to find a SMT zener, so the thing has some height too - because of the through-hole diode. Let's wrap it up to our system so we can move on with the cool stuff!
Making use of this board
We're going to use this board to create a nice little tool for every crafty dad! I already told this and that about boards, schematics, but haven't said anything about SPI. It is a serial protocol which we're going to use to talk to our display through the display driver. But how do we communicate on SPI? Let's check it out!

Step 11: A Couple of Words About SPI Protocol and Its Position in This Project

Picture of A Couple of Words About SPI Protocol and Its Position in This Project
Brief description of the SPI protocol
An SPI (Serial Peripheral Interface) bus offers bidirectional communication between two devices. Similar to the previously used I²C protocol, this too is Master/Slave based. It is often called four wire interface; however, an SPI bus often uses 3, 5, 6 or even more wires to communicate. The SPI bus can be expanded by adding slaves to it: their MOSI, MISO, SCLK pins are wired together, their slave select pin needs to be pulled separately. The slave select pin for an SPI slave is like the slave address byte sequence for an I²C device.
Usually it takes 8 clocks to complete a data transfer. While the master device sends a bit to the slave (on the MOSI line), the slave sends out a bit to the master (MISO line). This transfer is synchronized by the clock signal on the SCLK net. The frequency of this clock signal can go up to tens in MHz.

The SPI protocol and the PIC12LF1840T39A
At the heart of a PIC SPI module is the SPIxSR shift register, the shift occurs with every clock signal on the SCLK pin. Similar to other serial peripherals, this too is double buffered, the SSP1SR content cannot be written, or read directly. The data needed to be sent must be written into SSP1BUF, this acts as a transmit register at write. During transmission, the data is automatically loaded in SSP1SR, and is clocked out from here.
Reception is done again via the internal shift register. When 8 bits are clocked into the SSP1SR, the byte is loaded intu SSP1BUF, the user can read it from there. This time, the SSPBUF acted as a receive register.
The SPI modules built in the PICs can work up to 10 MHz. In our case in particular, we will use ~10 KHz.

To our great sadness, the SPI and the I²C lines are multiplexed to the same pins. This means, we will have to bit-bang one of them. I wrote some routines for another instructable of mine, I copied it to this processor, but hit some difficulties, so I wrote them again, this time without any interrupts involved.
Because I am using bit banged SPI, I won't really go into details about it. I'll rather focus on analyzing the MAX7219 datasheet, to see what I have to put on the pins to make it work!
Here's an image from the MAX7219 datasheet:
SPI timing requirement - Darrell Abbott - Flickr

You can see how the lines need to be toggled, as time passes. That is exactly what my signals have to look like on a scope, when I'm done. Look at the code sample and analyze what is written there, you'll see how it acts to generate the bit pattern we need.

SPIfunction - Darrell Abbott - Flickr
This can be reused, you only have to change the #define-s in the header file, or wherever you placed them. I have an interrupt driven version too, but I got rid of it, because I though that was guilty for my non-working display. Later it turned out the display was on the top layer instead of the bottom layer - this was giving me the headaches. However, at that point I already rolled over to the delay-based SPI bit-bang algorithm, and didn't wanted to switch back.

Once the code you wrote generates the exact waveform you expect it to generate, you can move on to send commands to your display! There are plenty of ways to test your algorithm, there are simulator, analyzers, scopes that can help you with this.
This is a single word transmission, it's cropped out and zoomed from the next image with the data exchange
SPI2 - Darrell Abbott - Flickr

A communication capture, the display numbers are refreshed during this transfer
SPI - Darrell Abbott - Flickr

Okay, it seems we got ourselves through this display thing too. Now we need to configure this PIC to have its MSSP module configured as an I²C master.

Step 12: Configuring the PIC As I²C Master

The I²C initialization routine

void I2C_Init( void )
{
TRISA1 = 1;
TRISA2 = 1;
ANSELA = 0b00000000;
SSP1ADD = 0x03;
SSP1STAT = 0x80;
SSP1CON1 = 0x28;
SSP1CON2 = 0x00;
}

This is pretty much the same with what we did with the sensor, only this time we have to set the MSSP up to act as a master. I wrote some functions to do this, I will do some snips for you with sample code, to get you rolling. For some stupid reason I wasn't able to post this as an image, So here it is, a bit uglier, but at least it works.

The main routine
This is my main code and I²C code, some software de-bouncing is done for changing the screen luminosity, I²C commands are issued to collect distance data from the HC-SR04 through slave processor, then the numbers are parsed to be displayed on the 7 segment LED display. For the sake of easier routing I chose the digit cathodes to go randomly to the MAX7219, it is way easier to get around this in software then to do some fancy routing to get the tracks right.
allfuncsi2cmaster - Darrell Abbott - Flickri2cmasterfunc - Darrell Abbott - Flickr
It might appear too small on certain screens, it is definitely small here, in the editor, so if you can't see it too good, follow it's link, and view it on flickr.

The three transfers you've already seen when I presented you the slave transmission. The bus is the same, so I didn't bother to take new snips - the concept itself is all right, and that's what matters.
i2cinterpic - Darrell Abbott - Flickr

i2c6500 - Darrell Abbott - Flickr

i2c6500m - Darrell Abbott - Flickr

Having said this, the master PIC now talks to the slave PIC, asking for distance measurements. When data is received the display values are immediately modified to show the current distance measurement.
I also added a user interface to set the screen brightness. The master PIC has a button on the MCLR pin. By pressing it at least for half of second, the user can cycle through eight levels of brightness. I only put this in, because the MAX7219 has this feature in-built, and had that button lying around uselessly anyway.
I am still thinking of a good feature to put on the second button, which connects to the slave PIC's MCLR pin. I thought of using it to measure difference between the actual and the previous measurement, but the sensor only sees 400 centimeters, that's a bit short, so I didn't bother.
Suggestions are welcome!

Step 13: Wrapping It Up, Putting a Ribbon on It

Picture of Wrapping It Up, Putting a Ribbon on It
Connect the ultrasonic sensor to the main board, and start doing some measurements, calibrations (this won't be needed, if you did everything right!).

I also want to design a sticker for this, I am doing this in Writer, text box sizes can be adjusted to match the enclosure dimensions, and button positions, so I don't really expect surprises while doing this.
I hope you'll be having fun while doing this last step of creating a useful tool, and a nice DIY gift!

There are still quite a few days until Christmas, so I will stash this in my workshop, so Dad won't find it! It will come with a set of replacement batteries, just in case he uses it too often!

I honestly hope you've had fun reading or building this, if it inspires at least one dude to after-build - it will make this instructable a great one.

DSC_0323 - Darrell Abbott - Flickr

A movie clip to prove that this is actually working:

Toodles!

Comments

Abham22 (author)2016-02-18

Great Work!

Could you please inform me which kind of logic analyser you using?

pityukecske (author)Abham222016-02-18

Thanks, it's Scanalogic 2

BurakTutal (author)2015-04-26

Nice Project. I have made a smilar one. But mine is talking according to distance :) You can check my project. :)

https://www.instructables.com/id/Arduino-wtv020-Sd-...

pityukecske (author)2013-12-20

Hi,

I don't really understand what you're getting at. An arduino is a board (which personally I don't like) with a microcontroller on it. So arduino is 'designed' for any kind of electronic control or monitoring task you might come up with. I don't know about the microsoft stuff, but there are boards, which are able to run .NET framework, those might be in some sort of connection with MS. These boards are named Fez Panda development boards, and - I believe - they are compatible with all kind of arduino shields.

I hope this helps, if it does not, you might get better help by formulating a question with more details in it. I'll be glad to answer :)

Istvan

halamka (author)2013-12-19

I have 1 or 2 of these. Is arduino designed to keep a simple BASIC computer from being built? Microsoft has ways of doing just that?

floppystick6 (author)2013-12-13

What kind of paper do you use to transfer the toner?

pityukecske (author)floppystick62013-12-13

Hi,

I just rip them out of ad magazines, the one i have now was actually for clocks and jewelry. Nothing special. I used thermo-transfer foils too, but with smaller success. This shiny paper works great for me!

chrwei (author)2013-12-13

this is some serious detail! I do have a couple suggestions, I'll try to be constructive..

1. there's a few steps here that seems unrelated to the project, or are related in ways that isn't obvious. step2 seems like it should be a separate 'ible entirely.

2. step1 talks about the inspiration, and a use case that is not this 'ible's final form. this is fine, but some introduction of the parts chosen and why, and something about the final form would be helpful. I also don't see the devices accuracy and range mentioned.

3. what does the board in step3 actually do? I still have no idea what all the eeproms are for. that step almost seems like it's from a different instructible. can you introduce how each of these parts fit into the final form in each step?

4. there's no step for the final assembly? having this might answer some of what the various boards are for.

overall, great detail, but I'm left a bit confused as to how all these steps relate to each other

pityukecske (author)chrwei2013-12-13

First of all, I'd like to say thanks for your input, I'll keep the suggestions in mind from now on. I'll answer your questions one by one:
1 - indeed, they are, I chose to make them a part of the ible simply because it was indeed in the timeline of the project, and - as I said at the very beginning of the intro - I wrote this while I was working on it. I dedicated a step for that, because in my opinion these boards are quite an important "step" of the work itself.

2 - In step 1 I meant to share the main reason why building this I²C sensor came to me at all. Indeed, I should have mentioned how the sensor and my meter relate on to the other, but thought it was something trivial - ssut snap on the slave PIC + HC-SR04 to the arduino's I²C bus, and that's it. In fact the simplicity of the sensor made me search for an application, that could actually fit in my working I²C sensor.
3 - that step supposed to help out with understanding how the I²C bus works. Since I haven't had the sensor working at the time, I had to pick a slave that already responds to I²C commands. I picked the EEPROMS in question. I've had dozens of that type of EEPROM, so I ended up soldering 8 of them (for each possible slave address) on my board, for bigger memory.
4 - you're right again, there are no pictures about the final assembly, I simply forgot to add more details about that, I thought the schematic of the thing would be enough. I won't forget that next time.

Thanks for your suggestions again, they're already making changes to the next upcoming instructable of mine.

Istvan

pityukecske (author)pityukecske2013-12-13

The 4 meter range is present in the 'ible, I could have made it more obvious, though. It's at the 12-th step. The accuracy is of around 2 millimeters, given by the HC-SR04 sensor, and timer accuracy.

Istvan

agis68 (author)2013-12-13

excellent work and realy nice presentation...you have my votes....

give us a cost list of your construction

pityukecske (author)agis682013-12-13

Thanks!

I put together a list, they might not match the ebay prices, I chose the prices I would be able to buy them from our local electronics shop. A lot of components I've already had, so that don't count in the BOM, not in mine, at least!

• 1 pcs. - plastic enclosure - ~20$
• 1 pcs. - HC-SR04 - 2$
• 2 pcs. - PIC12LF1840T39A - 2 x 2$
• 1 pcs. - MAX7219 - 3.8$
• 1 pcs. - common cathode display - 3$
• 2 pcs. - right angled tactile switches - 2$
• 6 pcs. - resistors - < 1$
• 1 pcs. - Copper board - 2$
• 1 pcs. - 5 V low-dropout regulator - 2$
• X pcs. - FeCl etchant, solder, toner, etc - <3$
• 1 pcs. - BCR562 - 1.3$
• 1 pcs. - 3.3 V zener diode - 1$
• 1 pcs. - 3.3 V low-dropout regulator - 2$

So you're looking at something around 47$, if you have to buy everything.

Istvan

Build_it_Bob (author)2013-12-12

Very nice work , and very professional . Enclosure looks great too!
Build_it_Bob

pityukecske (author)Build_it_Bob2013-12-13

Glad you like it! Thanks!

alexandrubenza (author)2013-12-12

you are scarily thorough. absolutely solid, amazing work. you're a truly gifted individual!

Thanks for the compliment, it really means a lot! :)

artemff (author)2013-12-12

it's a very detailed instructable, which is great for educational purposes.

However, one can simplify life by using a ready-to-use components.
I made a similar device some time ago (it was a part of another project where distance
measuring was required), and used just an ultrasonic module, an Arduino, and a standard backlit-LCD screen. In this case there's no voltage compatibility problem, one just connects the pins and programs the device. Indeed, when the ultrasounder sends the sounding pulse, the current is high and the voltage drop can reset arduino, so I had to use a large electrolytic capacitor.

As far as I remember, the range of the device was ~4 m (depends on the reflecting surface and angle).

pityukecske (author)artemff2013-12-12

Thank you!

Well, if everything is powered from 5V, everything is just fine, Unfortunately I am notorious of making these kind of mistakes - I learn a lot by correcting them afterwards, though.
Personally, I don't really like to use arduinos, it sort of takes away a little from the process of building something from scratch, not to mention the overkill it means in some situations.
The large cap is generally a good idea, but when powering up for the first time, and the cap is discharged, it acts as a short-circuit for a short time -> power surge on that side. I'm glad the level shifter worked out well in my case!
Yes, the range is around four meters, I've read on forums about cases in which the thing still worked at 6.5. It goes down to the components quality, I assume.

Thanks for your comment! :)

thegoom (author)2013-12-10

We demand a video of your dad opening his present.

pityukecske (author)thegoom2013-12-10

I'll see what I can do about that! ;)

jessyratfink (author)2013-12-10

Fantastic documentation. :D

pityukecske (author)jessyratfink2013-12-10

Thanks! :)

audreyobscura (author)2013-12-09

Woah! Thanks for sharing such a thorough build with us here at Instructables.

Thanks for your comment! As for the project, I only hope it will inspire many!

About This Instructable

39,257views

342favorites

License:

More by pityukecske:Ultrasonic Obstacle-avoiding RobotHDD Clock - persistence of visionCar battery charger with PIC12F683
Add instructable to: