Automotive Ultrasonic Hacking HowTo

Understand the Existing System

The first step in the process is to understand the existing system on a vehicle that is equipped with “Park Assists”. Just for background information, the term “Park Assist” was chosen for a reason. GM will tell you very quickly, this is NOT a safety device. This is for parking ONLY. Their fear is some bonehead will back over someone and claim the backup sensors never warned them. Not being considered a safety device made our lives much easier. That was good news since we had such a terrible time anyway. I could write a book about EMC testing!

Now, just so happens, Momma has a company car (2008 Tahoe) that is getting close to the end of its lease. Also, just so happens, Momma is out of town on a business trip in China. While the cat is a away the mice will play!

Back to step 1, in this step we’re going to activate the system and see how it works. Part of activating the system is putting the vehicle into reverse gear with the engine off. So, block the wheels so the vehicle won’t roll away while out of park gear. The system can be activated by keying up the ignition (again, leave the engine off) and pulling the gear shifter into reverse. With the vehicle stationary, you can get out of the vehicle and play with the system. There are three LED lights that light up based on range. There is also a chime when a new object is detected. Or, a continuous chime for very close objects. With the back hatch open the chime is easier to hear. Now is the time you can check the range of the system to verify to makes it out to 2.5 meters. We spent many months working on the FOV (field of view) for GM.I could never understand why GM wanted such a long range – they are the only ones shooting out to 2.5 meters. Everyone else is happy at 2 meters. Would a customer actualy care about that extra 1/2 meter? I know, I rant too much.

Find the Brain Box

OK, at this point we need to find the brain-box. Where, Oh where, is that silly little module hiding. You would think, that module has got to be near the back of the vehicle near the sensors. The closer the module is to the sensors the cheaper the wiring back, and fourth. Boy, right behind those side covers would be a great place to hide that little control module. Nope. No where to be found.

Maybe under the rear seats. There were a couple more carpeted covers I pulled loose. I was starting to get worried at this point. After all, the vehicle has to be turned back into the dealership soon.

My neighbor, who works at a dealership, came to my rescue – his fiend in the parts department gave me a tip that the Tahoe puts the module under the passenger seat. Sure enough, there it was right under the seat. I could almost reach it with the seat jacked all the way back. In the end, I took the four bolts out that hold the seat down and simply rocked the seat back. With the module just a bit more exposed I was able to wrench it free from the vehicle.

Success – The module is mine! At least until Momma comes home.

Below are some more pictures of the process of looking for that darn little module. By the way, the acronym UPA is short for “Ultrasonic Park Assist”.

Sorting Out the Pinouts

he next step involves sorting out where pins on the UPA go. There are two connectors which connect the UPA to the vehicle. One connector has 12 pins and the other 8.

Sorting out the pin functions is a major pain. However, by close inspection of the circuit board we can at least figure out some of them. Below is a table which lists all the pins in the UPA connectors. By inspection, it was rather obvious where battery power, vehicle ground, and CAN communications make connections. Also, by “beeping out” the harness between the UPA module and the sensors, more functions became clear.

The main image on this page shows a close-up of the two cavity connector on the UPA module. The pinout matches the numbers in the table below. With the power, ground, and sensor connections sorted we’re ready to start having some fun. Note, the UPA not a “switched powered” module. In other words, the module is always powered. This implies that the module sleeps, and wakes, based on CAN messages. To verify this, simply apply 12 volts to the module and watch the current load. After 10 to 20 seconds, the module will drop its current load to less than 200uA – this is sleep mode.

Note, pin #4 on the eight pin connector (Sensor Power) is a regulated 8.5Vdc.

Below are a bunch of pictures of the UPA module (both top and bottom). A couple of things to note about the UPA module:

  1. CPU – MC9S12B96CFUE
    1. This is a Freescale (formerly Motorola) chip which use to be called a Star12. This is a 16bit micro capable of running up to 24MHz.
    2. The “96” in the part number means it has 96k of flash space.
    3. The “B” is a sub-family designation – means it has two serial ports, CAN, plus a bunch of other junk.
    4. The “CFUE” is temperature grade info (good to -40 to 85 degree C – good for inside a vehicle).
  2. CAN – TH 8056
    1. Single Wire CAN – Good for low speed GMLAN.
  3. Sensor Power – LM2941S
    1. Linear Regulator – Provides a regulated 5Vdc / 1Amp output for the sensors.
    2. Enable pin allows the micro to shut off the sensors when not in use (like while travelling down the highway).
    3. I’m surprised GM allows this chip to be used – the manufacture spec lists the chip only good to a maximum of 26 volts. GM still designs their vehicle so that “good old boys”  can crank their cars by double battery. If you REALLY want to crank your car – put to car batteries in series. Using two batteries, you can really crank your rod. I guess people (in the deep woods) still do this to their vehicles. Maybe up in Alaska – I don’t know, it’s just in the spec. Suppliers live and breath “the spec”. GM engineers and suppliers don’t debate things like this – if it’s in the spec you WILL do it. I know, I’m on a rant again.

