The mmWave C4001 is a 24 GHz radar-based sensor for human detection. A key advantage of the C4001 over common PIR sensors is the ability to detect static presence. PIR sensors only react to motion, while the C4001 can detect micro-movements such as breathing. It is also more resistant to environmental factors such as light, temperature, and dust.
The sensor supports motion detection up to 25 meters and human presence detection up to 16 meters. It provides UART and digital outputs for easy integration with microcontrollers. In this tutorial you will learn how to connect the mmWave C4001 sensor to an Arduino or an ESP32. We will use it for motion detection and to measure the distance and speed of objects.
Required Parts
You can get the mmWave C4001 Sensor at Amazon or DFRobot. You also will need an Arduino or an ESP32. I am using an Arduino UNO and an ESP32 Lite in this tutorial but any other Arduino, ESP32 or ESP8266 will work as well. Finally, a breadboard and some Dupont cables for wiring will be useful.

mmWave C4001 Radar Sensor

ESP32 lite

USB C Cable

Arduino Uno

USB Cable for Arduino UNO

Dupont Wire Set

Breadboard
Makerguides is a participant in affiliate advertising programs designed to provide a means for sites to earn advertising fees by linking to Amazon, AliExpress, Elecrow, and other sites. As an Affiliate we may earn from qualifying purchases.
Hardware of the mmWave C4001 Sensor
The C4001 sensor is based on 24 GHz millimeter-wave radar technology. It uses FMCW (Frequency-Modulated Continuous Wave) modulation to measure distance and motion. The sensor continuously transmits a frequency-modulated signal and analyzes the reflected waveform.
The frequency difference between transmitted and received signals is used to calculate distance. The Doppler shift is used to detect movement and velocity. This allows the sensor to detect both large movements and very small motions such as breathing.
In contrast to PIR sensors this sensing method does not rely on infrared radiation. It works independently of ambient light and temperature conditions. The picture below shows the back and front of the mmWave C4001 Sensor module. You can see the golden radar antennas at the front of the board.

Detection Capabilities
The sensor supports multiple measurement modes in parallel. It can detect human presence, motion, distance, and speed. The maximum motion detection range is up to 25 meters. Human presence detection is reliable up to around 16 meters.
The distance measurement range starts at approximately 1.2 meters and extends to 25 meters. The velocity detection range is from 0.1 m/s to 10 m/s.
The radar beam has a field of view of about 100° horizontally and 40° vertically. This allows wide-area coverage with a single sensor.

Electrical Characteristics
The C4001 operates with a supply voltage (VIN) of either 3.3 V or 5 V. This makes it compatible with common microcontrollers such as Arduino and ESP32.
The default serial communication baud rate is 9600. The sensor provides both UART communication and a digital output pin. The UART interface allows access to detailed measurement data such as distance and speed. The digital output can be used for simple presence detection signals.
The module includes pins for power (VIN), ground (GND), serial transmit (TX), serial receive (RX), and a digital output signal (OUT). The picture below shows the pinout of the module:

Signal Processing and Output
The sensor processes radar reflections internally and outputs structured data. Over UART, it provides real-time information about detected targets. This includes presence status, motion state, distance, and velocity.
The digital output pin provides a high or low signal based on detection status. This is useful for simple trigger-based applications. The internal processing reduces the need for complex signal analysis on the microcontroller.
Environmental Performance
The C4001 has strong resistance to environmental interference. It is not affected by lighting conditions, dust, humidity, or temperature changes.
The operating temperature range is from −40 °C to 85 °C. This allows use in both indoor and outdoor environments.
The sensor maintains stable performance in conditions where infrared sensors typically fail. This includes warm environments or scenarios with minimal human movement.
Technical Specification
The following table summarizes the Technical Specification of the mmWave C4001 Sensor.
| Parameter | Value |
|---|---|
| Operating Voltage | 3.3 V / 5 V |
| Operating Current | 10 mA (average), 100 mA (peak) |
| Operating Frequency | 24 GHz |
| Modulation Type | FMCW (Frequency-Modulated Continuous Wave) |
| Motion Detection Range | Up to 25 m |
| Human Presence Detection Range | Up to 16 m |
| Distance Measurement Range | 1.2 m to 25 m |
| Velocity Measurement Range | 0.1 m/s to 10 m/s |
| Beam Angle | 100° (horizontal) × 40° (vertical) |
| Communication Interfaces | UART, Digital I/O |
| Default Baud Rate | 9600 |
| Operating Temperature | −40 °C to 85 °C |
| Module Dimensions | 26 mm × 30 mm |
Using the mmWave C4001 Sensor without Microcontroller
You need to use a microcontroller (or computer) to program specific settings of the mmWave C4001 Sensor. For instance, you can set the detection distance or the detection sensitivity. But once, configured you can use the C4001 without a microcontroller.
The wiring diagram below shows you how to connect an LED the lights up, if the sensor detects a person. You simply need to provide power (3.3…5V) to the VIN and GND pins and then can connect an LED with a resistor to the OUP pin:

