8.19
【项目目标】
通过小智MCP服务远程获取ESP32 S3外接DHT11/22模块测得温湿度数据,并通过继电器控制小风扇改善湿度,从而完成一个微型的环境监测模型。


材料清单
- DHT22/DHT11 X1
- I2C128X64 1036驱动 LED屏 X1
- ESP32 S3开发板 X1
- 3V继电器 X1
步骤1 相关库安装及DHT11/22数据显示到OLED屏测试
温湿度模块我选用的是DFrobot DHT11/22模块。
1、搜索 DHT sensor library → 选择 Adafruit 维护的版本(当前最新为1.4.4+),库依赖 Adafruit Unified Sensor Library(需同步安装)。

本测试项目中DHT模块接线。

(DF DHT11/22模块上有上拉电阻)
2、安装库:U8g2库,用于支持在SSD1036驱动I2C OLED屏上显示数据。

3、0.96 128X64 SSD1036驱动屏接线


4、代码测试读取温湿度数据并显示在OLED屏。
#include <DHT.h>
#include <U8g2lib.h>
#include <Arduino.h>
#include <FreeRTOS.h>
// 硬件配置
#define DHTPIN 4 // DHT11数据引脚(GPIO4)
#define DHTTYPE DHT22
#define SCREEN_SDA 8 // OLED SDA (GPIO8)
#define SCREEN_SCL 9 // OLED SCL (GPIO9)
// 全局变量
volatile float temperature = NAN;
volatile float humidity = NAN;
// OLED对象 (软件I2C)
U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(
U8G2_R0,
SCREEN_SCL,
SCREEN_SDA,
U8X8_PIN_NONE
);
DHT dht(DHTPIN, DHTTYPE); // 传感器对象
//==================== OLED显示任务 ====================//
void displayTask(void *pvParameters) {
for(;;) {
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_wqy14_t_gb2312); // 使用14像素字体
// 标题行(顶部居中)
u8g2.drawUTF8((128 - u8g2.getUTF8Width("环境监测"))/2, 12, "环境监测");
// 温度显示(左对齐)
u8g2.drawUTF8(0, 32, "温度:");
u8g2.setCursor(40, 32);
if(!isnan(temperature)) u8g2.print(temperature, 1);
else u8g2.print("--.-");
u8g2.drawUTF8(70, 32, "°C");
// 湿度显示(右对齐)
u8g2.drawUTF8(0, 52, "湿度:");
u8g2.setCursor(40, 52);
if(!isnan(humidity)) u8g2.print(humidity, 1);
else u8g2.print("--.-");
u8g2.drawUTF8(70, 52, "%");
u8g2.sendBuffer();
vTaskDelay(1000 / portTICK_PERIOD_MS); // 1秒刷新
}
}
//==================== 传感器任务 ====================//
void sensorTask(void *pvParameters) {
for(;;) {
float t = dht.readTemperature();
float h = dht.readHumidity();
if (!isnan(t) && !isnan(h)) {
temperature = t;
humidity = h;
}
vTaskDelay(3000 / portTICK_PERIOD_MS); // 3秒读取
}
}
//==================== 主设置函数 ====================//
void setup() {
Serial.begin(115200);
// 初始化硬件
dht.begin();
// OLED初始化
u8g2.begin();
u8g2.enableUTF8Print();
u8g2.setFont(u8g2_font_wqy14_t_gb2312);
u8g2.clearBuffer();
// 居中显示启动信息
u8g2.drawUTF8(
(128 - u8g2.getUTF8Width("系统启动中..."))/2,
35,
"系统启动中..."
);
u8g2.sendBuffer();
delay(2000); // 显示2秒启动画面
// 创建FreeRTOS任务
xTaskCreatePinnedToCore(
sensorTask,
"SensorTask",
2048, // 优化内存占用
NULL,
1,
NULL,
0
);
xTaskCreatePinnedToCore(
displayTask,
"DisplayTask",
2048,
NULL,
1,
NULL,
0
);
}
//==================== 主循环 ====================//
void loop() {
// 空循环,所有功能在FreeRTOS任务中运行
delay(1000);
}
【注】上面代码修改#define DHTTYPE DHT11为#define DHTTYPE DHT22,就可以使用DHT22模块。

步骤2 小智入场,语音控制
我们想实现小智语音控制指令
数据查询:当前温度/当前湿度,DHT11模块接在IO4。
风扇控制:打开风扇/关闭风扇,风扇用接在IO6的继电器控制开关。

系统结构

基本功能

【注】
#define DHTTYPE DHT11换成#define DHTTYPE DHT22,就可以更换模块。