Pulling the Sensors

Another hassle step – gotta pull the fascia loose to get at the sensors. Luckily, only one side of the fascia has to come completely loose. I left the left side attached and only loosened the right side completely. Still, there are a bunch of screws. There are also two “Christmas Tree” fasteners used in the center of the fascia. Those types of fasteners should be illegal.

Below are some more pics showing the fascia coming loose from the Tahoe. The fascia never came completely off the vehicle. Lucky for me, I had some old replacement sensors I could use to fill the holes. Not quite the right color, but, close enough – we’ll see if Momma notices.

To remove the sensors from the fascia, there are two little snap-over arms that engage two little ears on the sensor. It’s pretty easy to pull the little arms back and push the sensor back through the fascia. Just don’t over extend the plastic arms or they won’t flex back into place.

Sensor Teardown

OK, we gotta see what’s in one of these puppies. I dug all the silicone rubber out of the backside of one sensor. Kinda anticlimactic – they make so many of these sensors that they have developed their chip for inside the sensor. Wow! The rule of thumb is that it cost a million+ bucks to develop your own custom chip. Wow.

Bottom left corner of the photo is the business end of the sensor. This is the painted surface that sticks through the fascia. We call this the membrane – inside the cavity (it’s kinda like a thimble) is a small piezo element. The element is about 3/16″ diameter circle in the floor of the membrane (or, thimble). When the element is hit with a 160 volts it flexes. The flexing can happen because the thickness of the floor is really thin – about 1/32″. Hitting the element at 38kHz and you get a ultrasonic speaker.

But wait, there is more, when not acting like a speaker, the sensor acts like a microphone. If ultrasonic energy hits the membrane, the piezo element inside will produce a really small amount of voltage. The sensor amplifies this into a signal to work with. Remember, switching from active speaker mode to passive microphone mode takes time. This is the ring-down where the sensor is essential deaf to any return echo. This defines the minimum detectable distance a single sensor can detect. For GM, Ford, and Chrysler (makes I’m familiar with) who all use a 16mm membrane, the minimum distance is about 10cm.

Bottom right in the picture is an isolator cup. The cup goes under the plastic cover ring which in the upper right of the picture. What is not shown is the isolator ring that goes over the nose of the sensor. The isolator ring is slipped on just before the sensor is pushing into the retainer clip. Note, the retainer clip is a plastic piece which is glued (actual ultrasonically wielded – how ironic) to the inside surface of the fascia. Looking at an installed sensor, a small amount of the plastic retainer clip is visible – kind of like a buffer between the fascia and the sensor. The edge of the isolator ring is also visible while looking at an installed sensor on a vehicle.

The picture below shows the membrane cavity (bottom left). On the bottom right side is an isolator ring. The other pic show the three pin connector.

Hacking GMLAN

Now comes the fun part. Hacking single-wire GMLAN is really, rather easy. Our goal here is sort out all the GMLAN messages the UPA module needs to operate. Remember when we learned how to operate the system in the vehicle? Now we can use that to watch the CAN messages.

First, some background on single-wire GMLAN (SW-GMLAN). All the messages on SW-GMLAN are of the “extended” variety. So, all the CAN identifiers are 29-bits in length. Makes for long hexadecimal numbers. Second, SW-GMLAN runs at 33.333 kbits/s. You need to know both these bits of info to make a connection to the bus.

Each module on the bus has a unique node ID. Each message sent by a module, on the low speed single-wire CAN bus, is tagged with the modules node address. The ID is packed into the message identifier – it’s the last 8bits of the identifier. Each module broadcasts a NCA message (no data in the NCA) at a frequency of every 1 to 2 seconds. The NCA message is just a simple message to let everyone know that the node is present. So, powering up a single-wire GMLAN module, and at a minimum it will broadcast its NCA message which contains the node address.

This makes it easy to figure out a module’s node address. Simply power-up the UPA module and it will spit out several NCA messages before it goes into sleep mode. The UPA module has a node address of 0xBB. it was rumored that BB was chosen to match the initials of the lady who oversaw the UPA program at GM. Not sure about that.