For most practical applications you probably want to connect a Relay instead of an LED to switch a higher voltage or current device. This is possible but you will need to use a Relay Module with an integrated amplifier circuit, since the OUT pin can’t drive the relay coil directly. Below is an example circuit:

For more information about relay modules see the How To Use A Relay With Arduino, Interfacing a Relay Module With ESP32 and the Control AC devices with Solid State Relay tutorials.
In the next sections I will show you how to connect the mmWave C4001 Sensor to an Arduino or an ESP32. Once configured, you can decided if you want to use the sensor with or without a microcontroller. For more complex control tasks, you will need the microcontroller.
Connecting the mmWave C4001 Sensor to Arduino
Connecting the mmWave C4001 Sensor to an Arduino UNO is easy. Start by connecting VIN to 5V or 3.3V of the Arduino. Next connect GND to GND. Finally, we connect the UART interface by wiring RX to GPIO 5 and TX to GPIO 4. The picture below shows the complete wiring:

For convenience here a table with the connections you need to make:
| C4001 | Arduino Uno |
|---|---|
| VIN | 5V or 3.3V |
| GND | GND |
| RX | GPIO 5 |
| TX | GPIO 4 |
You won’t even need a breadboard to connect the sensor to an Arduino UNO. Four Dupont cables are sufficient. The photo below shows my wiring:

Connecting the mmWave C4001 Sensor to ESP32
The ESP32 has three serial interfaces and you can configure the pins and interface you want to use. Here I connect TX to GPIO 17 and RX to GPIO 16. We will need to remember this when writing the code. Finally, we connect VIN to 3.3V and GND to G. The picture below shows the complete wiring:

For convenience here a table with the connections you need to make:
| C4001 | ESP32 Lite |
|---|---|
| VIN | 3.3V |
| GND | G |
| RX | 17 |
| TX | 16 |
Installing the DFRobot_C4001 library
Before we can write any for motion detection, we will need to install the DFRobot_C4001 library. To install this library go to the DFRobot_C4001 repo, click on the green “<> Code” button and then “Download ZIP” to download the library as a ZIP file as shown below:

Then create a new Arduino Sketch, go to Sketch -> Include Library -> Add .ZIP Library … to install the downloaded ZIP library (DFRobot_C4001-master.zip):

