Rd-03D mmWave Radar: Multi-Human Tracking with Distance, Speed & Positioning
Table of Contents
Rd-03D mmWave Radar with ESP32:
Rd-03D mmWave Radar With ESP32: Multi-Human Tracking with Distance, Speed & Positioning- The RD-03D mmWave Human Detection Sensor by Ai-Thinker is truly amazing. This small and affordable millimeter-wave radar sensor can do things that even expensive high-end human detection sensors cannot.
Personally, I believe this mmWave sensor will soon replace most human detection sensors. That’s because, with this sensor, you can not only detect humans but also measure their distance accurately. And it’s not just about one target; this mmWave Radar can detect up to 3 targets at the same time and measure their distances with great accuracy.
And the best part is, you have full control over to choose whether you want to use this sensor for detecting a single target or multiple targets. If you want to detect just one target, you can use the single-target detection command. And if you want to detect up to 3 targets, you can use the multi-target detection command.
This sensor doesn’t just stop there! It can also measure the angle of moving targets in addition to detecting them and calculating their distances.
This capability opens up a whole new world of possibilities for advanced tracking systems.
For example,
- It can be used to intelligently recognize and track how a person moves within an area.
- It can follow the path of their movement,
- Understand where they are heading, and
- Keep track of their position over time.
This makes it highly useful for monitoring human activity, and identifying motion patterns.
Another impressive feature is;
- Its ability to calculate the speed of a moving human by analyzing motion data.
This is incredibly useful for projects like automatic door systems, where you might want the door to respond faster if a person is running versus walking.
Additionally, with its ability to track the movement path and positions using X and Y coordinates, this sensor can play a critical role in home automation and load-controlling projects.
For instance, you could program it to turn on lights or appliances only when a person is detected in a specific area, saving energy and improving convenience.
In security projects, this mmWave radar sensor is a game-changer. It can not only detect intruders but also track their movements within the protected area in real time.
You can set it up to trigger alarms, activate cameras, or even send notifications when unauthorized movement is detected. Its ability to monitor multiple targets simultaneously ensures no movement goes unnoticed, making it perfect for advanced surveillance systems.
Overall, this millimeter-wave radar sensor is packed with features that make it ideal for various applications, from
- Smart homes and automation to
- Security and
- Tracking projects.
It’s small, affordable, and incredibly powerful; everything you would want in a modern detection system.
Compared to a traditional PIR (Passive Infrared) sensor, the RD-03D mmWave Human Detection Sensor is in a completely different league.
While a PIR sensor can only detect motion and struggles with accuracy in distance or direction, the mmWave sensor goes far beyond. It can measure the precise distance, angle, and even speed of moving targets, something a PIR sensor simply cannot do.
Plus, it can track multiple targets at the same time, while a PIR sensor is limited to detecting general motion in its field of view and can only be used indoors.
This makes the mmWave sensor a far more versatile and reliable choice for advanced automation and security systems. Because the mmWave sensor is not affected by light, you can use it both outdoors and indoors.
Now, my question is:
Will you still use a PIR sensor?
Let me know in the comments.
Today, we are going to use this powerful mmWave radar sensor with the ESP32 and let me tell you; the information and data I am going to share with you; you cannot find it anywhere else. Because, until today, no one has used this sensor with Arduino, ESP32, or ESP8266. So, do not waste your time looking for its Arduino library; just sit back, relax, and read this article.
We will practically detect single and multiple targets, and based on the measured distance and direction of movement, we will control a specific load.
So, without any further delay, let’s get started!
Amazon Links:
ESP32 WiFi + Bluetooth Module (Recommended)
*Please Note: These are affiliate links. I may make a commission if you buy the components through these links. I would appreciate your support in this way!
Rd-03D Radar Overview:
The Rd-03D is a radar module made by Shenzhen Ai-Thinker Technology Co., Ltd., using the “Artificial Internet of things” AIoT S5KM312CL chip from Iclegend.
It is a millimeter wave sensor SoC “System on a chip” based on Frequency Modulated Continuous Wave “FMCW” radar transceiver technology. It works in the 24GHz K-band with a maximum sweep bandwidth of 0.25GHz.
This module uses FMCW waveform and advanced signal processing technology combined with built-in intelligent positioning and tracking algorithm to detect multiple targets in a specific area. The Rd-03D can accurately sense human motion and other activity in the area, track movement paths, and measure the speed and distance of targets.
It can intelligently distinguish the human body in the form of movement, micro-movement, and rest. The module has good resistance to external interference and is not affected by wireless signals such as WIFI.
Rd-03D Characteristics:
Some of its key characteristics are
- It supports 24 GHz ISM frequency band.
- Features a high-performance microstrip antenna with one transmitter and two receiver “1T2R”.
- Offers accurate target tracking and positioning.
- Maximum sensing distance is 8 meters.
- The range resolution is 0.75m and the ranging accuracy is 0.15m.
- Detection range covers ±60° horizontally and ±30° vertically.
- Supports UART communication, enabling easy adjustment of radar settings via the serial port.
- Operates on a single 5V power supply.
- Suitable for wall mounting.
- Includes a tool to adjust detection range, data reporting time, and target retention.
- Compact size: 15 x 44 mm.
- Plug-and-play with default settings.
- Adopts standard 1*4P-1.25mm socket connector
Typical application scenarios are
- Smart home appliances
- Intelligent business
- Smart Security and
- Smart lighting
Rd-03D Main Parameters:
The Rd-03D model comes with a standard socket connector and features an onboard PCB antenna. It operates in the frequency range of 24G to 24.25GHz. The module is designed to work in temperatures ranging from -40°C to 85°C and can be stored at temperatures between -40°C and 125°C with less than 90% relative humidity. It requires a 5V power supply with a current of at least 200mA. The module uses a UART interface with a default data rate of 256,000 bps.
Model: Rd-03D
Package: Standard 1*4P-1.25mm socket connector
Size: 15.0*44.0(±0.2)mm
Antenna: on-board PCB antenna
Frequency: Frequency 24G-24.25GHz
Working Temperature: -40℃~85℃
Storage Temperature: -40°C~125°C, <90%RH
Power Supply: Support voltage 5V, supply current ≥200mA
Interface: UART
UART Rate: Default 256000 bps
Rd-03E Installation and Orientation:
For the precise human detection, distance measurement, direction of movement, and speed measurement. It should be installed 1.5 to 2 meters above the floor.
The radar installation azimuth is shown in Figures (a) or (b), positive and negative directions are azimuth angles, up and down directions are pitch angles, and the radar antenna plane normal direction is 0°.
The range for detecting moving humans covers ±60° horizontally and ±30° vertically, the maximum detection distance in the normal direction is 8m
Rd-03D Pin Definition:
On the backside, you can see that it has a standard socket connector, which allows us to easily connect it to any controller board without soldering any wires. Unfortunately, they forgot to send the connector, so now I will have to solder the wires to the RX, TX, GND, and 5V contacts. We are going to use only four wires to connect this sensor to the ESP32. And let me tell you:
The Operating range of TX and RX pins is 0 to 3.3 Volts; so you can safely use this Radar module with 3.3V compatible controller boards.
All the 4 wires have been soldered and now I can use these 4 wires to connect it to the ESP32.
Rd-03D Interfacing with ESP32:
Connect the 5V and GND pins of the ESP32 to the VCC and GND pins on the millimeter-wave Radar module.
Connect the TX and RX to the ESP32 GPIOs 16 and 17 respectively; I am using Serial Port 1 on the ESP32. For the connections, you can follow this circuit diagram.
About the ESP32 Development boards:
I performed my initial experiments on a breadboard. But since I also need to control a load automatically, I realized I would need a relay for that. So, I decided to create my own ESP32-based development board using Altium Designer. On this board, I added a 5V 3A power supply and a 5V SPDT relay. After designing the PCB and Generating Gerber files. I opened the ALLPCB official website and ordered 5 PCBs. For the order placement watch the video tutorial given at the end of this article.
Allpcb is not just one of the fastest PCB manufacturing companies in the world, but it also has high quality and amazing prices. For only 5 dollars, you can get single or double-sided PCBs up to 150 x 100mm in size. This is great because most companies only offer sizes up to 100x100mm. With Allpcb’s 150x100mm size, you can make multiple PCBs on one board, saving money by paying just 5 dollars!
ESP32 Development Board
After a few days, I got 5 high-quality PCBs and I am really thankful to ALLPCB for sponsoring such high-quality PCBs. The traces are clean and precise, and the silkscreen is clear and easy to read. It’s impressive how well they have managed to capture the detailed design.
Rd-03D Arduino Library:
When I reached this point, I got stuck for a while. That’s because no one had used the RD-03D mmWave Radar Module with Arduino, ESP32, ESP8266, or Raspberry Pi Pico before. I could not find any Arduino library for this sensor, not on the product’s official page and not by searching in the Arduino IDE library manager.
Then I tried searching on Google, but I still couldn’t find a library for it. So, as always, I decided to write my own code.
What I am about to explain is something you need to read and understand because it’s not guaranteed that you will always find a ready-made library for every sensor you work with. Sometimes you will come across sensors that don’t have a library available anywhere. That’s when this knowledge will really help you. So, let me explain how I tackled this.
I started off by downloading all the PDF files from the Ai-thinker product’s official page. In the datasheet I went to the communication protocol section where I confirmed the default baud rate of the Radar Serial port which is 256000 bps. I also carefully studied the Data Frame Format including information about the Frame Header, Intra-frame data, and Frame Tail.
And closely observed how the Radar outputs the detected target information, including X and Y coordinates, Target Speed, and Distance Value.
Data example: AA FF 03 00 OE 03 B1 86 10 00 68 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 CC
Target 1x coordinate: OxOE+Ox03*256= 782
0-782=-782 mm;
Target 1y coordinate: OxB1+Ox86*256 = 34481
34481-2^15= 1713 mm;
Target 1 speed: 0x10 + OxO0 * 256= 16
0-16=-16cm/s;
Target 1 Distance Resolution: Ox68+Ox01*256= 360 mm
This is an example of the data that the RD-03D radar sends when it detects a target. Now, this data might look complicated, but it’s just a set of numbers that tell us what the radar is seeing.
Here’s what the radar is saying:
It found one target, which we are calling Target 1 (that’s the blue part in the example).
For Target 2 and Target 3 (red and black), the radar didn’t detect anything, so their data is all zeros.
Now, let’s see what information we can get about Target 1:
Target 1 X Coordinate:
The X coordinate tells us how far left or right the target is.
The radar gives the value as 0x0E + 0x03 * 256.
We multiply by 256 because the data is stored in two bytes (or two numbers) for each value.
Lower byte is 0x0E (14 in decimal).
Higher byte is 0x03 (3 in decimal).
To get the full X coordinate:
Value = (3 × 256) + 14 = 782
Since it’s 0-782, the result is -782 mm, which means it’s 782 mm to the left.
Target 1 Y Coordinate:
The Y coordinate tells us how far forward or backward the target is.
The radar gives 0xB1 + 0x86 * 256, which equals 34481.
But because the radar uses a specific format, we subtract 2^15 (or 32768), and we get 1713 mm. So, the target is 1713 mm forward.
Target 1 Speed:
This is how fast the target is moving.
The radar gives 0x10 + 0x00 * 256, which equals 16.
Since it’s 0-16, the speed is -16 cm/s. A negative value just means the target is moving backward.
Target 1 Distance Resolution:
This tells us how precisely the radar measured the distance.
The radar gives 0x68 + 0x01 * 256, which equals 360 mm.
Next, using the X and Y coordinate values, we can find the target distance, and angle. I will explain this in the programming. So, this is how we take the raw data and turn it into useful information!
Before, you start the programming, make sure you have installed the ESP32 board in the Arduino IDE. You can read my getting started article on the ESP32 WiFi + Bluetooth Module.
Reading the Rd-03D data frame:
I wrote a simple program to check the data coming from the radar. As I already explained, the Rd-03D Radar module can detect either one target or multiple targets.
If you want to activate single target detection, you just need to send the Single_Target_Detection_CMD to the radar. And if you want to switch to detecting multiple targets, you send the Multi_Target_Detection_CMD instead.
Right now, as you can see, I have implemented the Single_Target_Detection_CMD to test how it works.
Rd-03D Data Frame Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
#include <Arduino.h> #define RX_PIN 16 #define TX_PIN 17 #define BAUD_RATE 256000 // Variables uint8_t RX_BUF[64] = {0}; uint8_t RX_count = 0; uint8_t RX_temp = 0; // Single-Target Detection Commands uint8_t Single_Target_Detection_CMD[12] = {0xFD, 0xFC, 0xFB, 0xFA, 0x02, 0x00, 0x80, 0x00, 0x04, 0x03, 0x02, 0x01}; // Multi-Target Detection Command uint8_t Multi_Target_Detection_CMD[12] = {0xFD, 0xFC, 0xFB, 0xFA, 0x02, 0x00, 0x90, 0x00, 0x04, 0x03, 0x02, 0x01}; void setup() { Serial.begin(115200); // Debugging Serial1.begin(BAUD_RATE, SERIAL_8N1, RX_PIN, TX_PIN); Serial1.setRxBufferSize(64); // Set buffer size Serial.println("RD-03D Radar Module Initialized"); // Send single-target detection command Serial1.write(Single_Target_Detection_CMD, sizeof(Single_Target_Detection_CMD)); // Send single-target detection command //Serial1.write(Multi_Target_Detection_CMD, sizeof(Single_Target_Detection_CMD)); delay(200); Serial.println("Single-target detection mode activated."); //Serial.println("Multi-target detection mode activated."); RX_count = 0; Serial1.flush(); } void processRadarData() { // output data printBuffer(); // Reset buffer and counter memset(RX_BUF, 0x00, sizeof(RX_BUF)); RX_count = 0; } void loop() { // Read data from Serial1 while (Serial1.available()) { RX_temp = Serial1.read(); RX_BUF[RX_count++] = RX_temp; // Prevent buffer overflow if (RX_count >= sizeof(RX_BUF)) { RX_count = sizeof(RX_BUF) - 1; } // Check for end of frame (0xCC, 0x55) if ((RX_count > 1) && (RX_BUF[RX_count - 1] == 0xCC) && (RX_BUF[RX_count - 2] == 0x55)) { processRadarData(); } } } // Function to print buffer contents void printBuffer() { Serial.print("RX_BUF: "); for (int i = 0; i < RX_count; i++) { Serial.print("0x"); if (RX_BUF[i] < 0x10) Serial.print("0"); // Add leading zero for single-digit hex values Serial.print(RX_BUF[i], HEX); Serial.print(" "); } Serial.println(); } |
This code listens for data coming from the radar sensor through Serial1 and stores it in a buffer called RX_BUF. It checks each piece of data and adds it to the buffer, keeping track of how much data has been collected using RX_count. To prevent the buffer from overflowing, it stops adding data if it reaches the buffer’s maximum size.
The code also looks for a specific pattern (0x55 followed by 0xCC) at the end of the buffer to confirm that a full message has been received. Once a complete message is detected, it calls the processRadarData() function to handle the data. There’s also a printBuffer() function that can display the collected data in a readable format for debugging purposes.
Practical Demonstration:
I have already uploaded the code. Now, you need to be mindful of the radar’s orientation when setting it up.
There are two ways to install the sensor, and in my case, I am going with option B. Just make sure the Ai-Thinker logo is at the top.
Alright, let’s open the Serial Monitor.
As you can see, we have successfully read the complete data frame from the radar.
RX_BUF: 0xAA 0xFF 0x03 0x00 0x38 0x00 0x11 0x82 0x00 0x00 0x68 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x55 0xCC
Right now, it’s only detecting one target, so the data for the other two targets shows as 0. You can see how responsive the radar is. I have already explained this data frame in detail, so I am sure there is no confusion at this point.
Multiple Targets Detection:
Next, I activated the multi-target detection mode, and the radar successfully detected two targets.
RX_BUF: 0xAA 0xFF 0x03 0x00 0x05 0x01 0x19 0x82 0x00 0x00 0x68 0x01 0xE3 0x81 0x33 0x88 0x20 0x80 0x68 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x55 0xCC
For now, I am going to focus on single target detection, because right now I am alone in my studio. And honestly, I don’t even need to detect multiple targets because my work can easily be done with single target detection. That’s because I am mainly interested in the target’s distance and angle.
Looking at this data, it’s hard to understand what’s going on.
That’s because all the values are in hexadecimal format. So, let’s first convert this data into a format that’s easy for humans to read and understand!
Modified Single Target Detection Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
#include <Arduino.h> #define RX_PIN 16 #define TX_PIN 17 #define BAUD_RATE 256000 // Variables uint8_t RX_BUF[64] = {0}; uint8_t RX_count = 0; uint8_t RX_temp = 0; // Target details int16_t target1_x = 0, target1_y = 0; int16_t target1_speed = 0; uint16_t target1_distance_res = 0; float target1_distance = 0; float target1_angle =0; // Single-Target Detection Commands uint8_t Single_Target_Detection_CMD[12] = {0xFD, 0xFC, 0xFB, 0xFA, 0x02, 0x00, 0x80, 0x00, 0x04, 0x03, 0x02, 0x01}; void setup() { Serial.begin(115200); // Debugging Serial1.begin(BAUD_RATE, SERIAL_8N1, RX_PIN, TX_PIN); Serial1.setRxBufferSize(64); // Set buffer size Serial.println("RD-03D Radar Module Initialized"); // Send single-target detection command Serial1.write(Single_Target_Detection_CMD, sizeof(Single_Target_Detection_CMD)); delay(200); Serial.println("Single-target detection mode activated."); RX_count = 0; Serial1.flush(); } void processRadarData() { // output data //printBuffer(); /* RX_BUF: 0xAA 0xFF 0x03 0x00 Header * 0x05 0x01 0x19 0x82 0x00 0x00 0x68 0x01 target1 * 0xE3 0x81 0x33 0x88 0x20 0x80 0x68 0x01 target2 * 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 target3 * 0x55 0xCC */ if (RX_count >= 32) { // Extract data for Target 1 target1_x = (RX_BUF[4] | (RX_BUF[5] << 8)) - 0x200; target1_y = (RX_BUF[6] | (RX_BUF[7] << 8)) - 0x8000; target1_speed = (RX_BUF[8] | (RX_BUF[9] << 8)) - 0x10; target1_distance_res = (RX_BUF[10] | (RX_BUF[11] << 8)); target1_distance = sqrt(pow(target1_x, 2) + pow(target1_y, 2)); target1_angle = atan2(target1_y, target1_x) * 180.0 / PI; Serial.print("Target 1 - Distance: "); Serial.print(target1_distance / 10.0); Serial.print(" cm, Angle: "); Serial.print(target1_angle); Serial.print(" degrees, X: "); Serial.print(target1_x); Serial.print(" mm, Y: "); Serial.print(target1_y); Serial.print(" mm, Speed: "); Serial.print(target1_speed); Serial.print(" cm/s, Distance Resolution: "); Serial.print(target1_distance_res); Serial.println(" mm"); // Reset buffer and counter memset(RX_BUF, 0x00, sizeof(RX_BUF)); RX_count = 0; } } void loop() { // Read data from Serial1 while (Serial1.available()) { RX_temp = Serial1.read(); RX_BUF[RX_count++] = RX_temp; // Prevent buffer overflow if (RX_count >= sizeof(RX_BUF)) { RX_count = sizeof(RX_BUF) - 1; } // Check for end of frame (0xCC, 0x55) if ((RX_count > 1) && (RX_BUF[RX_count - 1] == 0xCC) && (RX_BUF[RX_count - 2] == 0x55)) { processRadarData(); } } } // Function to print buffer contents void printBuffer() { Serial.print("RX_BUF: "); for (int i = 0; i < RX_count; i++) { Serial.print("0x"); if (RX_BUF[i] < 0x10) Serial.print("0"); // Add leading zero for single-digit hex values Serial.print(RX_BUF[i], HEX); Serial.print(" "); } Serial.println(); } |
So, as you can see, I have made a few changes to the previous code.
1 2 3 4 5 6 7 8 9 10 11 |
// Target details int16_t target1_x = 0, target1_y = 0; int16_t target1_speed = 0; uint16_t target1_distance_res = 0; float target1_distance = 0; float target1_angle =0; |
This time, I have also defined some variables specifically for the target, which will help us work with the data more easily.
1 2 3 4 5 6 7 8 9 10 11 |
target1_x = (RX_BUF[4] | (RX_BUF[5] << 8)) - 0x200; target1_y = (RX_BUF[6] | (RX_BUF[7] << 8)) - 0x8000; target1_speed = (RX_BUF[8] | (RX_BUF[9] << 8)) - 0x10; target1_distance_res = (RX_BUF[10] | (RX_BUF[11] << 8)); target1_distance = sqrt(pow(target1_x, 2) + pow(target1_y, 2)); target1_angle = atan2(target1_y, target1_x) * 180.0 / PI; |
This code extracts and calculates information about a target from a data buffer (RX_BUF). It combines two bytes to get the X and Y coordinates, then adjusts them: the X coordinate is shifted by 0x200 to center it around zero, and the Y coordinate is shifted by 0x8000 to handle negative values. The speed is calculated similarly, adjusted by 0x10 for an offset. The distance resolution is read as-is, while the total distance is found using the Pythagorean theorem to combine X and Y. Finally, the angle is calculated using the atan2 function, converting the position into degrees for easier interpretation.
Finally, using these instructions,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
Serial.print("Target 1 - Distance: "); Serial.print(target1_distance / 10.0); Serial.print(" cm, Angle: "); Serial.print(target1_angle); Serial.print(" degrees, X: "); Serial.print(target1_x); Serial.print(" mm, Y: "); Serial.print(target1_y); Serial.print(" mm, Speed: "); Serial.print(target1_speed); Serial.print(" cm/s, Distance Resolution: "); Serial.print(target1_distance_res); Serial.println(" mm"); |
We send data to the Serial Monitor.
Practical Demonstration:
Now, you can easily see the distance, angle, and other data. It might look a bit overwhelming because I am sending a lot of data to the Serial Monitor. I did this just to show you that everything is working perfectly. But let’s simplify it by not printing the X-axis, Y-axis, speed, and distance resolution.
Now it’s looking much cleaner and easier to understand. Focus on the distance and angle because this test will give us a full picture of the radar’s capabilities. Honestly, this radar isn’t just responsive – it’s impressively accurate. I have personally tested it, and it can measure distances up to 8 meters with precision while also calculating the angle accurately.
What really stands out is how reliably it measures angles. With this angle information, the possibilities are endless. For instance, you can use it to control loads in different directions based on a target’s left or right movement. Imagine automating lights, fans, or other devices just by moving in a specific direction.
To explain this,
I am going to use this 5V SPDT type relay to control a 110/220Vac bulb. This relay is connected to the ESP32 GPIO13. For the relay connections you can follow this circuit diagram.
Safety:
Remember safety first, When the 110/220Vac supply is connected, never touch the relay contacts as it can be extremely dangerous. It is important to note that when working with mains voltage, proper safety precautions should always be taken and it is advisable to consult relevant electrical codes and standards.
Controlling a Bulb based on the Human distance:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
#include <Arduino.h> #define RX_PIN 16 #define TX_PIN 17 #define BAUD_RATE 256000 // Variables uint8_t RX_BUF[64] = {0}; uint8_t RX_count = 0; uint8_t RX_temp = 0; // Target details int16_t target1_x = 0, target1_y = 0; int16_t target1_speed = 0; uint16_t target1_distance_res = 0; float target1_distance = 0; float target1_angle = 0; // Relay control int Relay = 13; int RelayFlag = 0; unsigned long relayOnTimer = 0; unsigned long relayOffTimer = 0; const unsigned long onDelay = 2000; // 2 seconds const unsigned long offDelay = 5000; // 5 seconds // Single-Target Detection Commands uint8_t Single_Target_Detection_CMD[12] = {0xFD, 0xFC, 0xFB, 0xFA, 0x02, 0x00, 0x80, 0x00, 0x04, 0x03, 0x02, 0x01}; void setup() { Serial.begin(115200); // Debugging Serial1.begin(BAUD_RATE, SERIAL_8N1, RX_PIN, TX_PIN); Serial1.setRxBufferSize(64); // Set buffer size Serial.println("RD-03D Radar Module Initialized"); // Send single-target detection command Serial1.write(Single_Target_Detection_CMD, sizeof(Single_Target_Detection_CMD)); delay(200); Serial.println("Single-target detection mode activated."); RX_count = 0; Serial1.flush(); pinMode(Relay, OUTPUT); digitalWrite(Relay, LOW); delay(1000); } void processRadarData() { if (RX_count >= 32) { // Extract data for Target 1 target1_x = (RX_BUF[4] | (RX_BUF[5] << 8)) - 0x200; target1_y = (RX_BUF[6] | (RX_BUF[7] << 8)) - 0x8000; target1_speed = (RX_BUF[8] | (RX_BUF[9] << 8)) - 0x10; target1_distance_res = (RX_BUF[10] | (RX_BUF[11] << 8)); target1_distance = sqrt(pow(target1_x, 2) + pow(target1_y, 2)) / 10.0; target1_angle = atan2(target1_y, target1_x) * 180.0 / PI; Serial.print("Distance: "); Serial.print(target1_distance); Serial.print(" cm, Angle: "); Serial.print(target1_angle); Serial.println(" degrees"); // Relay logic unsigned long currentMillis = millis(); if (target1_distance > 100 && target1_distance < 280) { // If within range, check delay before turning on relay if (RelayFlag == 0) { if (relayOnTimer == 0) { relayOnTimer = currentMillis; // Start timer } else if (currentMillis - relayOnTimer >= onDelay) { digitalWrite(Relay, HIGH); RelayFlag = 1; Serial.println("Relay ON"); } } } else if (target1_distance > 280) { // If out of range, check delay before turning off relay if (RelayFlag == 1) { if (relayOffTimer == 0) { relayOffTimer = currentMillis; // Start timer } else if (currentMillis - relayOffTimer >= offDelay) { digitalWrite(Relay, LOW); RelayFlag = 0; Serial.println("Relay OFF"); } } } else { // Reset timers if target is not in a valid range relayOnTimer = 0; relayOffTimer = 0; } // Reset buffer and counter memset(RX_BUF, 0x00, sizeof(RX_BUF)); RX_count = 0; } } void loop() { // Read data from Serial1 while (Serial1.available()) { RX_temp = Serial1.read(); RX_BUF[RX_count++] = RX_temp; // Prevent buffer overflow if (RX_count >= sizeof(RX_BUF)) { RX_count = sizeof(RX_BUF) - 1; } // Check for end of frame (0xCC, 0x55) if ((RX_count > 1) && (RX_BUF[RX_count - 1] == 0xCC) && (RX_BUF[RX_count - 2] == 0x55)) { processRadarData(); } } } |
Code Explanation:
I modified this code slightly to add relay control based on the radar’s target detection. Most of the code remains the same, but here’s what I added.
1 2 3 |
int Relay = 13; int RelayFlag = 0; |
First, I defined the relay pin (Relay = 13) and a flag (RelayFlag) to track whether the relay is ON or OFF. The flag helps prevent unnecessary toggling of the relay.
1 2 3 4 5 6 7 |
unsigned long relayOnTimer = 0; unsigned long relayOffTimer = 0; const unsigned long onDelay = 2000; // 2 seconds const unsigned long offDelay = 5000; // 5 seconds |
I also introduced two timers (relayOnTimer and relayOffTimer) to handle delays for turning the relay ON or OFF. These timers ensure that the relay only activates after a target stays in range for 2 seconds and deactivates 5 seconds after the target moves out of range. This avoids false triggering caused by momentary detection glitches.
Here’s how it works:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
if (target1_distance > 100 && target1_distance < 280) { // If within range, check delay before turning on relay if (RelayFlag == 0) { if (relayOnTimer == 0) { relayOnTimer = currentMillis; // Start timer } else if (currentMillis - relayOnTimer >= onDelay) { digitalWrite(Relay, HIGH); RelayFlag = 1; Serial.println("Relay ON"); } } } |
when the target is within the set distance range (100–280 cm), the code starts the relayOnTimer. If the target stays in range for 2 seconds, the relay turns ON, and the RelayFlag updates to indicate the relay is active.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
else if (target1_distance > 280) { // If out of range, check delay before turning off relay if (RelayFlag == 1) { if (relayOffTimer == 0) { relayOffTimer = currentMillis; // Start timer } else if (currentMillis - relayOffTimer >= offDelay) { digitalWrite(Relay, LOW); RelayFlag = 0; Serial.println("Relay OFF"); } } } |
Similarly, if the target moves out of range (distance > 280 cm), the relayOffTimer starts. After 5 seconds, the relay turns OFF, and the flag resets.
1 2 3 4 5 |
// Reset timers if target is not in a valid range relayOnTimer = 0; relayOffTimer = 0; |
The timers are reset if the target isn’t consistently in or out of range, ensuring smooth and accurate control. This setup keeps the system responsive and reliable for load control. Let’s watch this in action.
Practical Demonstration:
As you can see, the bulb is ON because my distance from the radar is within the set limit. The light will stay ON as long as I am in this area.
The radar’s algorithm is so smart that even if you are not moving, it can still detect you. This means it can sense even very small movements, called micro-movements. Some sensors fail to detect a person when they are completely still, but that’s not the case with the RD-03D mmWave radar.
It can intelligently pick up even the tiniest movements. For example, I have been in this area for a while now, and the bulb hasn’t turned OFF even once, nor has there been any false triggering.
I also tested this while sitting completely still on a chair, and the sensor worked perfectly. As I mentioned, the bulb will stay ON as long as I am in this area, whether I move forward, backward, left, or right, as long as my distance is within the set limit. The bulb will only turn OFF when my distance increases and I leave the area. For example, if I go to another room, the light will turn OFF automatically.
As you saw, the light just turned OFF. Now it will stay OFF until I return to this room. This feature makes it perfect for automating loads in bathrooms, staircases, lawns, and even for security purposes. You can confidently use this radar both indoors and outdoors.
Now, let’s control the load based on the target’s angle!
Controlling a Bulb based on the Human angle/position:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
#include <Arduino.h> #define RX_PIN 16 #define TX_PIN 17 #define BAUD_RATE 256000 // Variables uint8_t RX_BUF[64] = {0}; uint8_t RX_count = 0; uint8_t RX_temp = 0; // Target details int16_t target1_x = 0, target1_y = 0; int16_t target1_speed = 0; uint16_t target1_distance_res = 0; float target1_distance = 0; float target1_angle = 0; // Relay control int Relay = 13; int RelayFlag = 0; unsigned long relayOnTimer = 0; unsigned long relayOffTimer = 0; const unsigned long onDelay = 2000; // 2 seconds const unsigned long offDelay = 5000; // 5 seconds // Single-Target Detection Commands uint8_t Single_Target_Detection_CMD[12] = {0xFD, 0xFC, 0xFB, 0xFA, 0x02, 0x00, 0x80, 0x00, 0x04, 0x03, 0x02, 0x01}; void setup() { Serial.begin(115200); // Debugging Serial1.begin(BAUD_RATE, SERIAL_8N1, RX_PIN, TX_PIN); Serial1.setRxBufferSize(64); // Set buffer size Serial.println("RD-03D Radar Module Initialized"); // Send single-target detection command Serial1.write(Single_Target_Detection_CMD, sizeof(Single_Target_Detection_CMD)); delay(200); Serial.println("Single-target detection mode activated."); RX_count = 0; Serial1.flush(); pinMode(Relay, OUTPUT); digitalWrite(Relay, LOW); delay(1000); } void processRadarData() { if (RX_count >= 32) { // Extract data for Target 1 target1_x = (RX_BUF[4] | (RX_BUF[5] << 8)) - 0x200; target1_y = (RX_BUF[6] | (RX_BUF[7] << 8)) - 0x8000; target1_speed = (RX_BUF[8] | (RX_BUF[9] << 8)) - 0x10; target1_distance_res = (RX_BUF[10] | (RX_BUF[11] << 8)); target1_distance = sqrt(pow(target1_x, 2) + pow(target1_y, 2)) / 10.0; target1_angle = atan2(target1_y, target1_x) * 180.0 / PI; Serial.print("Distance: "); Serial.print(target1_distance); Serial.print(" cm, Angle: "); Serial.print(target1_angle); Serial.println(" degrees"); // Relay logic based on angle unsigned long currentMillis = millis(); if (target1_angle > 60 && target1_angle < 95) { // If angle is in range, check delay before turning on relay if (RelayFlag == 0) { if (relayOnTimer == 0) { relayOnTimer = currentMillis; // Start timer } else if (currentMillis - relayOnTimer >= onDelay) { digitalWrite(Relay, HIGH); RelayFlag = 1; Serial.println("Relay ON"); } } relayOffTimer = 0; // Reset off timer if target is in range } else { // If angle is out of range, check delay before turning off relay if (RelayFlag == 1) { if (relayOffTimer == 0) { relayOffTimer = currentMillis; // Start timer } else if (currentMillis - relayOffTimer >= offDelay) { digitalWrite(Relay, LOW); RelayFlag = 0; Serial.println("Relay OFF"); } } relayOnTimer = 0; // Reset on timer if target is out of range } // Reset buffer and counter memset(RX_BUF, 0x00, sizeof(RX_BUF)); RX_count = 0; } } void loop() { // Read data from Serial1 while (Serial1.available()) { RX_temp = Serial1.read(); RX_BUF[RX_count++] = RX_temp; // Prevent buffer overflow if (RX_count >= sizeof(RX_BUF)) { RX_count = sizeof(RX_BUF) - 1; } // Check for end of frame (0xCC, 0x55) if ((RX_count > 1) && (RX_BUF[RX_count - 1] == 0xCC) && (RX_BUF[RX_count - 2] == 0x55)) { processRadarData(); } } } |
I again modified the code and this time, the relay turns ON when the angle is between 60° and 95° and turns OFF when the target’s angle moves out of this range. Let’s watch this in action.
Practical Demonstration:
This time, the light will stay ON as long as I remain in this area. The light will only turn OFF if I move towards the bathroom or go to the other side of the table.
For this demonstration, I am using a single bulb, but you can use multiple bulbs too.
For example, when you’re in the center of the area, one light can turn ON; when you move to the left side, another light can turn ON; and when you move to the right side, a different light can turn ON.
You can even control a ceiling fan or control the direction of lights on a staircase based on the target’s movement.
Multiple Targets Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
#include <Arduino.h> #define RX_PIN 16 #define TX_PIN 17 #define BAUD_RATE 256000 // Variables uint8_t RX_BUF[64] = {0}; uint8_t RX_count = 0; uint8_t RX_temp = 0; // Target details int16_t target1_x = 0, target1_y = 0; int16_t target1_speed = 0; uint16_t target1_distance_res = 0; int16_t target2_x = 0, target2_y = 0; int16_t target2_speed = 0; uint16_t target2_distance_res = 0; int16_t target3_x = 0, target3_y = 0; int16_t target3_speed = 0; uint16_t target3_distance_res = 0; // Single-Target Detection Command uint8_t Single_Target_Detection_CMD[12] = {0xFD, 0xFC, 0xFB, 0xFA, 0x02, 0x00, 0x80, 0x00, 0x04, 0x03, 0x02, 0x01}; // Multi-Target Detection Command uint8_t Multi_Target_Detection_CMD[12] = {0xFD, 0xFC, 0xFB, 0xFA, 0x02, 0x00, 0x90, 0x00, 0x04, 0x03, 0x02, 0x01}; void setup() { Serial.begin(115200); // Debugging Serial1.begin(BAUD_RATE, SERIAL_8N1, RX_PIN, TX_PIN); Serial1.setRxBufferSize(64); // Set buffer size Serial.println("RD-03D Radar Module Initialized"); // Send multi-target detection command Serial1.write(Multi_Target_Detection_CMD, sizeof(Multi_Target_Detection_CMD)); delay(200); Serial.println("Multi-target detection mode activated."); RX_count = 0; Serial1.flush(); } void processRadarData() { // output data //printBuffer(); /* RX_BUF: 0xAA 0xFF 0x03 0x00 Header * 0x05 0x01 0x19 0x82 0x00 0x00 0x68 0x01 target1 * 0xE3 0x81 0x33 0x88 0x20 0x80 0x68 0x01 target2 * 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 target3 * 0x55 0xCC */ if (RX_count >= 32) { // Extract data for Target 1 target1_x = (RX_BUF[4] | (RX_BUF[5] << 8)) - 0x200; target1_y = (RX_BUF[6] | (RX_BUF[7] << 8)) - 0x8000; target1_speed = (RX_BUF[8] | (RX_BUF[9] << 8)) - 0x10; target1_distance_res = (RX_BUF[10] | (RX_BUF[11] << 8)); float target1_distance = sqrt(pow(target1_x, 2) + pow(target1_y, 2)); float target1_angle = atan2(target1_y, target1_x) * 180.0 / PI; Serial.print("Target 1 - Distance: "); Serial.print(target1_distance / 10.0); Serial.print(" cm, Angle: "); Serial.print(target1_angle); Serial.print(" degrees, X: "); Serial.print(target1_x); Serial.print(" mm, Y: "); Serial.print(target1_y); Serial.print(" mm, Speed: "); Serial.print(target1_speed); Serial.print(" cm/s, Distance Resolution: "); Serial.print(target1_distance_res); Serial.println(" mm"); // Extract data for Target 2 target2_x = (RX_BUF[12] | (RX_BUF[13] << 8)) - 0x200; target2_y = (RX_BUF[14] | (RX_BUF[15] << 8)) - 0x8000; target2_speed = (RX_BUF[16] | (RX_BUF[17] << 8)) - 0x10; target2_distance_res = (RX_BUF[18] | (RX_BUF[19] << 8)); float target2_distance = sqrt(pow(target2_x, 2) + pow(target2_y, 2)); float target2_angle = atan2(target2_y, target2_x) * 180.0 / PI; Serial.print("Target 2 - Distance: "); Serial.print(target2_distance / 10.0); Serial.print(" cm, Angle: "); Serial.print(target2_angle); Serial.print(" degrees, X: "); Serial.print(target2_x); Serial.print(" mm, Y: "); Serial.print(target2_y); Serial.print(" mm, Speed: "); Serial.print(target2_speed); Serial.print(" cm/s, Distance Resolution: "); Serial.print(target2_distance_res); Serial.println(" mm"); // Extract data for Target 3 target3_x = (RX_BUF[20] | (RX_BUF[21] << 8)) - 0x200; target3_y = (RX_BUF[22] | (RX_BUF[23] << 8)) - 0x8000; target3_speed = (RX_BUF[24] | (RX_BUF[25] << 8)) - 0x10; target3_distance_res = (RX_BUF[26] | (RX_BUF[27] << 8)); float target3_distance = sqrt(pow(target3_x, 2) + pow(target3_y, 2)); float target3_angle = atan2(target3_y, target3_x) * 180.0 / PI; Serial.print("Target 3 - Distance: "); Serial.print(target3_distance / 10.0); Serial.print(" cm, Angle: "); Serial.print(target3_angle); Serial.print(" degrees, X: "); Serial.print(target3_x); Serial.print(" mm, Y: "); Serial.print(target3_y); Serial.print(" mm, Speed: "); Serial.print(target3_speed); Serial.print(" cm/s, Distance Resolution: "); Serial.print(target3_distance_res); Serial.println(" mm"); // Reset buffer and counter memset(RX_BUF, 0x00, sizeof(RX_BUF)); RX_count = 0; } } void loop() { // Read data from Serial1 while (Serial1.available()) { RX_temp = Serial1.read(); RX_BUF[RX_count++] = RX_temp; // Prevent buffer overflow if (RX_count >= sizeof(RX_BUF)) { RX_count = sizeof(RX_BUF) - 1; } // Check for end of frame (0xCC, 0x55) if ((RX_count > 1) && (RX_BUF[RX_count - 1] == 0xCC) && (RX_BUF[RX_count - 2] == 0x55)) { processRadarData(); } } } // Function to print buffer contents void printBuffer() { Serial.print("RX_BUF: "); for (int i = 0; i < RX_count; i++) { Serial.print("0x"); if (RX_BUF[i] < 0x10) Serial.print("0"); // Add leading zero for single-digit hex values Serial.print(RX_BUF[i], HEX); Serial.print(" "); } Serial.println(); } |
Read my articles on
I will also write another article about controlling strip lights and servos based on the human position or angle. Consider subscribing if you don’t want to miss that article and video. So, that’s all for now.
It is very nice explanation. I learned something about radar mmVave sensor
Thank you very much!
Very useful sensor