Below is a CANalyzer trace when I just powered the UPA module up alone. You can see the messages are timed at about 1.2 seconds apart. After seven messages, the module goes to sleep. I know the module went to sleep because the power supply goes from a load of 30mA to zero (actually, 50uA in sleep mode).

CANalyzer Trace:

Begin Triggerblock Wed Apr 28 12:25:14 am 2010
0.0000 Start of measurement
0.0668 CAN 1 Status:chip status error active
3.4715 1  FFFE0BBx        Rx   d 0 TE
4.7002 1  FFFE0BBx        Rx   d 0 TE
5.9288 1  FFFE0BBx        Rx   d 0 TE
7.1579 1  FFFE0BBx        Rx   d 0 TE
8.3866 1  FFFE0BBx        Rx   d 0 TE
9.6153 1  FFFE0BBx        Rx   d 0 TE
10.8441 1  FFFE0BBx        Rx   d 0 TE
End TriggerBlock

The thing to note here is the 0xBB on the end of each CAN message identifier. When we get jacked into the vehicle the 0xBB on the end of the identifier will help us spot our messages much more easily. The rest of those bits in the identifier have meaning to GM. They can encode a destination node address, priority, plus other junk into those extra bits. As an example, for the messages above, CANalyzer says they have a “priority” of 03 – whatever that means

By the way, we’ll need some kind of CAN tool that lets you see what’s going on with the CAN bus. The old timers (like me) use tools from Vector – tools like CANalyzer and CANoe. CANalyzer goes for $5000 and, I think, CANoe is over $15,000 (maybe even $20k – I can’t remember). The new guys use stuff from Intrepid Controls like the neoVI. The neoVI is much cheaper at one to three grand, depending on the model. I also see some really cheap stuff in the back of Circuit Cellar (under $200 bucks). Bottom line, you gotta have some kinda tool that will let you spy on the CAN messages that are flying around. The expensive tools allow automatic decoding of the CAN messages. GM gives a database of all the CAN messages to suppliers. The database is read by the tools to decode the messages. The auto message decode is not needed for what we’re doing here – I’m just providing background info.

There are also some home-grown solutions that look good. Here is a link to one that uses the mbed processor:

The tool you choose does have to have a mode where repeating messages are updated in-place on the screen. In other words, the screen does not scroll as new messages are received. This mode makes it easy to see that there are, on average, only about 20 repeating messages on GMLAN with the engine off and the vehicle in a static state. Within these 20 messages are the messages from our UPA module. Remember, all we have to do is look for the node address to is if the message is from the UPA.

If we strip out all the UPA messages we’re left with all the messages from all the other modules. Duh. But, if we play those other messages back we could fake the UPA into thinking it’s running inside a GM vehicle. If the UPA see those messages it is going to think it should be up and running – actively pinging those sensors. So, now we can run the UPA module outside of the vehicle. Now, we can run the UPA on the bench, in the lab! Now we’re cooking.

So, back to the Tahoe for some more data:

Time        CAN-ID        Data
36.012160   639           00 04 00 00 00 00 00 00
28.224290   623           00 10 00 00 00 00 00 00
3.006250    624           00 08 00 00 00 00 00 00
3.001670    62c           00 40 00 00 00 00 00 00
3.003730    631           00 20 00 00 00 00 00 00
1.010850    621           00 7f 00 00 00 00 00 00
4.995430    10002040x     0a 01 64 05
5.003230    10004060x     02
4.999470    1000c060x     01 60
4.986210    1000c099x     01 99
4.981670    1000c0a0x     01 a0
5.002310    1000c0a1x     01 a1
1.199300    100180c0x     fc 26 01
0.100050    1001a0c0x     00 00
0.050190    1001c040x     07 65 04 00 00 00 00 00
15.989280   1001e060x     86 28 04 ff 88
36.015570   1001e0bbx     34 14 01 7f 2b                *
0.039250    10020080x     00
31.216490   10022040x     00 cf 05
4.990500    10024040x     42 1e 16 4b 31 33 31 31
4.988940    10026040x     31 5a 32 43 37 35 31 37
0.100010    10028040x     17 30 c0 5f 6d 88 00 03
0.102630    100380a1x     00
0.101680    1004a040x     e0 20 45 01 00 00 01
0.102390    1004c040x     00 b9 00 00 00 00 00 00
0.197560    1005ax        02 00
0.087690    1005c040x     00 00
0.085130    1005e040x     40 04 30 00 91
0.298550    10064040x     00 ec 00 00 00
0.070490    1009e040x     10 00 00 00 00 40 00 00
0.070010    100a2040x     00
0.999960    100a6097x     0a 04 1c 02 74 66
0.511890    100a8080x     9c 08 1e b3 0a
0.999970    100aa097x     09 23 30 15 6e 2a 22 04
1.000010    100ac097x     01 60 00 10 00 01 cf 32
0.508680    100c2099x     00 60
0.075080    100c4040x     04 00 02 00 00 0f fa
1.399920    1011c0bbx     10 70 00 00                   *
46.542630   10138060x     00
0.075100    10248040x     00 00 00 00
1.199960    1028a080x     00 00
1.170130    1031c089x     0c 20
1.201250    1031c097x     0e 00
0.075200    104f0040x     00 1c 00
0.160020    130058x       73 f3 73 00 08 00
0.999980    2c058x        03
0.498020    32058x        03
0.499980    c0099x        41 00 00 20 6e 01 6c
0.201150    c0160c0x      07 66 00 00 00 00 00 00
0.499310    c030040x      00 5b d1 00
0.087850    c050040x      00 4f 00 00 00 00 40 00
0.087280    c052040x      00 00 9c 00 06 53 53 41
0.296160    c06e040x      40 00 04 00 03 1e 14 00
0.069700    c0b6040x      fd 2a 02
0.200080    e059x         01 51
1.116090    fffe040x
1.199950    fffe058x
1.194590    fffe059x
1.199300    fffe05ax
1.199600    fffe060x
1.200020    fffe080x
1.199960    fffe085x
1.170130    fffe089x
1.200190    fffe097x
1.200860    fffe099x
1.197070    fffe0a0x
1.204570    fffe0a1x
1.602950    fffe0a8x
1.210160    fffe0b0x
1.199960    fffe0bbx                                    *
1.195460    fffe0c0x                                   