Code Example: Motion Detection with C4001
The following code examples initializes the C4001 radar sensor over a UART interface and waits until the sensor is detected. It then configures the sensor to operate in speed mode and enables micro-motion detection for higher sensitivity. In the main loop, it continuously checks for detected targets and reads their distance and speed. When a target is present, the measured values are sent to the serial monitor.
// Libraries:
// - DFRobot_C4001 V 1.0.0
// https://github.com/DFRobot/DFRobot_C4001
// - ESP32 Core V 3.3.8
#include "DFRobot_C4001.h"
#if defined(ARDUINO_AVR_UNO) || defined(ESP8266)
SoftwareSerial mySerial(4, 5);
DFRobot_C4001_UART radar(&mySerial, 9600);
#elif defined(ESP32)
DFRobot_C4001_UART radar(&Serial2, 9600, /*rx*/ 17, /*tx*/ 16);
#endif
void setup() {
Serial.begin(115200);
while (!radar.begin()) {
Serial.println("Can't find sensor");
delay(1000);
}
radar.setSensorMode(eExitMode);
radar.setDetectionRange(/*min*/ 30, /*max*/ 1000, /*trig*/ 1000);
radar.setTrigSensitivity(1); // 0-9
radar.setKeepSensitivity(2); // 0-9
radar.setDelay(/*trig*/ 0, /*keep*/ 4);
radar.setPwm(/*pwm1*/ 50, /*pwm2*/ 0, /*timer*/ 10);
radar.setIoPolaity(1);
}
void loop() {
if (radar.motionDetection()) {
Serial.println("Motion detected");
}
delay(100);
}
Include Library
The code starts by including the DFRobot_C4001.h library for the sensor. It contains the class definitions and communication functions for the sensor.
#include "DFRobot_C4001.h"
Platform-Specific Configuration
The code supports both Arduino Uno, ESP8266, and ESP32. It uses conditional compilation to select the correct serial interface. For Arduino Uno and ESP8266, a software-based serial port is used. This is required because these boards have limited hardware UART interfaces.
#if defined(ARDUINO_AVR_UNO) || defined(ESP8266) SoftwareSerial mySerial(4, 5); DFRobot_C4001_UART radar(&mySerial, 9600);
Pin 4 is configured as RX and pin 5 as TX. The sensor communicates at a baud rate of 9600.
For the ESP32, a hardware UART is used instead. This improves reliability and performance.
#elif defined(ESP32) DFRobot_C4001_UART radar(&Serial2, 9600, /*rx*/ 17, /*tx*/ 16); #endif
The ESP32 is configured to use Serial2 with GPIO17 as RX and GPIO16 as TX. You can specify other pins here.
Object Initialization
The DFRobot_C4001_UART object represents the radar sensor. It handles all communication and configuration. The constructor takes a serial interface and a baud rate. On the ESP32, additional parameters define the RX and TX pins.
Setup Function
The setup function initializes serial communication and configures the sensor.
void setup() {
Serial.begin(115200);
The main serial interface is started at 115200 baud. This is used for debugging output at the Serial Monitor.
while (!radar.begin()) {
Serial.println("Can't find sensor");
delay(1000);
}
The radar.begin() function initializes communication with the sensor. It checks if the module responds correctly. If the sensor is not detected, the code prints an error message and retries every second.
Sensor Mode
The sensor mode defines how detection events are handled.
radar.setSensorMode(eExitMode);
The eExitMode is a trigger-based mode. The sensor generates an output when motion is detected and resets after a defined condition. This mode is optimized for simple motion detection applications.
Other modes can provide continuous data, but exit mode focuses on event-based detection.
Detection Range Settings
The detection range defines where the sensor actively detects motion.
radar.setDetectionRange(/*min*/ 30, /*max*/ 1000, /*trig*/ 1000);
The minimum range is set to 30 cm. Objects closer than this distance are ignored. The maximum range is set to 1000 cm. Objects beyond this distance are ignored.
The trigger range defines the distance at which detection events are generated. In this case, it is equal to the maximum range, so the full detection zone is active.
Sensitivity Configuration
The sensor provides two types of sensitivity settings.
radar.setTrigSensitivity(1); // 0-9
The trigger sensitivity controls how easily motion is detected. A low value such as 1 reduces sensitivity and helps avoid false positives.
radar.setKeepSensitivity(2); // 0-9
The keep sensitivity controls how the sensor maintains detection after motion is detected. A slightly higher value ensures that small movements keep the detection active.
These two parameters allow fine-tuning between responsiveness and stability.
Timing Configuration
The timing settings define how quickly the sensor reacts and how long it stays active.
radar.setDelay(/*trig*/ 0, /*keep*/ 4);
The trigger delay is set to 0. This means the sensor reacts immediately when motion is detected.
The keep delay is set to 4. This defines how long the detection state is maintained after motion stops. This prevents rapid switching between detected and not detected states.
PWM Output Settings
The sensor can generate PWM signals for external control.
radar.setPwm(/*pwm1*/ 50, /*pwm2*/ 0, /*timer*/ 10);
The first PWM channel is set to a value of 50. This defines the duty cycle.
The second PWM channel is disabled by setting it to 0.
The timer value defines the PWM period. This feature can be used to control external devices such as LEDs or relays directly from the sensor.
Output Polarity
The output polarity defines the logic level of the detection signal. Note that the spelling of the function name in the library is incorrect (“Polaity” instead of “Polarity”).
radar.setIoPolaity(1);
A value of 1 sets the output to active high. This means the output signal becomes HIGH when motion is detected. If set to 0, the logic would be inverted and the signal would become LOW when motion is detected.
Loop Function
The loop function continuously checks for motion.
void loop() {
if (radar.motionDetection()) {
Serial.println("Motion detected");
}
delay(100);
}
The function radar.motionDetection() returns true when motion is detected. The detection logic uses all configured parameters, including range, sensitivity, and timing.
When motion is detected, a message is printed to the serial monitor.
The short delay of 100 milliseconds ensures fast updates while keeping CPU usage low.
Code Example: Speed and Distance Measurements with C4001
The next code initializes the C4001 mmWave radar sensor using UART communication and configures it for motion and speed detection. In the main loop, the code continuously checks if a target is detected and, if so, reads the distance and speed of the target. The measured values are then printed to the serial monitor, allowing real-time tracking of moving objects.
// Libraries:
// - DFRobot_C4001 V 1.0.0
// https://github.com/DFRobot/DFRobot_C4001
// - ESP32 Core V 3.3.8
#include "DFRobot_C4001.h"
#if defined(ARDUINO_AVR_UNO) || defined(ESP8266)
SoftwareSerial mySerial(4, 5);
DFRobot_C4001_UART radar(&mySerial, 9600);
#elif defined(ESP32)
DFRobot_C4001_UART radar(&Serial2, 9600, /*rx*/ 17, /*tx*/ 16);
#endif
void setup() {
Serial.begin(115200);
while (!radar.begin()) {
Serial.println("Can't find sensor");
delay(1000);
}
Serial.println("Sensor connected");
radar.setSensorMode(eSpeedMode);
radar.setFrettingDetection(eON);
delay(500);
Serial.println("Ready");
}
void loop() {
if (radar.getTargetNumber() > 0) {
Serial.print("Distance: ");
Serial.print(radar.getTargetRange());
Serial.print(" m Speed: ");
Serial.print(radar.getTargetSpeed());
Serial.println(" m/s");
}
delay(100);
}
Include Libary
As before, the code starts by including the required library for the radar sensor.
#include "DFRobot_C4001.h"
Platform-Specific Configuration
Next we have the same conditional compilation to support multiple platforms. It selects the correct serial interface depending on the board.
#if defined(ARDUINO_AVR_UNO) || defined(ESP8266) SoftwareSerial mySerial(4, 5); DFRobot_C4001_UART radar(&mySerial, 9600); #elif defined(ESP32) DFRobot_C4001_UART radar(&Serial2, 9600, /*rx*/ 17, /*tx*/ 16); #endif
On Arduino Uno and ESP8266, Pin 4 is configured as RX and pin 5 as TX. On the ESP32, Serial2 is initialized with GPIO17 as RX and GPIO16 as TX.
Setup Function
The setup function initializes serial communication and prepares the sensor.
void setup() {
Serial.begin(115200);
The serial monitor is started at 115200 baud. This is used to print measurement results.
while (!radar.begin()) {
Serial.println("Can't find sensor");
delay(1000);
}
Serial.println("Sensor connected");
The radar.begin() function initializes communication with the sensor. The loop ensures that the program waits until the sensor responds. Once successful, a confirmation message is printed.
Sensor Mode
The sensor is configured to speed mode.
radar.setSensorMode(eSpeedMode);
The eSpeedMode enables motion analysis with velocity measurement. In this mode, the sensor tracks moving targets and calculates their speed using Doppler shift. This mode is different from simple presence detection because it provides dynamic information about movement.
Micro-Motion Detection
The code enables fretting detection.
radar.setFrettingDetection(eON);
Fretting detection allows the sensor to detect very small movements. This includes micro-motions such as breathing or slight body movement. When enabled, the sensor becomes more sensitive to subtle motion and essentially allows the sensor to detect stationary persons.
Stabilization Delay
A short delay is added after configuration.
delay(500);
Serial.println("Ready");
The delay gives the sensor time to apply the new settings. After this, the system is ready to start measurements.
Loop Function
The loop continuously reads data from the sensor.
void loop() {
if (radar.getTargetNumber() > 0) {
The function getTargetNumber() returns the number of detected targets. If at least one target is present, the code proceeds to read measurement data.
Serial.print("Distance: ");
Serial.print(radar.getTargetRange());
The function getTargetRange() returns the distance to the detected target. The value is given in meters.
Serial.print(" m Speed: ");
Serial.print(radar.getTargetSpeed());
The function getTargetSpeed() returns the speed of the detected target. This is calculated using Doppler shift and is expressed in meters per second.
Serial.println(" m/s");
}
delay(10);
}
The results are printed to the serial monitor in a readable format. The loop runs continuously with a short delay of 100 milliseconds.
Output Example
The screenshot below shows what you should see on the Serial Monitor when the sensor detects an object.

Move your hand further and closer to the sensor with different speeds and you should see that the measured distance and speed values are changing.
Conclusions
In this tutorial you learned how to connect the mmWave C4001 sensor to an Arduino or an ESP32 for motion detection. In comparison to Passive Infrared (PIR) motion sensors, the radar-based C4001 is more reliable and can detect stationary persons in specific distance ranges.
If you only need a binary detection signal (detected, not detected), you can program the sensor once (e.g. set detection distance) via a microcontroller and then use the OUT pin. For more complex control actions you can program the microcontroller to react to the sensor measurements.
Note, that there is also the Gravity: C4001 mmWave Human Presence Detection Sensor, which is very similar to the C4001 but has a shorter range (12 Meter) and communicates via I2C, instead of UART. Also have a look the mmWave C4002 and mmWave C1001 sensors.
For additional information about the mmWave C4001 sensor see the Wiki page, the Datasheet and the repo that has other code examples.
If you have any questions feel free to leave them in the comment section.
Happy Tinkering ; )
Stefan is a professional software developer and researcher. He has worked in robotics, bioinformatics, image/audio processing and education at Siemens, IBM and Google. He specializes in AI and machine learning and has a keen interest in DIY projects involving Arduino and 3D printing.

