آموزش ها
5/1 خانه هوشمند اتصال سنسور dht11 به اپلکیشن فلاتر
قسمت 5 بخش اول آموزش کد آردوینو برای dt11 برای دریافت دما و رطوبت برای خانه هوشمن
مقدمه
مدلهای اینترنت اشیاء (IoT) به شما امکان میدهند سنسورها را به اپلیکیشن موبایل یا وب متصل کنید تا دادهها را در لحظه ببینید. در این مقاله، میخواهم نشان دهم چگونه سنسور DHT11 (برای اندازهگیری دما و رطوبت) را به یک برد ESP32 متصل کنید، دادهها را از طریق پروتکل MQTT ارسال کنید، و یک اپلیکیشن فلاتر بسازید که آن دادهها را نمایش دهد.
کد شما ساختار چندوظیفهای (FreeRTOS tasks) در ESP32 را پیاده کرده است تا خواندن سنسور و ارسال MQTT را به صورت موازی انجام دهد. من این ساختار را تشریح میکنم، سپس بخش اپلیکیشن فلاتر را طراحی میکنم تا اطلاعات را از MQTT بگیرد و نمایش دهد.
این پروژه برای کسانی مناسب است که میخواهند بر ترکیب سختافزار، شبکه (MQTT) و موبایل تسلط بیشتری پیدا کنند.
ابتدا بررسی مختصر اجزای پروژه:
سنسور DHT11: سنسوری ساده برای خواندن دما و رطوبت.
برد ESP32: میکروکنترولی با امکانات WiFi مناسب برای پروژههای IoT.
پروتکل MQTT: پروتکلی سبک برای ارسال پیام بین دستگاهها با الگوی publish/subscribe.
اپلیکیشن فلاتر: رابط کاربری موبایل که بتواند پیامهای MQTT را دریافت کند و دادهها را نشان دهد.
محصولات استفاده شده
اصولی درباره DHT11 و محدودیتها
قبل از هر چیز، سنسور DHT11 محدودیتهایی دارد که باید در نظر بگیرید:
نرخ بهروزرسانی پایین: تا حدود هر ۱ ثانیه میتوان خواند.
دقت محدود: دقت دما ±2 درجه سلسیوس و رطوبت ±5٪.
در دماهای خیلی بالا یا پایین عملکرد ممکن است نامطلوب باشد.
سنسور در محیطهای مرطوب یا با نویز دیجیتال نزدیک ممکن است دچار خطا شود.
به همین دلیل، استفاده از تأخیر مناسب بین خواندنها و بررسی خطاهای مقدار NaN در کد شما مهم است (که شما در کد نیز کنترل کردهاید).
همچنین در پروژههای حساستر، ممکن است بخواهید از سنسورهای دقیقتر مثل DHT22 یا BME280 استفاده کنید.
توضیح کامل کد ESP32
کد ارسالی شامل دو تسک جداگانه (sensorTask و mqttTask) است و از FreeRTOS روی ESP32 استفاده میکند. اجازه دهید بخش به بخش توضیح دهم:
بخش کتابخانهها و پیکربندی
کد شما از کتابخانههای زیر استفاده میکند:
#include
#include
#include
#include
#include
#include
- #define DHTPIN 4
#define DHTTYPE DHT11
WiFi.h برای اتصال به شبکه WiFi - PubSubClient برای کار با MQTT
- ArduinoJson برای ساخت رشته JSON
- Adafruit_Sensor و DHT برای کار با سنسور DHT
سپس پین داده و نوع سنسور تعریف میشود:
#define DHTPIN 4
#define DHTTYPE DHT11
- و سپس یک شیء
DHT_Unifiedایجاد میشود:
DHT_Unified dht(DHTPIN, DHTTYPE);
- و سپس یک شیء
DHT_Unifiedایجاد میشود:
uint32_t delayMS;
float temp;
float humadity;
bool hvac = true;
delayMSبرای تأخیر بین خواندنها،tempوhumadityبرای ذخیره مقدار دما و رطوبت، وhvacیک متغیر کنترلی است.همچنین اطلاعات مربوط به سرور MQTT و شیء client:
const char *mqtt_server = MQTT_SERVER;
const uint16_t mqtt_port = MQTT_PORT;
WiFiClient espClient;
PubSubClient client(espClient);
پارامترهای MQTT_SERVER, MQTT_PORT, MQTT_USER, MQTT_PASSWORD در فایل secrets.h تعریف شدهاند و نباید در کد عمومی قرار گیرند.
تابع callback
وقتی پیام جدیدی از موضوعی که مشترک شدهایم میآید، این تابع فراخوانی میشود:
void callback(char *topic, byte *payload, unsigned int length) { … }
در اینجا تنها پیام و موضوع چاپ میشود، اما میتوانید بر اساس موضوع، عملیاتی انجام دهید.
تابع reconnect
اگر ارتباط MQTT قطع شود، این تابع تلاش میکند مجدداً به بروکر متصل شود:
while (!client.connected()) {
if (client.connect("ESP32Client", MQTT_USER, MQTT_PASSWORD)) {
client.subscribe("thermostat/command");
}
…
}
در اینجا پس از اتصال، در موضوع thermostat/command مشترک میشود تا فرمانها از اپلیکیشن بگیرد.
تابعهای تسکها و setup
در setup()، شما:
ارتباط سریال را شروع میکنید
سنسور DHT را آغاز میکنید
تأخیر بین خواندنها را محاسبه میکنید
به WiFi وصل میشوید
سرور MQTT را تنظیم میکنید
دو تسک مجزا روی هسته ۱ ایجاد میکنید:
sensorTaskوmqttTask
تسک sensorTask بهطور مداوم دما و رطوبت را میخواند و مقادیر را در متغیرهای جهانی temp و humadity ذخیره میکند.
تسک mqttTask نیز وضعیت اتصال MQTT را مدیریت میکند و هر ثانیه یک پیام JSON حاوی دادههای سنسور را روی موضوع thermostat/sensor منتشر میکند:
doc["temp"] = temp;
doc["hum"] = humadity;
doc["hvac"] = hvac;
serializeJson(doc, jsonString);
client.publish("thermostat/sensor", jsonString.c_str());
دقت به این نکته باشد که شما باید JSONDocument یا DynamicJsonDocument را استفاده کنید. (در کد شما صرف JsonDocument doc; نوشته شده؛ لازم است یکی از نسخههای مشخص مثل StaticJsonDocument<128> یا DynamicJsonDocument باشد).
گام به گام اجرای پروژه
مرحله ۱: راهاندازی محیط
نصب افزونه ESP32 برای Arduino IDE
نصب کتابخانههای
DHTوAdafruit Sensorنصب
PubSubClientوArduinoJsonآماده کردن IP یا آدرس بروکر MQTT شما (مثلاً Mosquitto محلی یا سرویس ابری)
مرحله ۲: تعریف secrets.h
در فایلی به نام secrets.h، مقادیر زیر را قرار دهید:
#define WIFI_SSID "نام_WiFi"
#define WIFI_PASSWORD "کلمه_عبور"
#define MQTT_SERVER "آدرس_بروکر"
#define MQTT_PORT 1883
#define MQTT_USER "نام_کاربری"
#define MQTT_PASSWORD "رمز_عبور"
مرحله ۳: آپلود کد به ESP32
کد کامل را در IDE خود قرار دهید، مطمئن شوید که انتخاب برد صحیح است (ESP32 Dev Module یا مدل شما) و سپس آپلود کنید.
از مانیتور سریال با نرخ 115200 استفاده کنید تا پیامها مانند اتصال WiFi، ارسال JSON و پیامهای خطا را ببینید.
مرحله ۴: آزمون بر روی MQTT
برای بررسی اینکه دادهها به درستی ارسال میشوند، میتوانید از یک کلاینت MQTT استفاده کنید مثل:
MQTT Explorer
MQTT.fx
یا حتی اپلیکیشن موبایل MQTT Dashboard
مشترک شوید به موضوع thermostat/sensor و ببینید پیامهایی شبیه زیر دریافت میشوند:
{"temp":25.5,"hum":60.0,"hvac":true}
اگر پیامها دریافت شدند، بخش ESP32 به درستی عمل میکند.
مرحله ۵: ساخت اپلیکیشن فلاتر
در ادامه، بخشی از کد فلاتر را ارائه میدهم که میتواند به بروکر MQTT متصل شود و پیامها را دریافت کند.
ابتدا در فایل pubspec.yaml وابستگی زیر را اضافه کنید:
dependencies:
mqtt_client: ^10.0.0
flutter:
sdk: flutter
سپس در فایل Dart (مثلاً mqtt_service.dart)، یک کلاس برای مدیریت اتصال MQTT بسازید:
import 'package:mqtt_client/mqtt_client.dart';
import 'package:mqtt_client/mqtt_server_client.dart';
class MqttService {
late MqttServerClient client;
Future connect(String server, String clientId) async {
client = MqttServerClient(server, clientId);
client.port = 1883;
client.logging(on: true);
client.keepAlivePeriod = 60;
client.onConnected = onConnected;
client.onDisconnected = onDisconnected;
client.onSubscribed = onSubscribed;
final connMess = MqttConnectMessage()
.withClientIdentifier(clientId)
.withWillTopic('willtopic')
.withWillMessage('My Will message')
.startClean()
.withWillQos(MqttQos.atLeastOnce);
client.connectionMessage = connMess;
try {
await client.connect();
} catch (e) {
print('MQTT client exception: $e');
client.disconnect();
}
}
void onConnected() {
print('Connected to MQTT broker');
client.subscribe('thermostat/sensor', MqttQos.atLeastOnce);
}
void onDisconnected() {
print('Disconnected from MQTT');
}
void onSubscribed(String topic) {
print('Subscribed to $topic');
}
}
سپس در main.dart یا ویجت اصلی خود:
import 'package:flutter/material.dart';
import 'mqtt_service.dart';
import 'package:mqtt_client/mqtt_client.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) =>
MaterialApp(home: SensorPage());
}
class SensorPage extends StatefulWidget {
@override
_SensorPageState createState() => _SensorPageState();
}
class _SensorPageState extends State {
late MqttService mqttService;
String temp = '--';
String hum = '--';
@override
void initState() {
super.initState();
mqttService = MqttService();
mqttService.connect('آدرس_بروکر_MQTT', 'flutter_client').then((_) {
mqttService.client.updates?.listen((List> c) {
final MqttPublishMessage recMess = c[0].payload as MqttPublishMessage;
final message =
MqttPublishPayload.bytesToStringAsString(recMess.payload.message);
print('Received message: $message');
// پیام JSON را پارس کن
final map = jsonDecode(message);
setState(() {
temp = map['temp'].toString();
hum = map['hum'].toString();
});
});
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('دما و رطوبت')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('دما: $temp °C',
style: TextStyle(fontSize: 24)),
SizedBox(height: 16),
Text('رطوبت: $hum %',
style: TextStyle(fontSize: 24)),
],
),
),
);
}
}
با این کد، اپلیکیشن فلاتر به بروکر MQTT وصل میشود، مشترک thermostat/sensor میشود و پیامها را شنود میکند. با دریافت پیام، JSON آن را تجزیه کرده و مقادیر دما و رطوبت را در رابط کاربری بهروزرسانی میکند.
نکات پیشرفته و بهبودها
کنترل فرمان از اپلیکیشن
از آنجایی که شما در کد ESP32 مشترک thermostat/command شدهاید، اپلیکیشن فلاتر میتواند پیامهایی روی آن موضوع بفرستد تا دستگاه ESP32 فرمانها را دریافت کند (مثلاً روشن/خاموش کردن HVAC). در فلاتر باید متد client.publish('thermostat/command', payload) را استفاده کنید.
نگهداری اتصال
در فلاتر باید مکانیزم reconnect در صورت افت اتصال MQTT پیاده شود. مثلاً اگر اتصال قطع شود، دوباره تلاش کند. در مثالهای آموزش MQTT در فلاتر این قابلیت درج شده است. Medium
امنیت
در پروژههای حقیقی، از TLS/SSL برای MQTT استفاده کنید تا ارتباط شما امن باشد. همچنین احراز هویت (نام کاربری و رمز عبور) را جدی بگیرید.
ذخیرهسازی دادهها
میتوانید دادههای دریافتی را در دیتابیس محلی (مثلاً SQLite یا Hive) ذخیره کنید تا تاریخچه مقادیر را نمایش دهید.
حاصل کار با پلتفرمهای دیگر
اگر هم بخواهید از طریق وب یا داشبورد آنلاین نمایش دهید، میتوانید ارتباط MQTT را به سمت سرور وب منتقل کنید یا از Node-RED یا Grafana استفاده کنید. در آموزشهای ESP32 به MQTT، این روش رایج است. Random Nerd Tutorials+1
مشکلات رایج و راه حلها
| مشکل | علت احتمالی | راه حل |
|---|---|---|
| دریافت پیام خالی یا NaN | خراب بودن خواندن سنسور | در کد کنترل isnan() را داشته باشید و اگر مقدار نامعتبر است دوباره تلاش کنید |
| عدم اتصال به WiFi | SSID یا رمز عبور اشتباه | بررسی مقدارهای WIFI_SSID و WIFI_PASSWORD و چاپ وضعیت WiFi در سریال |
| قطع شدن ارتباط MQTT | نداشتن مکانیزم reconnect | تابع reconnect را کامل پیاده کرده و در mqttTask آن را فرا بخوانید |
| خطا در پارس JSON در فلاتر | پیام دریافتی ممکن است فرمت JSON نداشته باشد | قبل از jsonDecode بررسی کنید که پیام بهدرستی دریافتی است |
| تداخل تسکها | منابع مشترک بین تسکها | از متغیرهای اتمی یا قفل (درصورت نیاز) استفاده کنید، البته در پروژه ساده معمولاً مشکلی پیش نمیآید |
جمعبندی
در این مقاله راهاندازی کامل سنسور DHT11 به همراه ESP32، ارسال دادهها از طریق MQTT و ساخت اپلیکیشن فلاتر برای نمایش آن را آموزش دادم. از کد شما به عنوان مبنا استفاده کردم و جزئیات آن را توضیح دادم، سپس بخش فلاتر را طراحی کردم تا ارتباط دوطرفه امکانپذیر باشد.