These are all the single-wire CAN messages that are flying on the bus while testing the UPA. So, data taken from the vehicle, while keyed up / engine off, while in reverse gear. There are three messages from our UPA in the above list (I marked them with an asterisk). The first one (CAN-ID 1001e0bbx which is non-repeating) happened when I walked behind the vehicle. Walking behind the vehicle caused the chime to sound while UPA is active. So, in other words, the CAN message 1001e0bbx with its data, causes a chime. And since the CAN-ID ends in 0xBB we know it’s from our UPA module. Note, the little “x” after the CAN-ID just means they are “extend” CAN messages.

The second message from the UPA is a repeating status message (CAN-ID 1011c0bbx) that comes across every 1.4 seconds. The 0x70 in the data is the distance. I verified this by placing a trash can behind the car. Moving the trash can changed the second byte in the data packet. With 0x70, the distance was about one meter.

The last UPA message is just the CAN-ID fffe0c0x, which has no data. This is just the keep-alive message we saw before.

So that’s it, just strip those three UPA messages out of the above list. Replay those messages back (at the same rate / with the same data) to a UPA on the bench and bingo, one happy UPA module. I don’t need to know what all those other CAN message mean. All I know is, play them back to the UPA and it will go active and ping the sensors. Once the sensors are pinging away on the bench, we can probe them!

Simulating GMLAN

Believe it, or not, but we’re getting close to being able to probe those sensors. It’s been a pain, but, we’re now able to totally simulate all the GMLAN messages so that the UPA is happy.

To be sure, not all those messages from step #6 are necessary to make the UPA happy. Using CANalyzer to simulate all the messages, I was able to start with the full list and slowly drop messages, one by one, until I was left with a minimized list.

Now, the problem remains as to how to simulate the messages. Using CANalyzer is a major pain in the butt. A much better solution would be to build some embedded device to generate the messages.

As an example, the main picture on this page shows the GUI from a bench simulator we made several years ago. The simulator generated all the messages necessary to simulate a vehicle for our testing purposes. The simulator hardware was a small box which had a Star12 (Freescale MC9S12C128) board inside from I built a carrier board for the tech-no arts board which had a nice power supply, a UART interface, and a nice single-wire CAN interface. A couple of programmable status LEDs rounded out the design. All packed into a nice metal case. We made bunches of these things – everyone had to have one.

The simulator hardware simply generates the list of repeating CAN messages with each message being generated at the right rate. The simulator hardware also had a serial port that would accept commands. Using the input commands, the simualtor could change the data within the simulated CAN messages. This allowed us to further simulate the vehicle. For example, we could simulate vehicle speed, ignition switch, gear shifter position, along with a bunch of other stuff. You can see some of the setting posible on the GUI screen.

If I had it to do over again, I would use the mbed board instead. The mbed is a much more powerful micro than the Star12. Plus, the mbed has an Ethernet port – much better than using USB/RS-232. In fact, the more I think about it, using the mbed for CAN development would be really sweet. Oh, I degrees.

