Skip to Content

Indoor Fall Detection with mmWave C1001

Indoor Fall Detection with mmWave C1001

The C1001 60GHz mmWave Indoor Fall Detection Sensor is a compact radar module designed for human presence and safety monitoring. It operates in the 60 GHz millimeter-wave band and can detect motion, posture, and even very small body movements such as breathing.

Unlike traditional PIR or camera-based systems, this sensor uses radar signals to analyze reflected waves from the human body. This allows it to detect both movement and stillness, including cases where a person has fallen and remains motionless. It also works in complete darkness and does not capture images, which makes it suitable for privacy-sensitive indoor environments.

The module includes built-in algorithms for fall detection, sleep monitoring, presence sensing, and basic vital sign measurements. It can measure respiration and heartbeat within a short range and detect human activity up to about 11 meters. Processed results are output directly over a serial interface, so no complex signal processing is required on the microcontroller.

In this tutorial you will learn how to connect the mmWave C4002 sensor to an Arduino or an ESP32 to measure vital signs, to detect falls and to monitor sleep quality.

Required Parts

You can get the mmWave C1001 Sensor at DFRobot or Amazon. You also will need an Arduino, ESP8266 or an ESP32. I am using an Arduino UNO and an ESP32-C3 SuperMini in this tutorial but any other Arduino, ESP32 or ESP8266 will work as long as it has a 5V output pin. Finally, a breadboard and some Dupont cables for wiring will be useful.

mmWave C1001 Sensor

ESP32-C3 SuperMini

USB C Cable

Arduino

Arduino Uno

USB Data Sync cable Arduino

USB Cable for Arduino UNO

Dupont wire set

Dupont Wire Set

Half_breadboard56a

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 C1001 Sensor

The C1001 sensor is based on a 60 GHz millimeter-wave radar front-end. It operates in the frequency range of approximately 61 to 61.5 GHz and transmits low-power electromagnetic waves into the environment. The transmitted signal is reflected by objects and human bodies, and the sensor analyzes the returned signal to extract motion and presence information. The picture below shows the back and front of the C1001 sensor module:

Back and Front of mmWave C1001 Sensor Module
Back and Front of mmWave C1001 Sensor Module

The radar uses a point cloud imaging approach to model the position and movement of a human target. This method allows the sensor to detect posture changes and spatial movement rather than relying only on simple motion triggers. As a result, it can distinguish between standing, moving, and lying positions with higher reliability.

The sensor can also detect micro-movements such as chest displacement caused by breathing and heartbeat. This is possible because millimeter-wave signals are sensitive to very small surface changes, even in the sub-millimeter range.

Detection Range and Field of View

The C1001 sensor supports a maximum detection distance of up to 11 meters under optimal conditions. It provides a wide field of view with an approximate coverage angle of 100 degrees in both horizontal and vertical directions.

When used for fall detection, the sensor is typically mounted on the ceiling. At an installation height of around 2.7 meters, the effective fall detection area forms a circular zone with a diameter of about 4 meters.

For vital sign monitoring, the detection range is shorter. Breathing and heartbeat measurements are most accurate within a distance of about 0.4 to 1.5 meters. Sleep monitoring can extend slightly further, up to approximately 2.5 meters from the sensor.

Signal Processing and Embedded Algorithms

The C1001 integrates onboard signal processing and detection algorithms. Raw radar data is processed internally, and the user receives high-level information instead of raw signal streams. This reduces the computational load on the connected microcontroller.

The internal firmware implements multiple detection modes. These include human presence detection, motion tracking, fall detection, and sleep monitoring. The fall detection algorithm analyzes posture transitions and identifies sudden changes followed by immobility. It can also track the duration of inactivity after a fall event.

The vital sign detection algorithm extracts periodic patterns from reflected signals. It calculates respiration rates typically in the range of 10 to 25 breaths per minute and heart rates between 60 and 100 beats per minute.

Electrical Characteristics

The sensor operates with a supply voltage of 5 V and typically consumes less than 100 mA. This makes it suitable for embedded systems powered by standard development boards.

The radar transmitter outputs a signal with a power level of about 6 dBm. This ensures sufficient signal strength for indoor detection while maintaining low power consumption and safe operation.

The device is designed to operate in a temperature range from −20 °C to 60 °C. This allows stable operation in typical indoor environments and many semi-industrial applications.

Interface and Data Output

The C1001 communicates through a UART serial interface. It provides processed data frames that include detection states such as presence, fall status, and vital sign information.

Power supply (5V) is via the VIN and GND pins, while TX and RX pins are used for serial communication with a microcontroller. Two auxiliary digital output pins labeled IO1 and IO2 are designed to report fall events and human presence. The photo below shows the pinout of the C1001 module:

Pinout of the C1001 Sensor
Pinout of the C1001 Sensor

Note that I could not get IO1 or IO2 to work. I could not find a function in the library to activate these output pins and per default they seem not to be active.

Installation and Operating Constraints

The sensor requires good positioning to achieve optimal performance. For fall detection, a top-mounted configuration is recommended. The radar beam forms a three-dimensional coverage area, and obstacles or moving objects can affect signal quality.

The accuracy of vital sign detection depends on distance, orientation, and environmental stability. External motion sources such as fans or curtains can introduce noise into the reflected signal.

Due to the nature of radar sensing, the module performs consistently in low-light or completely dark environments. It is not affected by ambient light conditions and does not rely on optical sensing.

Technical Specification

The following table summarizes the technical specification of the mmWave C1001 sensor:

ParameterValue / Description
Operating Frequency60 GHz mmWave (approx. 61–61.5 GHz)
Detection TechnologyFMCW radar with point cloud processing
Detection RangeUp to 11 m (presence and motion)
Fall Detection RangeOptimized for ceiling mounting, ~4 m diameter area
Vital Sign Detection Range~0.4 m to 1.5 m (up to ~2.5 m for sleep monitoring)
Field of View~100° horizontal and vertical
Detection CapabilitiesPresence, motion, fall detection, sleep monitoring
Vital Sign MeasurementRespiration and heart rate
Respiration Rate Range~10–25 breaths per minute
Heart Rate Range~60–100 bpm
Supply Voltage5 V
Operating Current< 100 mA
Transmit Power~6 dBm
Communication InterfaceUART (TTL level)
Digital Output PinsIO1 (fall detect), IO2 (presence detect)
Logic Level3.3 V
Operating Temperature−20 °C to 60 °C
Mounting MethodCeiling or wall mount (ceiling preferred for fall detection)
Data OutputProcessed data (no raw radar data required)
Supported PlatformsArduino, ESP32, ESP8266

Connecting the mmWave C1001 Sensor to Arduino

Connecting the mmWave C1001 Sensor to an Arduino UNO is simple. Start by connecting VIN to 5V of the Arduino. Next connect GND to GND. Finally, connect the UART interface by wiring RX to GPIO 5 and TX to GPIO 4. The picture below shows the complete wiring:

Connecting the mmWave C1001 Sensor to an Arduino UNO
Connecting the mmWave C1001 Sensor to an Arduino UNO

For convenience here a table with the connections you need to make:

C1001Arduino Uno
VIN5V
GNDGND
RXGPIO 5
TXGPIO 4

Connecting the mmWave C1001 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 3 and RX to GPIO 4. We will need to remember this when writing the code. Finally, we connect VIN to 5V and GND to G. The picture below shows the complete wiring:

Connecting the mmWave C1001 Sensor to an ESP32-C3 Supermini
Connecting the mmWave C1001 Sensor to an ESP32-C3 Supermini

Note that you need an ESP32 with a 5V output pin and that you need to connect VIN of the C1001 to the 5V pin and not the common 3.3V pin! The picture below shows the pinout of the ESP32-C3 Supermini, I am using here.

Pinout of ESP32-C3 Supermini

For convenience here a table with the connections you need to make.

C1001ESP32-C3 Supermini
VIN5V
GNDG
RX4
TX3

Installing the DFRobot_HumanDetection library

Before we can write any code, we will need to install the DFRobot_HumanDetection library. To install this library go to the DFRobot_HumanDetection 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_HumanDetection-master.zip):

Code: Vital Signs Measurements

The following code examples shows you how to measure vital signs such as heart and respiration rate with the C1001 sensor. I slightly changed the original code of the basic.ino in the github repo by removing some print outs and adding support for Arduino and ESP866 microcontrollers in addition to the ESP32.

// Libraries:
// - DFRobot_HumanDetection V 1.0.0
//   https://github.com/DFRobot/DFRobot_HumanDetection
// - ESP32 Core V 3.3.8

#include "DFRobot_HumanDetection.h"

#if defined(ESP8266) || defined(ARDUINO_AVR_UNO)
  #include <SoftwareSerial.h>
  SoftwareSerial mySerial(4, 5);
  DFRobot_HumanDetection hu(&mySerial);
#elif defined(ESP32)
  DFRobot_HumanDetection hu(&Serial1);
#else
  #error "Unsupported board."
#endif


