Skip to main content

Extending MCP with Grove

Overview

This document describes how to use the DHT20 temperature and humidity sensor via the Grove interface on the ESP32-S3-based SenseCAP Watcher development board.

Hardware Preparation

SenseCAP Watcher for XiaoZhiGrove - Temperature & Humidity Sensor V2.0 (DHT20)

Grove Interface Definition (J5)

According to the hardware schematic, the Grove interface uses the following pins:

Grove PinfunctionESP32-S3 Connection
Pin 1SCLGPIO48 (I2C0_SCL)
Pin 2SDAGPIO47 (I2C0_SDA)
Pin 3VCCGROVE_3.3V (3.3V Power)
Pin 4GNDGND

DHT20 Sensor

  • I2C Address: 0x38 (Fixed, cannot be changed)
  • Supply Voltage: 2.0V - 5.5V (use 3.3V)
  • Communication Protocol: I2C (up to 400kHz)
  • Measurement Range:
    • Temperature: -40°C to +80°C (Accuracy: ±0.5°C)
    • Humidity: 0% to 100% RH (Accuracy: ±3% RH)

Execution Result

File Structure

Original Code:

New files:

  • dht20.h

  • dht20.cc

  • You should place the above two files in the following location:

    • file location
      main/boards/sensecap-watcher/
      ├── dht20.h # DHT20 driver header
      └── dht20.cc # DHT20 driver implementation

Modified files

Note

The above example code is for reference only. You should modify it according to your specific sensor model, development environment, and firmware version to ensure proper integration and successful compilation.

  • Grove I2C sensors can be connected directly to the I2C bus.
  • Non-I2C sensors can be connected via available GPIO pins, allowing for flexible integration of various sensor types.

Core Functionality(DHT20 Sensor)

1. Sensor Initialization

The DHT20 driver implements the complete initialization process:

  • Step1. Reset Sensor: Send 0xBA reset command
  • Step2. System Configuration: Write [0x08, 0x00] to register 0xE1
  • Step3. Calibration Check: Check status register bit[3] to ensure sensor is calibrated
  • Step4. Retry Mechanism: Retry up to 5 times if calibration fails
esp_err_t ret = dht20_sensor_->Initialize();
if (ret == ESP_OK) {
ESP_LOGI(TAG, "DHT20 initialized successfully");
}

2. Temperature & Humidity Reading

Reading Process:

  • Step1. Trigger measurement: send command [0xAC, 0x33, 0x00]
  • Step2. Wait for measurement: delay 80ms
  • Step3. Read 8 bytes: [status, humidity High, Middle, Low | temperature High, Middle, Low, CRC]
  • Step4. Data Parsing:
    • Humidity = (20-bit raw value) × 100 / 2^20
    • Temperature = (20-bit raw value) × 200 / 2^20 - 50
float temperature, humidity;
esp_err_t ret = dht20_sensor_->ReadTempAndHumidity(temperature, humidity);
if (ret == ESP_OK) {
printf("Temperature: %.2f°C, Humidity: %.2f%%\n", temperature, humidity);
}

3. I2C Communication Protocol

FunctionCommand/RegisterData
Reset Sensor0xBA-
Configuration Register0xE1[0x08, 0x00]
Trigger Measurement-[0xAC, 0x33, 0x00]
Read Status-1 byte
Read Data-7 bytes

4. Status Byte Definition

BitFunctionValue
bit[7]Measurement Status1=Measuring, 0=Idle
bit[6:4]Reserved-
bit[3]Calibration Status1=Calibrated, 0=Not Calibrated
bit[2:0]Reserved-

5. Data Parsing Algorithm

// Humidity data (20 bits)
uint32_t humidity_raw = (data[1] << 12) | (data[2] << 4) | (data[3] >> 4);
float humidity = humidity_raw * 100.0f / 1048576.0f;

// Temperature data (20 bits)
uint32_t temperature_raw = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5];
float temperature = temperature_raw * 200.0f / 1048576.0f - 50.0f;

Core Functionality(Internal MCP Tool Interface)

1. Function Description

  • Tool name : self.grove.get_temperature_humidity
  • Tool application : Read current temperature and humidity from the Grove-connected DHT20 sensor

2. Example Call

The AI can call it as follows:

result = call_tool("self.grove.get_temperature_humidity", {})

3. Return Format

  • Success:

    {
    "temperature": 25.32,
    "humidity": 65.47,
    "status": "ok"
    }
  • Failure:

    {
    "error": "DHT20 sensor not initialized"
    }

    or

    {
    "error": "Failed to read DHT20: ESP_ERR_TIMEOUT"
    }

4. Modified Code Part and Explanation