For our purposes, we don’t need all that fancy stuff. We have a list of CAN messages that get the UPA into the correct mode. By emitting those messages back to the DUT (device under test) we’ll get the UPA to activate the sensors, which is the goal, after all.

Below is a labeled picture showing all the major components on the bench simulator.  Note the foam block on the bench setup. This is used to absorb the ultrasonic energy from the sensors. So, the sensors are effectively deaf while face down in the foam block. The other pictures below show a couple close up shots of the simulator’s carrier board and its enclosure.

Probing the Sensors

Until now, the reverse engineering has been kind of focused on the UPA module. I simply tricked the UPA module into thinking it’s operating inside the vehicle. The UPA happily powers up and goes online – pinging the sensor. All this was required so that I could start probing the interaction between the UPA module and the sensors.

OK, so how do the sensors know to go active? Well, there is a two-way communications channel between each sensor and the UPA module. The UPA sends commands to the sensor and the sensor simply responds. However, the sensor’s response appears to be a bit more complicated. Instead of responding with a digital value, it appears that the sensor responds with the actual echo. This is both good – and bad. While it’s easy to send serial commands to the sensor, it’s a bit more difficult to receive the echo pulses back. What this means is, the control module must be able to both send commands and time-stamp any response pulses after the command is sent. The up side is, the sensor is capable of reporting multiple targets (echoes). You see, most digital sensor simply report the distance to the closest object. By sensing multiply echoes, this sensor is able to report motion even if the motion is beyond the closest target. Very cool for security applications.

I’m getting ahead of myself, we still need to sort out the startup sequence between the sensors and the UPA module. Below is a screen capture from the LSA (logic state analyzer) which shows the communications between the UPA module and the four sensors. It appears there is some kind of download going on between the UPA module and the sensors. The UPA is probably setting up each sensor based on it mounting position. So, outside corner sensors might have a different gain than sensors mounted in the middle of the fascia. I’m just guessing here as to what they might be doing. But again, if we capture the communications stream between the UPA and a sensors, maybe we can just play it back to trick the sensors into thinking they have been initialized.

It appears that the communications from the UPA to the sensors is 9600 baud.

Below is a screen shot from the LSA. This was taken from power-up – in other words, the LSA was trigged as the UPA was turned on (powered up). The LSA captured all the communications between the four sensors and the UPA. So, there is a 210mS power-up delay, then the “Init String” is sent, next there are two S1 S2 string (I have no idea), and then lastly the UPA goes into sensor scan mode which repeats. Note, I’m just making up my own labels for each area of the plot. The labels just help in explaining everything.

LSA Plot 1

The next plot shows the start of the Init String area expanded so that we can see the values. The LSA has the ability to decode the waveform into a RS232 stream. There are four decoders (one for each sensor) – each decoder is setup for 9600 baud / positive logic. Once captured, I can download the converted values from the LSA into a log file for later playback.

LSA Plot2

Next will be sorting out the three sections (Init String, S1, S2). Once these sections are played back, I’m hoping, the sensor will start responding to ping requests.

But first, I finally got off my butt and hooked up my oscilloscope. Good thing too. Have a close look at the following scope plot. The thing to note is, the third pulse is at a different level than the first two. Looks like, the first two quick pulses come from the UPA module to command the sensor to start a ping. The 0.85ms third pulse comes back from the sensor. I think the 0.85ms pulse is the ring-down time.



The next plot was taken with the sensor blocked. You can see there are tons of return echoes right after the ring-down. Again, note the different voltages in the pulses. The UPA pulses (at the beginning) are 0.8 volts lower than the sensor’s pulses. Very interesting. Oh, just for reference, the first grid line up, from the bottom, is ground. So, when the UPA is active on the comm wire, it pulls the wire down from 8V (nominal) to 1.5V. The sensor, when active, pulls the wire down to 2V. These are rough numbers – the scope actually shows a delta of 0.72V between the two. The really good news is now we can tell who is talking on the wire.



Another reference plot, here is the standard sensor pinout. With the sensor pinout, we can get ready to start powering up the sensor independently of the UPA module. I noticed this too, the UPA module has a pull-down on the comm line. However, the sensor has a much stronger pull-up on the comm line. Therefore, the UPA can verify the sensor is plugged in simply by sampling the comm line. If the comm line if low, the sensor is disconnected. If the comm line is high then the sensor is present. This provides a way to perform diagnostics. The UPA module probably samples the comm line before applying power to the sensors (yes, the UPA can switch power on/off to the sensors). If sensor power is off, but the comm line is high, we have a short to battery. After turning the sensors on, the UPA can sample the comm wire and detect either a missing sensor, or, a short to ground. This is the kind of info can be reported over the CAN bus to a diagnostic test tool. Very handy info for repair folks.