void setup() {
  Serial.begin(115200);

  #if defined(ESP32)
    Serial1.begin(115200, SERIAL_8N1, 3, 4);
  #elif defined(ESP8266) || defined(ARDUINO_AVR_UNO)
    mySerial.begin(115200);
  #endif

  Serial.println("Starting...");
  while (hu.begin() != 0) {
    Serial.println("init error!!!");
    delay(1000);
  }

  hu.configWorkMode(hu.eSleepMode);
  hu.configLEDLight(hu.eHPLed, 1); 

  // Sensor needs reset after parameters change
  hu.sensorRet();    
}

void loop() {
  Serial.print("Presence        : ");
  switch (hu.smHumanData(hu.eHumanPresence)) {
    case 0:
      Serial.println("No"); break;
    case 1:
      Serial.println("Yes"); break;
    default:
      Serial.println("Read error");
  }

  Serial.print("Motion          : ");
  switch (hu.smHumanData(hu.eHumanMovement)) {
    case 0:
      Serial.println("None"); break;
    case 1:
      Serial.println("Still"); break;
    case 2:
      Serial.println("Active"); break;
    default:
      Serial.println("Read error");
  }

  Serial.print("Movement        : ");
  Serial.println(hu.smHumanData(hu.eHumanMovingRange));
  Serial.print("Respiration rate: ");
  Serial.println(hu.getBreatheValue());
  Serial.print("Heart rate      : ");
  Serial.println(hu.getHeartRate());
  Serial.println("-----------------------");
  delay(1000);
}

Imports

The code starts by including the required library for the sensor. The DFRobot_HumanDetection library provides all functions needed to communicate with the C1001 mmWave sensor and access processed data.

#include "DFRobot_HumanDetection.h"

This library abstracts the low-level UART protocol. It allows you to work with high-level functions such as presence detection, motion status, and vital signs.

Board-Specific Serial Configuration

The code then configures the serial interface depending on the target board. The C1001 sensor communicates over UART, so a serial port must be assigned.

#if defined(ESP8266) || defined(ARDUINO_AVR_UNO)
  #include <SoftwareSerial.h>
  SoftwareSerial mySerial(4, 5);
  DFRobot_HumanDetection hu(&mySerial);
#elif defined(ESP32)
  DFRobot_HumanDetection hu(&Serial1);
#else
  #error "Unsupported board."
#endif

For boards like Arduino Uno or ESP8266, a software-based serial port is created on pins 4 and 5. This is required because these boards often have only one hardware UART.

For the ESP32, the code uses Serial1, which is a hardware UART. This is more reliable and supports higher data rates.

The object hu is created as an instance of the sensor class. It receives a pointer to the serial interface, which is used internally for communication with the sensor.

Setup Function

The setup() function initializes serial communication and prepares the sensor for operation.