Added Header Files Include

  • Add the following include statements at the beginning of sensecap_watcher.cc:

    #include "dht20.h"
    #include "mcp_server.h"
  • Purpose

    • dht20.h — Introduces the driver APIs for the DHT20 sensor, enabling temperature and humidity reading.

    • mcp_server.h — Provides APIs related to the internal MCP server, allowing communication and registration of MCP tools.

Added Member Variable

  • Add the following member variable in the SensecapWatcher class:

    DHT20* dht20_sensor_ = nullptr;
  • Purpose

    • dht20_sensor_ — A pointer to the DHT20 sensor object connected via the Grove interface.
      It is used to initialize, store, and access the temperature and humidity sensor throughout the lifecycle of the SensecapWatcher instance.
    • It is assigned in InitializeDHT20() and used by MCP tools to read sensor data.
    • Initialized to nullptr to indicate the sensor has not yet been set up, with null-checks performed before reading.

Added DHT20 Initialization and MCP Tool

  • Add the following member function in the SensecapWatcher class:

    void InitializeDHT20() {
    ESP_LOGI(TAG, "Initialize DHT20 sensor on Grove port");

    // Create DHT20 sensor instance
    dht20_sensor_ = new DHT20(i2c_bus_);

    // Initialize and calibrate the sensor
    esp_err_t ret = dht20_sensor_->Initialize();
    if (ret != ESP_OK) {
    ESP_LOGE(TAG, "DHT20 initialization failed: %s", esp_err_to_name(ret));
    delete dht20_sensor_;
    dht20_sensor_ = nullptr;
    return;
    }

    ESP_LOGI(TAG, "DHT20 sensor initialized successfully");

    // Register MCP tool for reading temperature and humidity
    auto& mcp_server = McpServer::GetInstance();
    mcp_server.AddTool("self.grove.get_temperature_humidity",
    "Read temperature and humidity from the Grove-connected DHT20 sensor.\n"
    "Return format: {\"temperature\": value(°C), \"humidity\": value(%), \"status\": \"ok\"}\n"
    "If reading fails, returns an error message.",
    PropertyList(),
    [this](const PropertyList&) -> ReturnValue {
    if (!dht20_sensor_) {
    return "{\"error\": \"DHT20 sensor not initialized\"}";
    }

    float temperature = 0.0f;
    float humidity = 0.0f;

    esp_err_t ret = dht20_sensor_->ReadTempAndHumidity(temperature, humidity);
    if (ret != ESP_OK) {
    std::string error_msg = "{\"error\": \"Failed to read DHT20: ";
    error_msg += esp_err_to_name(ret);
    error_msg += "\"}";
    return error_msg;
    }

    // Format response as JSON
    char buffer[128];
    snprintf(buffer, sizeof(buffer),
    "{\"temperature\": %.2f, \"humidity\": %.2f, \"status\": \"ok\"}",
    temperature, humidity);
    return std::string(buffer);
    });

    ESP_LOGI(TAG, "DHT20 MCP tool registered: self.grove.get_temperature_humidity");
    }
  • Add DHT20 Initialization Call in Constructor

    SensecapWatcher() {
    ...
    InitializeCamera();
    InitializeDHT20(); // Initialize Grove DHT20 sensor
    }
  • Purpose

    • InitializeDHT20() — Initializes the DHT20 sensor on the Grove interface and registers an MCP tool to allow internal access to temperature and humidity data.
    • The function creates the DHT20 instance, calibrates it, and checks for initialization errors.
    • If initialization succeeds, it registers the MCP tool self.grove.get_temperature_humidity for reading sensor data via JSON.
    • Called in the constructor of SensecapWatcher to ensure the sensor is ready when the board starts:

Troubleshooting

Common Issues

  1. Sensor Initialization Failure
  • Possible Causes:
    • Grove interface not powered correctly
    • I2C connection issues
    • Sensor not connected or damaged
  • Solution
    uint32_t grove_power = esp_io_expander_get_level(io_exp_handle, BSP_PWR_GROVE);
    ESP_LOGI(TAG, "Grove power status: %d", grove_power);
  1. Read Timeout
  • Possible Causes:
    • Sensor busy
    • I2C bus conflict
  • Solution
    • Ensure ≥1s interval between readings
    • Check for other devices on I2C
  1. Invalid Data
  • Possible Causes:
    • Sensor not calibrated
    • CRC check failed (not implemented in current version)
  • Solution:
    • Re-initialize sensor
    • Consider implementing CRC check (data[6])

References

Maintenance

code resides in main/boards/sensecap-watcher/, does not affect other boards.

Technical Support

Loading Comments...