UPA Sensor Pinout

UPA Sensor Pinout

OK, the plan is to duplicate the UPA output into a sensor. To do that, we need a quick & easy dev. board to play with. The MBed is just perfect for such playing around. If you don’t know the MBed, pop on over to for a quick intro. It’s just a sweet little play platform. The thing to note in the following picture is the MBed is sharing the LSA probes with the UPA module. Now, to switch from the UPA to the MBed, all I have to do is load a different config file into the LSA so it uses a different set of probe pins. Sweeeeet.

MBed / Sensor I/O

I know, I know, this is taking forever to finish. Well, since I started this an MBed contest has started. I entered the contest so now I have a deadline to meet!

It’s now time to free the sensors from that old UPA module and drive them with my new MBed board!

A couple of notes about the main picture at right. The development board in the center provides a means to interface the MBed to the sensors. Each sensor channel requires a pair of interface transistors. I also hacked a UPA connector off an old module. Reusing the connector makes it easy to plug my existing sensor harness right into the development board. The GM connectors work really well.

Also note the foam block in the left side of the picture. This is soft egg carton foam. Placing a sensor nose down in the foam isolates the sensor from any return echo. In other words, it makes the sensor deaf to any echo – even if the sensor pings the echo will be lost in the foam. This foam is really handy during development.

OK, after a lot of messing around with my breadboard I came up with the circuit below. Since I’m not a real hardware guy I’m sure this could be improved. As an example, this circuit uses two micro pins for each sensor. It occurs to me that by flipping a micro pin between input / output a single IO pin could be used. However, that would require more thinking which I seem to be running low on lately.

The Tx/Rx circuit basically provides a half duplex comm channel to a sensor. The nominal bus voltage is eight volts. An inactive bus is idle at eight volts. When the micro, or sensor, wants to talk the bus is pulled low for short duration pulses.

TxRx Circuit follows:  *** August, 2014 Update – The circuit on the page is wrong!  Flow this link to the correct circuit.

UPA TxRx Circuit

MBed Development Board:

UPA TxRx MBed Board The Tx/Rx development board provides an easy connection to four sensors, has a nice socket for the MBed module, has easy probe points for the LSA, and has a handy DC power plug. OK, is that good for the dev. board? Post a comment below if something is unclear.

Now for some software. Below is a picture of the startup pulses.

Powerup Init Stuff:

UPA Init Stuff

Normal startup sequence consists of the following. There is a 78ms dwell right after power is applied. Next comes a bunches of pulses that make up an init sequence. Again, I’m guessing this sets the gain for the sensor. The two center sensors have a slightly different pattern than the outside two sensors. Finally, two reset pulses (I’m guessing they are reset pulses) are issued. After all that, the UPA started a round robin patter of pings and listen cycles. In other words, one sensor is command to ping at the exact same time another one, or two, sensors are command to listen only. This trick of pinging one sensor and using one / two other sensors to listen only allows for detection of very close object. The pinging sensor can’t detect close objects on his own because of the ring-down problem. There is a time lag between generating a ping and switching into listen mode. This lag is called “ring-down”.

Ping Types:

UPA Ping Types

After all the initialization is done the sensors are issued two types of commands. One command asks the sensor to actively ping. The second command asks the sensor to actively listen. Note, the numbers in the pulse train above are in microseconds. If the sensor detects an echo (either after the ping or while just listening) the sensor will drive the bus with the echo. It’s interesting that right after a ping the sensor drives the bus low for 970 microseconds. I’m assuming this is the ring down after a ping. Also note, there is no such event when the sensor is listening only (assuming there is no crosstalk between sensors i.e. the listening sensor is buried in the foam block). One other note, the 3C and C0 number just come from my LSA. The LSA can interpret the pulses as ASCII codes. So instead of drawing the waveform it’s easier just to note the ASCII codes (interrupted as 9600 baud signals).

Scan Pattern:

UPA Scan Pattern

The last thing to think about is a scan pattern for the sensors. The UPA used the scan pattern as shown in the picture above. The top of the table is labeled L, CL, CR, R which is short for Left, Center-Left, Center-Right, Right. This is how the sensors were arranged in the fascia. As you can see, there is only ever one sensor actively pinging at any one time. However, there is no requirement that this same pattern must be followed. As an example, it would be interesting to ping all the sensors together to see what would happen. Could longer range object be detected this way? Don’t know (yet).