#include <WiFi.h>
#include <WebSocketMCP.h>
#include <DHT.h>
#include <U8g2lib.h>
#include <ArduinoJson.h>
#include <FreeRTOS.h>
// 硬件配置
#define DHTPIN 4 // DHT11数据引脚(GPIO4)
#define DHTTYPE DHT11
#define RELAY_PIN 6 // 继电器控制风扇(GPIO6)
#define SCREEN_SDA 8 // OLED SDA (GPIO8)
#define SCREEN_SCL 9 // OLED SCL (GPIO9)
// 全局变量
volatile float temperature = NAN;
volatile float humidity = NAN;
volatile bool fanState = false;
// OLED对象 (软件I2C)
U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(
U8G2_R0,
SCREEN_SCL,
SCREEN_SDA,
U8X8_PIN_NONE
);
DHT dht(DHTPIN, DHTTYPE); // 传感器对象
// 网络配置
// WiFi配置
const char* ssid = "your-ssid";
const char* password = "your-password";
const char* mcpEndpoint = "ws://your-mcp-server:port/path"; // 替换完整token
WebSocketMCP mcpClient;
//==================== 前置声明 ====================//
void onConnectionStatus(bool connected);
void registerMcpTools();
void displayTask(void *pvParameters);
void sensorTask(void *pvParameters);
WebSocketMCP::ToolResponse fanControlCallback(const String& args);
WebSocketMCP::ToolResponse sensorQueryCallback(const String& args);
//==================== OLED显示任务 ====================//
void displayTask(void *pvParameters) {
for(;;) {
u8g2.clearBuffer();
u8g2.setFont(u8g2_font_wqy12_t_gb2312); // 改用12像素字体节省空间
// 标题行(顶部居中)
u8g2.drawUTF8((128 - u8g2.getUTF8Width("环境监测"))/2, 12, "环境监测");
// 温度显示(左对齐)
u8g2.drawUTF8(0, 28, "温度:");
u8g2.setCursor(40, 28);
if(!isnan(temperature)) u8g2.print(temperature, 1);
else u8g2.print("--.-");
u8g2.drawUTF8(70, 28, "°C");
// 湿度显示(右对齐)
u8g2.drawUTF8(0, 44, "湿度:");
u8g2.setCursor(40, 44);
if(!isnan(humidity)) u8g2.print(humidity, 1);
else u8g2.print("--.-");
u8g2.drawUTF8(70, 44, "%");
// 风扇状态(底部居中)
String fanText = "风扇:" + String(fanState ? "开启" : "关闭");
u8g2.drawUTF8((128 - u8g2.getUTF8Width(fanText.c_str()))/2, 60, fanText.c_str());
u8g2.sendBuffer();
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
//==================== 传感器任务 ====================//
void sensorTask(void *pvParameters) {
for(;;) {
float t = dht.readTemperature();
float h = dht.readHumidity();
if (!isnan(t) && !isnan(h)) {
temperature = t;
humidity = h;
}
vTaskDelay(3000 / portTICK_PERIOD_MS);
}
}
//==================== MCP工具回调 ====================//
WebSocketMCP::ToolResponse fanControlCallback(const String& args) {
DynamicJsonDocument doc(256);
deserializeJson(doc, args);
String state = doc["state"].as<String>();
if (state == "on") {
digitalWrite(RELAY_PIN, HIGH);
fanState = true;
return WebSocketMCP::ToolResponse("{\"status\":\"FAN_ON\"}");
} else {
digitalWrite(RELAY_PIN, LOW);
fanState = false;
return WebSocketMCP::ToolResponse("{\"status\":\"FAN_OFF\"}");
}
}
WebSocketMCP::ToolResponse sensorQueryCallback(const String& args) {
String jsonResponse = "{\"temperature\":" + String(temperature) +
",\"humidity\":" + String(humidity) + "}";
return WebSocketMCP::ToolResponse(jsonResponse);
}
//==================== 工具注册 ====================//
void registerMcpTools() {
// 风扇控制工具
mcpClient.registerTool(
"fan_control",
"控制风扇开关",
"{\"properties\":{\"state\":{\"enum\":[\"on\",\"off\"]}}}",
fanControlCallback
);
// 温湿度查询工具
mcpClient.registerTool(
"read_dht",
"查询温湿度",
"{}",
sensorQueryCallback
);
}
//==================== 连接状态回调 ====================//
void onConnectionStatus(bool connected) {
if (connected) {
Serial.println("[MCP] 已连接");
registerMcpTools(); // 关键!连接成功后注册工具
} else {
Serial.println("[MCP] 已断开");
}
}
//==================== 主设置函数 ====================//
void setup() {
Serial.begin(115200);
// 硬件初始化
pinMode(RELAY_PIN, OUTPUT);
digitalWrite(RELAY_PIN, LOW);
dht.begin();
// OLED初始化
u8g2.begin();
u8g2.enableUTF8Print();
u8g2.setFont(u8g2_font_wqy14_t_gb2312);
u8g2.clearBuffer();
u8g2.drawUTF8(20, 35, "系统启动中...");
u8g2.sendBuffer();
// WiFi连接(带进度提示)
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
u8g2.clearBuffer();
u8g2.drawUTF8(0, 30, "WiFi连接中...");
u8g2.sendBuffer();
delay(500);
}
// MCP连接
mcpClient.begin(mcpEndpoint, onConnectionStatus);
// 创建FreeRTOS任务
xTaskCreatePinnedToCore(
sensorTask,
"SensorTask",
2048, // 优化内存占用
NULL,
1,
NULL,
0
);
xTaskCreatePinnedToCore(
displayTask,
"DisplayTask",
2048,
NULL,
1,
NULL,
0
);
}
//==================== 主循环 ====================//
void loop() {
mcpClient.loop(); // 处理MCP通信
delay(10);
}
上传,测试通过。
小智后台:

评论