void setup() {
  Serial.begin(115200);

The main serial port is started at 115200 baud. This is used for debugging and printing sensor data to the Serial Monitor.

  #if defined(ESP32)
    Serial1.begin(115200, SERIAL_8N1, 3, 4);

On the ESP32, the hardware UART is initialized with a baud rate of 115200. The configuration SERIAL_8N1 means 8 data bits, no parity, and 1 stop bit. Pins 3 and 4 are assigned as RX and TX. For other boards, the software serial interface is started with the same baud rate.

  #elif defined(ESP8266) || defined(ARDUINO_AVR_UNO)
    mySerial.begin(115200);
  #endif

A simple message is printed to indicate that initialization has started:

  Serial.println("Starting...");

The function hu.begin() initializes communication with the sensor. It returns 0 on success. If initialization fails, the code prints an error message and retries every second. This loop ensures the sensor is ready before continuing:

  while (hu.begin() != 0) {
    Serial.println("init error!!!");
    delay(1000);
  }

Sensor Configuration

After initialization, the sensor is configured using library functions. This line sets the working mode of the sensor. The mode eSleepMode is optimized for detecting stationary humans and monitoring vital signs such as breathing and heart rate.

  hu.configWorkMode(hu.eSleepMode);

This function controls the onboard LED. The parameter eHPLed selects the LED type, and 1 turns it on. This can be useful for debugging or visual feedback.

  hu.configLEDLight(hu.eHPLed, 1); 

And this function resets the sensor. A reset is required after changing configuration parameters to ensure they take effect.

  hu.sensorRet(); 

Loop Function

The loop() function continuously reads data from the sensor and prints it to the Serial Monitor.

First we print a label for human presence detection.

void loop() {
  Serial.print("Presence        : ");

Next we use the function smHumanData() to read a specific type of sensor data. The parameter eHumanPresence selects presence detection.

  switch (hu.smHumanData(hu.eHumanPresence)) {

A return value of 0 means no human is detected. A value of 1 means a person is present. Any other value indicates a communication or read error:

    case 0:
      Serial.println("No"); break;
    case 1:
      Serial.println("Yes"); break;
    default:
      Serial.println("Read error");

Motion Status

The next section reads the motion state of the detected person. The parameter eHumanMovement selects motion classification.

  Serial.print("Motion          : ");
  switch (hu.smHumanData(hu.eHumanMovement)) {

A value of 0 means no motion is detected, a value of 1 means the person is present but stationary and a value of 2 indicates active movement.

    case 0:
      Serial.println("None"); break;
    case 1:
      Serial.println("Still"); break;
    case 2:
      Serial.println("Active"); break;

Movement Range

The code then prints a numeric value representing movement intensity or range.

  Serial.print("Movement        : ");
  Serial.println(hu.smHumanData(hu.eHumanMovingRange));

This value is useful for estimating how much the person is moving within the detection area. Higher values indicate stronger or larger movement.

Vital Signs

The sensor can also measure respiration and heart rate. The function getBreatheValue() returns the breathing rate in breaths per minute.

  Serial.print("Respiration rate: ");
  Serial.println(hu.getBreatheValue());

And the function getHeartRate() returns the heart rate in beats per minute. These measurements are based on micro-movements detected by the radar.

  Serial.print("Heart rate      : ");
  Serial.println(hu.getHeartRate());

Loop Timing

At the end of the loop, a delay is added to control the update rate.

  Serial.println("-----------------------");
  delay(1000);
}

Output Example

The following picture shows an example output of the code. Note that it takes several minutes until measurements for Respiration and Heart Rate appear. The sensor needs some time to get stable readings.

Measuring Respiration and Heart Rate contactless and over a distance of up to 1.5 meters is impressive. Note that the accuracy is limited, however. See the Functional Test Report of DFRobot C1001 mmWave Sensor post for more information on measurement condition, reliability and accuracy.

Code: Fall Detection

The following code demonstrates how to perform Fall Detection with the C1001 sensor. As before, it is a slightly modified version of the fall.ino code in the github repo of the DFRobot_HumanDetection library. It supports Arduino, ESP866 and ESP32 microcontrollers.

// Libraries:
// - DFRobot_HumanDetection V 1.0.0
//   https://github.com/DFRobot/DFRobot_HumanDetection
// - ESP32 Core V 3.3.8

#include "DFRobot_HumanDetection.h"

#if defined(ESP8266) || defined(ARDUINO_AVR_UNO)
  #include <SoftwareSerial.h>
  SoftwareSerial mySerial(4, 5);
  DFRobot_HumanDetection hu(&mySerial);
#elif defined(ESP32)
  DFRobot_HumanDetection hu(&Serial1);
#else
  #error "Unsupported board."
#endif


void setup() {
  Serial.begin(115200);

  #if defined(ESP32)
    Serial1.begin(115200, SERIAL_8N1, 3, 4);
  #elif defined(ESP8266) || defined(ARDUINO_AVR_UNO)
    mySerial.begin(115200);
  #endif

  Serial.println("Starting...");
  while (hu.begin() != 0) {
    Serial.println("init error!!!");
    delay(1000);
  }

  hu.configWorkMode(hu.eFallingMode);        // Set working mode to fall detection
  hu.configLEDLight(hu.eFALLLed, 1);         // Set HP LED switch, it will not light up even if the sensor detects a person present when set to 0.
  hu.configLEDLight(hu.eHPLed, 1);           // Set FALL LED switch, it will not light up even if the sensor detects a person falling when set to 0.
  hu.dmInstallHeight(270);                   // Set installation height, it needs to be set according to the actual height of the surface from the sensor, unit: CM.
  hu.dmFallTime(5);                          // Set fall time, the sensor needs to delay the current set time after detecting a person falling before outputting the detected fall, this can avoid false triggering, unit: seconds.
  hu.dmUnmannedTime(1);                      // Set unattended time, when a person leaves the sensor detection range, the sensor delays a period of time before outputting a no person status, unit: seconds.
  hu.dmFallConfig(hu.eResidenceTime, 200);    // Set dwell time, when a person remains still within the sensor detection range for more than the set time, the sensor outputs a stationary dwell status. Unit: seconds.
  hu.dmFallConfig(hu.eFallSensitivityC, 3);  // Set fall sensitivity, range 0~3, the larger the value, the more sensitive.
  hu.sensorRet();                            // Module reset, must perform sensorRet after setting data.
}

void loop() {
  Serial.print("Fallen    : ");
  switch (hu.getFallData(hu.eFallState)) {
    case 0:
      Serial.println("No"); break;
    case 1:
      Serial.println("Yes"); break;
    default:
      Serial.println("Read error");
  }

  Serial.print("Stationary: ");
  switch (hu.getFallData(hu.estaticResidencyState)) {
    case 0:
      Serial.println("No"); break;
    case 1:
      Serial.println("Yes"); break;
    default:
      Serial.println("Read error");
  }

  Serial.println("-----------------");
  delay(1000);
}

Imports and Object Initialization

The library inclusion and object creation are identical to the previous example. The same DFRobot_HumanDetection library is used, and the sensor object hu is initialized with a hardware or software serial interface depending on the board.

The UART configuration and communication setup follow the same structure as before. You can refer to the previous explanation for details on serial initialization and object construction.

Setup Function

The initialization sequence inside setup() is also the same. The serial interfaces are started, and the sensor is initialized using hu.begin(). The loop that retries initialization ensures stable communication before continuing.

The difference in this example starts with the sensor configuration. The sensor is now explicitly configured for fall detection.

Fall Detection Mode Configuration

The working mode is changed to a dedicated fall detection mode.

hu.configWorkMode(hu.eFallingMode);

This switches the internal firmware to use fall detection algorithms instead of general presence or sleep monitoring. The sensor now focuses on posture changes and sudden movements followed by inactivity.

LED Configuration

Two LED indicators are configured for visual feedback.

hu.configLEDLight(hu.eFALLLed, 1);
hu.configLEDLight(hu.eHPLed, 1);

The eFALLLed controls the LED that indicates a detected fall event. The eHPLed controls the presence detection LED. Setting both to 1 enables them.

These LEDs are driven by the internal detection logic and can help during testing and calibration.

Installation Height

The installation height is a critical parameter for accurate fall detection. The following function call sets the mounting height of the sensor to 270 cm. The value must match the real installation height because the radar processing depends on geometry.

hu.dmInstallHeight(270);

Incorrect height values can lead to inaccurate position estimation and false detections.

Fall Detection Timing

The sensor includes a configurable delay before confirming a fall event. The following function call sets a delay of 5 seconds. After detecting a potential fall, the sensor waits this amount of time before reporting it as a valid fall.

hu.dmFallTime(5);

This helps filter out short or non-critical movements that might otherwise trigger false positives.

Unmanned Detection Delay

The sensor also delays reporting when no person is present. The following function sets a delay of 1 second before switching to a “no person” state.

hu.dmUnmannedTime(1);

This prevents rapid toggling when a person briefly leaves or moves at the edge of the detection zone.

Stationary Dwell Detection

The sensor can detect when a person remains still for a long time. The following function call sets the dwell time to 200 seconds.

hu.dmFallConfig(hu.eResidenceTime, 200);

If a person remains motionless within the detection area for longer than this period, the sensor reports a stationary state.

This feature is useful in fall detection scenarios because a person lying still after a fall can be identified.

Fall Sensitivity

The sensitivity of the fall detection algorithm can be adjusted. Here sensitivity is set to level 3, which is the highest value.

hu.dmFallConfig(hu.eFallSensitivityC, 3);

Higher sensitivity increases the likelihood of detecting falls but may also increase false positives. Lower values reduce sensitivity and require more pronounced motion changes.

Applying Configuration

After all parameters are set, the sensor is reset.

hu.sensorRet();

As explained earlier, this step is required for the new configuration to take effect. The sensor restarts with the updated parameters.

Loop Function

The loop continuously reads fall-related data from the sensor. Unlike the previous example, this code uses getFallData() instead of smHumanData().

Fall State Detection

The function getFallData() retrieves processed fall detection results. The parameter eFallState selects the fall status:

Serial.print("Fallen    : ");
switch (hu.getFallData(hu.eFallState)) {

A value of 0 means no fall is detected, and a value of 1 indicates that a fall event has been confirmed by the sensor:

case 0:
  Serial.println("No"); break;
case 1:
  Serial.println("Yes"); break;

Stationary State Detection

The estaticResidencyState constant is used to retrieve the stationary dwell status:

Serial.print("Stationary: ");
switch (hu.getFallData(hu.estaticResidencyState)) {

A value of 1 means a person has remained still longer than the configured dwell time. This can indicate a critical situation, especially when combined with a detected fall.

case 0:
  Serial.println("No"); break;
case 1:
  Serial.println("Yes"); break;

Loop Timing

The loop ends with a delay and a separator for readability.

Serial.println("-----------------");
delay(1000);

Code: Sleep Monitoring

In this final code example we perform Sleep Monitoring with the C1001 sensor. The code is based on the sleep.ino example in the DFRobot_HumanDetection library. I just added support for Arduino and ESP866 microcontrollers.

// Libraries:
// - DFRobot_HumanDetection V 1.0.0
//   https://github.com/DFRobot/DFRobot_HumanDetection
// - ESP32 Core V 3.3.8

#include "DFRobot_HumanDetection.h"

#if defined(ESP8266) || defined(ARDUINO_AVR_UNO)
  #include <SoftwareSerial.h>
  SoftwareSerial mySerial(4, 5);
  DFRobot_HumanDetection hu(&mySerial);
#elif defined(ESP32)
  DFRobot_HumanDetection hu(&Serial1);
#else
  #error "Unsupported board."
#endif

void setup() {
  Serial.begin(115200);

  #if defined(ESP32)
    Serial1.begin(115200, SERIAL_8N1, 3, 4);
  #elif defined(ESP8266) || defined(ARDUINO_AVR_UNO)
    mySerial.begin(115200);
  #endif

  Serial.println("Starting...");
  while (hu.begin() != 0) {
    Serial.println("init error!!!");
    delay(1000);
  }

  hu.configWorkMode(hu.eSleepMode);
  hu.configLEDLight(hu.eHPLed, 1); 

  // Sensor needs reset after parameters change
  hu.sensorRet();    
}

void loop() {
  Serial.print("Bed entry status:");
  switch (hu.smSleepData(hu.eInOrNotInBed)) {
    case 0:
      Serial.println("Out of bed"); break;
    case 1:
      Serial.println("In bed"); break;
    default:
      Serial.println("Read error");
  }

  Serial.print("Sleep status:");
  switch (hu.smSleepData(hu.eSleepState)) {
    case 0:
      Serial.println("Deep sleep"); break;
    case 1:
      Serial.println("Light sleep"); break;
    case 2:
      Serial.println("Awake"); break;
    case 3:
      Serial.println("None"); break;
    default:
      Serial.println("Read error");
  }
  Serial.print("Awake duration: ");
  Serial.println(hu.smSleepData(hu.eWakeDuration));
  Serial.print("Deep sleep duration: ");
  Serial.println(hu.smSleepData(hu.eDeepSleepDuration));
  Serial.print("Sleep quality score: ");
  Serial.println(hu.smSleepData(hu.eSleepQuality));

  sSleepComposite comprehensiveState = hu.getSleepComposite();
  Serial.println("Comprehensive sleep status:{");

  Serial.print("\tExistence status: ");
  switch (comprehensiveState.presence) {
    case 0:
      Serial.println("No one"); break;
    case 1:
      Serial.println("Someone is present"); break;
    default:
      Serial.println("Read error");
  }

  Serial.print("\tSleep status:");
  switch (comprehensiveState.sleepState) {
    case 0:
      Serial.println("Deep sleep"); break;
    case 1:
      Serial.println("Light sleep"); break;
    case 2:
      Serial.println("Awake"); break;
    case 3:
      Serial.println("None"); break;
    default:
      Serial.println("Read error");
  }

  Serial.print("\tAverage respiration rate: ");
  Serial.println(comprehensiveState.averageRespiration);
  Serial.print("\tAverage heart rate: ");
  Serial.println(comprehensiveState.averageHeartbeat);
  Serial.print("\tNumber of turns: ");
  Serial.println(comprehensiveState.turnoverNumber);
  Serial.print("\tProportion of significant body movement: ");
  Serial.println(comprehensiveState.largeBodyMove);
  Serial.print("\tProportion of minor body movement: ");
  Serial.println(comprehensiveState.minorBodyMove);
  Serial.print("\tNumber of apneas: ");
  Serial.println(comprehensiveState.apneaEvents);
  Serial.println("}");

  Serial.print("Sleep abnormalities:");
  switch (hu.smSleepData(hu.eSleepDisturbances)) {
    case 0:
      Serial.println("Sleep duration less than 4 hours"); break;
    case 1:
      Serial.println("Sleep duration more than 12 hours"); break;
    case 2:
      Serial.println("Long time abnormal absence of person"); break;
    case 3:
      Serial.println("None"); break;
    default:
      Serial.println("Read error");
  }

  sSleepStatistics statistics = hu.getSleepStatistics();  // Get sleep statistics, for whole night
  Serial.print("\tSleep quality score: ");
  Serial.println(statistics.sleepQualityScore);
  Serial.print("\tProportion of awake time: ");
  Serial.println(statistics.sleepTime);
  Serial.print("\tProportion of light sleep time: ");
  Serial.println(statistics.wakeDuration);
  Serial.print("\tProportion of light sleep time: ");
  Serial.println(statistics.shallowSleepPercentage);
  Serial.print("\tProportion of deep sleep time: ");
  Serial.println(statistics.deepSleepPercentage);
  Serial.print("\tOut of bed duration: ");
  Serial.println(statistics.timeOutOfBed);
  Serial.print("\tNumber of times out of bed: ");
  Serial.println(statistics.exitCount);
  Serial.print("\tNumber of turns: ");
  Serial.println(statistics.turnOverCount);
  Serial.print("\tAverage respiration: ");
  Serial.println(statistics.averageRespiration);
  Serial.print("\tAverage heartbeat: ");
  Serial.println(statistics.averageHeartbeat);
  Serial.println("}");

  Serial.print("Sleep quality rating: ");
  switch (hu.smSleepData(hu.eSleepQualityRating)) {
    case 0:
      Serial.println("None"); break;
    case 1:
      Serial.println("Good sleep quality"); break;
    case 2:
      Serial.println("Average sleep quality"); break;
    case 3:
      Serial.println("Poor sleep quality"); break;
    default:
      Serial.println("Read error");
  }

  Serial.print("Abnormal struggle status: ");
  switch (hu.smSleepData(hu.eAbnormalStruggle)) {
    case 0:
      Serial.println("None"); break;
    case 1:
      Serial.println("Normal status"); break;
    case 2:
      Serial.println("Abnormal struggle status"); break;
    default:
      Serial.println("Read error");
  }

  Serial.println("-----------------------------------");
  delay(1000);
}

Imports and Object Initialization

The library inclusion and object creation are identical to the previous examples. The same DFRobot_HumanDetection class is used, and the sensor is connected through a UART interface. The object hu handles all communication and data processing.

The serial configuration for ESP32, ESP8266, and Arduino Uno is unchanged. Refer to the earlier explanations for details on how the UART interface is initialized and why hardware serial is preferred on the ESP32.

Setup Function

The initialization sequence in setup() follows the same pattern as before. The serial ports are started, and the sensor is initialized using hu.begin() with a retry loop to ensure stable communication.

The key difference is the selected working mode. In this example, the sensor is configured for sleep monitoring instead of fall detection.

hu.configWorkMode(hu.eSleepMode);

This mode enables algorithms for detecting presence in bed, sleep stages, and vital signs such as respiration and heart rate over time.

hu.configLEDLight(hu.eHPLed, 1);

The presence LED is enabled, which provides visual feedback when a person is detected.

hu.sensorRet();

As explained earlier, the sensor must be reset after configuration changes so that the new parameters take effect.

Loop Function Overview

The loop() function continuously reads sleep-related data from the sensor. Unlike the previous examples, this code uses smSleepData() and additional composite data structures to access more advanced metrics.

The output is more complex because the sensor aggregates and analyzes data over time.

Bed Entry Detection

The first section checks whether a person is in bed.

Serial.print("Bed entry status:");
switch (hu.smSleepData(hu.eInOrNotInBed)) {

The function smSleepData() works similarly to smHumanData(), but it accesses sleep-related parameters.

case 0:
  Serial.println("Out of bed"); break;
case 1:
  Serial.println("In bed"); break;

A value of 1 indicates that a person is detected within the bed area. This is determined using presence detection combined with position analysis.

Sleep State Detection

The next section reads the current sleep stage.

Serial.print("Sleep status:");
switch (hu.smSleepData(hu.eSleepState)) {

The sensor classifies sleep into multiple stages.

case 0:
  Serial.println("Deep sleep"); break;
case 1:
  Serial.println("Light sleep"); break;
case 2:
  Serial.println("Awake"); break;
case 3:
  Serial.println("None"); break;

These states are derived from motion patterns and vital sign variations. Deep sleep is characterized by minimal movement and stable breathing, while awake states show more activity.

Sleep Timing and Quality Metrics

The sensor provides duration and quality metrics as numeric values.

With the eWakeDuration constant the function returns how long the person has been awake within the monitoring period.

Serial.print("Awake duration: ");
Serial.println(hu.smSleepData(hu.eWakeDuration));

And with the eDeepSleepDuration constant the function tracks the duration of deep sleep.

Serial.print("Deep sleep duration: ");
Serial.println(hu.smSleepData(hu.eDeepSleepDuration));

The sleep quality score is a computed metric based on movement, breathing stability, and sleep stages.

Serial.print("Sleep quality score: ");
Serial.println(hu.smSleepData(hu.eSleepQuality));

Composite Sleep Data

Next the code then retrieves a structured dataset with multiple parameters.

sSleepComposite comprehensiveState = hu.getSleepComposite();

This structure combines several measurements into one object. It provides a snapshot of the current sleep condition. The presence value indicates whether a person is present:

switch (comprehensiveState.presence) {

Next we have the sleepState value, which repeats the sleep stage classification:

switch (comprehensiveState.sleepState) {

The averageRespiration value contains the average respiration rate calculated over a time window:

Serial.print("\tAverage respiration rate: ");
Serial.println(comprehensiveState.averageRespiration);

While the averageHeartbeat value contains average heart rate:

Serial.print("\tAverage heart rate: ");
Serial.println(comprehensiveState.averageHeartbeat);

The turnoverNumber value counts how often the person changes position during sleep:

Serial.print("\tNumber of turns: ");
Serial.println(comprehensiveState.turnoverNumber);

And the largeBodyMove value represents the proportion of large movements:

Serial.print("\tProportion of significant body movement: ");
Serial.println(comprehensiveState.largeBodyMove);

while the minorBodyMove value indicates smaller movements such as slight adjustments:

Serial.print("\tProportion of minor body movement: ");
Serial.println(comprehensiveState.minorBodyMove);

Finally, we have apneaEvents, which counts detected apnea events (pauses in breathing):

Serial.print("\tNumber of apneas: ");
Serial.println(comprehensiveState.apneaEvents);

Sleep Abnormalities

The code checks for abnormal sleep conditions.

Serial.print("Sleep abnormalities:");
switch (hu.smSleepData(hu.eSleepDisturbances)) {

The sensor reports predefined abnormal states.

case 0:
  Serial.println("Sleep duration less than 4 hours"); break;
case 1:
  Serial.println("Sleep duration more than 12 hours"); break;
case 2:
  Serial.println("Long time abnormal absence of person"); break;
case 3:
  Serial.println("None"); break;

These conditions are based on thresholds defined in the firmware.

Sleep Statistics

The code retrieves long-term statistics for the entire monitoring period. The following structure provides aggregated data, typically over a full night.

sSleepStatistics statistics = hu.getSleepStatistics();

For instance, this is the overall sleep quality score.

Serial.print("\tSleep quality score: ");
Serial.println(statistics.sleepQualityScore);

And this value represents the proportion of time spent awake.

Serial.print("\tProportion of awake time: ");
Serial.println(statistics.sleepTime);

Next we have and indicator for the percentage time of deep sleep t.

Serial.print("\tProportion of light sleep time: ");
Serial.println(statistics.shallowSleepPercentage);

And this value indicates the percentage of deep sleep.

Serial.print("\tProportion of deep sleep time: ");
Serial.println(statistics.deepSleepPercentage);

“timeOutOfBed” tracks how long the person was out of bed.

Serial.print("\tOut of bed duration: ");
Serial.println(statistics.timeOutOfBed);

and “exitCount” counts how often the person left the bed.

Serial.print("\tNumber of times out of bed: ");
Serial.println(statistics.exitCount);

We also get measurements for the total number of body turns

Serial.print("\tNumber of turns: ");
Serial.println(statistics.turnOverCount);

the average breathing rate over the monitoring period

Serial.print("\tAverage respiration: ");
Serial.println(statistics.averageRespiration);

and the average heart rate:

Serial.print("\tAverage heartbeat: ");
Serial.println(statistics.averageHeartbeat);

Sleep Quality Rating

The sensor provides a simplified classification of sleep quality.

Serial.print("Sleep quality rating: ");
switch (hu.smSleepData(hu.eSleepQualityRating)) {

case 1:
  Serial.println("Good sleep quality"); break;
case 2:
  Serial.println("Average sleep quality"); break;
case 3:
  Serial.println("Poor sleep quality"); break;

This rating is derived from the detailed metrics and provides an easy-to-interpret result.

Abnormal Struggle Detection

The final section detects unusual movement patterns.

Serial.print("Abnormal struggle status: ");
switch (hu.smSleepData(hu.eAbnormalStruggle)) {

case 0:
  Serial.println("None"); break;
case 1:
  Serial.println("Normal status"); break;
case 2:
  Serial.println("Abnormal struggle status"); break;

This feature identifies irregular or intense movements that may indicate discomfort or distress during sleep.

Loop Timing

The loop ends with a delay and a separator, similar to the previous examples. The one-second interval provides a steady update rate while allowing the sensor to accumulate meaningful data between readings.

Conclusions

In this tutorial you learned how to connect the mmWave C1001 sensor to an Arduino or an ESP32 to measure vital signs, to detect falls and to monitor sleep quality. Note that the C1001 sensor is not a certified medical device and should not be used for medical diagnosis or treatments!

For more information on how to install the sensor, its range and accuracy see the Functional Test Report of DFRobot C1001 mmWave Sensor article. Also have a look at the Wiki page and the code repo.

If you only need presence detection see our tutorials for the mmWave C4001 and mmWave C4002 sensors. These are also radar sensors but do not have built-in functions for fall detection or sleep monitoring.

If you have any questions feel free to leave them in the comment section.

Happy Tinkering ; )