Well, I could drone on and on about the source code. Or, as someone once said, “Use the force. View the source!” So below is all the source for the MBed to ping the sensor in round robin fashion.

Main.h Source Code

DigitalOut led1( LED1 );
DigitalOut led2( LED2 );
DigitalOut led3( LED3 );
DigitalOut led4( LED4 );
BusOut leds( LED1, LED2, LED3, LED4 );

// Group pins together into a bus.
BusIn senRx( p21, p22, p23, p24 );
BusOut senTx( p25, p26, p27, p28 );

// Define the pins to each sensor.
DigitalIn   senRxR( p21 );
InterruptIn senRxRIrq( p21 );
DigitalOut  senTxR( p25 );

DigitalIn   senRxCR( p22 );
InterruptIn senRxCRIrq( p22 );
DigitalOut  senTxCR( p26 );

DigitalIn   senRxCL( p23 );
InterruptIn senRxCLIrq( p23 );
DigitalOut  senTxCL( p27 );

DigitalIn   senRxL( p24 );
InterruptIn senRxLIrq( p24 );
DigitalOut  senTxL( p28 );

const uint8_t senCornerInit[] = {
    0xE0, 0xFE, 0xFE, 0xFE, 0xE0,   // 5
    0xE0, 0xFE, 0xFE, 0xFE, 0xE0,   // 10
    0xE0, 0xE0, 0xE0, 0xE0, 0xFE,   // 15
    0xE0, 0xE0, 0xE0, 0xE0, 0xFE,   // 20
    0xE0, 0xFE, 0xE0, 0xE0, 0xFE,   // 25
    0xE0, 0xE0, 0xFE, 0xE0, 0xFE,   // 30
    0xE0, 0xFE, 0xE0, 0xFE, 0xFE,   // 35
    0xE0, 0xFE, 0xE0, 0xFE, 0xFE,   // 40

    0xE0, 0xFE, 0xE0, 0xE0, 0xE0,   // 45
    0xFE, 0xE0, 0xFE, 0xFE, 0xE0,   // 50
    0xFE, 0xFE, 0xE0, 0xFE, 0xE0,   // 55
    0xFE, 0xE0, 0xE0, 0xE0, 0xE0,   // 60
    0xE0, 0xE0, 0xE0, 0xFE, 0xFE,   // 65
    0xFE, 0xE0, 0xE0, 0xFE, 0xE0,   // 70
    0xFE, 0xE0, 0xFE, 0xE0, 0xE0,   // 75
    0xFE, 0xFE

const uint8_t senMidInit[] = {
    0xE0, 0xFE, 0xFE, 0xFE, 0xE0,   // 5
    0xE0, 0xFE, 0xFE, 0xFE, 0xE0,   // 10
    0xE0, 0xE0, 0xE0, 0xE0, 0xFE,   // 15
    0xE0, 0xE0, 0xE0, 0xE0, 0xFE,   // 20
    0xE0, 0xE0, 0xFE, 0xE0, 0xFE,   // 25
    0xE0, 0xE0, 0xFE, 0xE0, 0xFE,   // 30
    0xE0, 0xFE, 0xFE, 0xFE, 0xFE,   // 35
    0xE0, 0xFE, 0xFE, 0xFE, 0xFE,   // 40

    0xE0, 0xE0, 0xE0, 0xFE, 0xE0,   // 45
    0xFE, 0xFE, 0xFE, 0xFE, 0xE0,   // 50
    0xFE, 0xFE, 0xFE, 0xFE, 0xE0,   // 55
    0xFE, 0xE0, 0xE0, 0xE0, 0xE0,   // 60
    0xE0, 0xE0, 0xE0, 0xFE, 0xFE,   // 65
    0xFE, 0xE0, 0xE0, 0xFE, 0xE0,   // 70
    0xE0, 0xFE, 0xE0, 0xE0, 0xE0,   // 75
    0xFE, 0xFE

const uint16_t pulE0[] = { 609,  838 };
const uint16_t pulFE[] = { 203, 1250 };

#define SIZE( a )   ( sizeof( a ) / sizeof( a[0] ) )

Main.c Source Code

#include "mbed.h"
#include "main.h"

#define AL  0x0F
#define AH  0x00

Serial pc( USBTX, USBRX );   // Define serial port using USB.

void updateLED( void );
void senInit( void );
void pulReset( void );
void pulPing( DigitalOut ioPin );
void pul( const uint16_t val[] );

void senRxIrqRise( void );
void senRxIrqFall( void );

Timer timer;                    // Define timer.

#define NUM_BUFF    2
#define MAX_ECHOS   32

uint32_t tmrEchos[ NUM_BUFF ][ MAX_ECHOS ];
uint8_t indEchos[ NUM_BUFF ];
uint8_t indBuff = 0, indBuffOld;

// ============================================================================
int main() {
    uint32_t i = 0, j;

    pc.baud( 115200 );

    pc.printf( "\r\nUltrasonic mbed mashup - Ver 0.1 - Nov. 15, 2010.\n" );
    wait_ms( 50 );

    senRxR.mode( PullUp );          // Setup Rx pins with pullups.
    senRxCR.mode( PullUp );
    senRxCL.mode( PullUp );
    senRxL.mode( PullUp );

    senRxRIrq.rise( &senRxIrqRise );
    senRxRIrq.fall( &senRxIrqFall );

    senTx.write( AH );              // Set all Tx pins to high state.

    wait_ms( 1000 );
    senInit( );
    pc.printf( "Init Done.\n" );

    while ( 1 ) {
        leds = 0x0F;
        senTxR = AH;
        pulReset( );
        wait_ms( 20 );
        leds = 0;

        i = 0;
        while ( ++i < 50 ) {
            indBuffOld = indBuff++;
            indBuff = indBuff >= NUM_BUFF ? 0 : indBuff;
            indEchos[ indBuff ] = 0;

            switch( i % 4 ) {
                case 0:
                    pulPing( senTxR );
                    led1 = 1;
                    if( indEchos[ indBuffOld ] ) {
                        for( j=0 ; j < indEchos[ indBuffOld ] ; j++ )
                            pc.printf( "%X ", tmrEchos[ indBuffOld ][ j ] );
                        pc.printf( "\n" );
                    while( timer.read_ms()  < 50 );
                case 1:
                    pulPing( senTxCR );
                    led2 = 1;
                    wait_ms( 50 );
                case 2:
                    pulPing( senTxCL );
                    led3 = 1;
                    wait_ms( 50 );
                case 3:
                    pulPing( senTxL );
                    led4 = 1;
                    wait_ms( 50 );
            leds = 0;

// ============================================================================
void senRxIrqRise( void ) {
    if( indEchos < MAX_ECHOS ) {
        tmrEchos[ indBuff ][ indEchos++ ] = timer.read_us();

// ============================================================================
void senRxIrqFall( void ) {
    if( indEchos < MAX_ECHOS ) {
        tmrEchos[ indEchos++ ] = timer.read_us() | 0x8000;

// ============================================================================
void updateLED( void ) {
    led2 = (senRxR == AH) ? 1 : 0;

// ============================================================================
void pulPing( DigitalOut ioPin ) {
    ioPin = AL;
    wait_us( 337 );
    ioPin = AH;
    wait_us( 356 );
    ioPin = AL;
    wait_us( 278 );
    ioPin = AH;

// ============================================================================
void pulReset( void ) {
    senTx.write( AL );
    wait_us( 1400 );
    senTx.write( AH );
    wait_us( 110 );
    senTx.write( AL );
    wait_us( 110 );
    senTx.write( AH );

    wait_ms( 6 );
    senTx.write( AL );
    wait_us( 684 );
    senTx.write( AH );

// Output one cycle.
// ============================================================================
void pul( const uint16_t val[] ) {
    senTx.write( AL );
    wait_us( val[0] );
    senTx.write( AH );
    wait_us( val[1] );

// ============================================================================
void senInit( void ) {
        uint8_t i;

        senTx.write( AL );
        wait_us( 1400 );
        senTx.write( AH );
        wait_us( 122 );

        for ( i=0 ; i < SIZE( senCornerInit ) ; i++ ) {       // Loop through array of dwell times.

            switch ( senCornerInit[i] ) {
                case 0xE0:
                    pul( pulE0 );
                case 0xFE:
                    pul(  pulFE );

        wait_ms( 11 );
        wait_ms( 23 );
        wait_ms( 45 );

Software Hacking

Well, I think we’ve beat this old dead horse about long enough.  I think this may be the last post in this series.  The attached image shows a typical ping echo response.  The event starts with a simple ping command from the MBed.  Right after the ping command, the sensor responds with the ping / ring-down.  Two milliseconds after the start of the command the sensor is now actively waiting for any echo.  The first echo comes 3.74ms (time between red & blue cursor bars in LSA plot) after the start of the ping command.  Note, you may have to click on the LSA plot to view the original image to see all the details in the image.

Well, I thinks that’s it.  There should be enough information in these posts for anyone to recreate a pinging automotive sensor(s).  As always, leave comments below for anything that is unclear.



Leave a Reply