How To Hack Cheap Car Parking Sensors With Arduino and an Oscilloscope

Written by nuralem | Published 2024/02/16
Tech Story Tags: robotics | arduino | pwm-signals | ultrasonic-sensors | reverse-engineering | sensor-data-decoding | how-to-hack-parking-sensors | robotics-project-for-beginners

TLDR Learn how to decode data from car parking sensors by reverse engineering cheap ultrasonic sensors. This comprehensive guide provides step-by-step instructions, Arduino sketches, and insights into understanding PWM signals, making it perfect for robotics enthusiasts and engineers. via the TL;DR App

Hello Hackers! Today we have a really interesting topic.

Searching the internet, I didn't find any simple solution on "How to read data from car parking sensors," so I decided to write a short and easy-to-understand article to help robotics engineers reverse engineer the cheapest waterproof ultrasonic sensors on the market.

What we need

  1. Cheapest Chinese sensors that you can find on the market (Alibaba, Aliexpress, Taobao, Amazon, etc.)

  2. 12-volt power supply (Picture 2);

  3. Arduino board with 5-volt logic (skip ARM versions with 3.3 volts);

  4. Any oscilloscope or logic analyzer;

  5. Computer with Arduino IDE.

Basics

First, we need to disassemble the sensor case (Picture 5).

We need to connect the black wire (pink dot) to “-” and the red wire (blue dot) to “+” of our 12-volt power supply. The green dot (GND) is connected to the GND of the Arduino board, and the red dot (DA) to any PWM-supported PIN of the Arduino board—PIN 3 in my case. As you can see, we have 4 connectors to our sensors. We will not connect them because we need to find each sensor's address; it will be easy to detect them in that case (Picture 6).

Before we download the Arduino sketch, we need to gather some information about the PWM signal used to send data to the parking display:

  • The type of PWM signal (LOW/HIGH);
  • The packet length;
  • The number of packets sent (logically, it is 4, each packet corresponds to each sensor output in our case, there are 4 sensors);
  • The duration between packets;
  • The duration of the start bit that separates each packet from each other;
  • The duration of the LOW and HIGH bits of each packet;

To get all this necessary information, we will use an oscilloscope. Connect it to the "GND" (green dot) and "DA" (red dot) pins, and don't forget to power on the sensor's board and Arduino.

Finally, we received 4 different packets. Don't panic if you don't understand, I will describe the first packet in detail with an explanation.

From this picture, we see that this is a HIGH logic PWM signal because the default value is 0 volts in our pin, and then it rises to 5 volts (Pink arrow).

Next, we see the start bit - marked as green ST (the longest signal).

We also see the body of the packet. I have marked only the high length of each signal with red and blue colors. Red represents a long signal (logic 1), while blue represents a short signal (logic 0).

The duration of the long, short, and start signals we will measure later. From this screen, we only need to extract the real binary data from each packet and write it down.

We got this data:

packet 1. 1000 1010 1111 1111 0

packet 2. 1000 1001 1111 1111 0

packet 3. 1000 1000 1111 1111 0

packet 4. 1000 1011 1111 1111 0

Next, we need to measure the duration of short, long, start bit, and time duration between the packets.

As you can see from the picture, we see green lines, that measure the duration. In our case T = 220uS. It is the duration of the long bit.

Finally, we got interesting data:

  1. The type of PWM signal is HIGH;

  2. The packet length is 2 bytes or 16 bits;

  3. The number of packets that sends is 4;

  4. The time between packets is 40mS;

  5. The duration of the start bit is 1.02mS;

  6. The duration of LOW is 80nS and the HIGH bit is 220nS;

Now we are ready to code a sketch.

Arduino Sketch

// our pin that supports PWM
int inputPin = 3;

// our packet, where we will store our 16bits information about sensor.
unsigned int packet = 0;

// our bit duration, for short = 80nS, long = 220nS and start bit = 1.02mS
unsigned long pulseDuration = 0;

void setup() {
  pinMode(inputPin, INPUT);
  Serial.begin(2000000);
}

void loop() {

  // pulseIn is a build-in function, that waits for HIGH signal. 
  // We have defined that our logic is PWM-HIGH, that's why we are using HIGH. 
  // 20.000 is a timeout of waiting for signal.
  pulseDuration = pulseIn(inputPin, HIGH, 20000);

  // We detect the start packet pulse. In our case the start bit is 1.02mS,
  // so it is between these values:
  if (pulseDuration > 900 && pulseDuration < 1040) {
    // We have successfully detected start bit, now we need to read the body of the packet.
    packet = 0;
    // Only read 16 bits of data.
    for (int i = 15; i >= 0; i--) {
      // Read packet (only HIGH bits, zeros are ignored, because we already have it)
      pulseDuration = pulseIn(inputPin, HIGH, 3000);

      // we detect only LONG bits with 220nS duration (logical 1);
      if (pulseDuration > 210 && pulseDuration < 250) {
        bitSet(packet, i);
      }
    
    }
    // Print the packet;
    Serial.println(packet, BIN);
  }  
}

We got this output from Arduino IDE. Now, we can compare it with our investigated values from our oscilloscope:

1000101111111111
1000100111111111
1000101011111111
1000100011111111

Great! We have verified that the code is working fine and we got the same values from the oscilloscope.

Our packet is divided into 2 parts. First 8 bits is the address of the sensor. The last 8 bits is the measured distance.

10001000 11111111
10001001 11111111
10001010 11111111
10001000 11111111

As we remember, we didn’t connect the sensors to board, that’s why we are receiving “11111111”.

We can change a code and split the address and values using this logic:

// We delete last 8 bits of our packet to get the address value; 
byte sensorAddressCheck = (byte)(packet >> 8);
// Using logical operation AND we remove first 8 bits;
byte sensorValueCheck = (byte)(packet & B11111111);

Now we have this code:

int inputPin = 3;
unsigned int packet = 0;

byte sensorValue = 0;
byte sensorAddress = 0;

unsigned long pulseDuration = 0;

void setup() {
  pinMode(inputPin, INPUT);
  Serial.begin(2000000);
}

void loop() {
  pulseDuration = pulseIn(inputPin, HIGH, 20000);

  if (pulseDuration > 900 && pulseDuration < 1040) {

    sensorAddress = 0;
    sensorValue = 0;
    packet = 0;

    for (int i = 15; i >= 0; i--) {
      pulseDuration = pulseIn(inputPin, HIGH, 3000);

      if (pulseDuration > 210 && pulseDuration < 250) {
        bitSet(packet, i);
      }
    
    }
    byte sensorAddressCheck = (byte)(packet >> 8);
    byte sensorValueCheck = (byte)(packet & B11111111);

    Serial.print("Sensor addr: ");
    Serial.print(sensorAddressCheck, BIN);
    Serial.print(" value: ");
    Serial.println(sensorValueCheck);

  }  
}

Arduino output:

Sensor addr: 10001011 value: 255
Sensor addr: 10000001 value: 80
Sensor addr: 10000010 value: 82
Sensor addr: 10001000 value: 255

Great Job!

Congrats! You have successfully reversed the PWM signal and I hope that it will help you in your robotics beginnings! If you want to know the distance in centimeters, additional work should be done. The general process is simple: You can put the roulette on the floor and take a box.

A little creative work and you will get a correlation table between 0-255 values from the sensor with your roulette. Thanks for your time!

PS. This work is part of a big project (Picture 7). If you want to learn more, subscribe to my channel! Thanks!


Written by nuralem | Software engineer
Published by HackerNoon on 2024/02/16