ساخت وب سرور دما و رطوبت با سنسور DHT و ESP32

آیا تا به حال آرزو کردهاید که بتوانید با موبایل، تبلت یا کامپیوتر خود در هر زمان دما و رطوبت خانه، یخچالهای بزرگ یا انبار شراب خود را کنترل کنید؟ اگر چنین است، این پروژه اینترنت اشیا (IoT) میتواند نقطه شروع بسیار خوبی باشد.
این پروژه از ESP32 بهعنوان دستگاه کنترل استفاده میکند که به یک شبکه WiFi موجود متصل شده و یک وبسرور ایجاد میکند. هنگامی که دستگاهی به این وبسرور متصل میشود، ESP32 دما و رطوبت نسبی را از سنسور DHT11/DHT22 میخواند و با رابط کاربری زیبا آن را به مرورگر وب دستگاه ارسال میکند. هیجانزدهاید؟ بیایید شروع کنیم!
پیشنهاد میشود ابتدا این مقاله را بخوانید: آموزش ساخت 4 وب سرور با برد ESP32
چرا از سنسور داخلی ESP32 استفاده نمیکنیم؟
در واقع ESP32 شامل یک سنسور دما با محدوده دمایی -40°C تا 125°C است. این سنسور دما ولتاژی متناسب با دما تولید میکند که سپس توسط یک مبدل آنالوگ به دیجیتال داخلی به فرم دیجیتال تبدیل میشود.
طبق دیتاشیت ESP32، مشکل این سنسور دما این است که آفست (Offset) آن از چیپ به چیپ متفاوت است، به دلیل تغییرات فرآیندی. همچنین اندازهگیری دما تحت تأثیر حرارت تولید شده توسط مدار Wi-Fi قرار میگیرد. بنابراین، سنسور دمای داخلی تنها برای کاربردهایی مناسب است که تغییرات دما را شناسایی میکنند و نه دمای مطلق را.
اتصال سنسور DHT11/DHT22 به ESP32
اتصال سنسور DHT11/DHT22 به ESP32 نسبتاً ساده است. ابتدا ESP32 را روی برد بورد قرار دهید، مطمئن شوید که هر سمت برد روی بخش متفاوتی از برد بورد قرار دارد.
سنسور را در کنار ESP32 روی برد بورد قرار دهید. پایه VCC سنسور را به پایه 3.3V ESP32 و زمین را به GND متصل کنید. پایه Data سنسور را به پایه D4 ESP32 وصل کنید. در نهایت، یک مقاومت 10KΩ Pull-up بین VCC و خط Data اضافه کنید تا خط همیشه در حالت HIGH باقی بماند و ارتباط صحیح بین سنسور و ESP32 برقرار شود.
تصویر زیر نحوه سیمکشی را نشان میدهد:

نصب کتابخانه سنسور DHT
برای شروع خواندن دادههای سنسور، ابتدا باید کتابخانه سنسور DHT را نصب کنید. این کتابخانه از طریق مدیر کتابخانه Arduino در دسترس است.
برای نصب، به مسیر Sketch > Include Library > Manage Libraries… بروید و صبر کنید تا شاخص کتابخانهها دانلود و فهرست کتابخانههای نصبشده بهروز شود.
در کادر جستجو عبارت «DHT sensor» را وارد کنید. کتابخانه DHT توسط Adafruit را پیدا کرده و روی Install کلیک کنید.
کتابخانه سنسور DHT از بکاند پشتیبانی Adafruit Sensor استفاده میکند، بنابراین کتابخانه Adafruit Unified Sensor را نیز در مدیر کتابخانه جستجو کرده و نصب کنید.
ایجاد وبسرور ESP32 حالت WiFi Station (STA)
حالا به بخش جذاب پروژه میرسیم! همانطور که عنوان نشان میدهد، ما یک وبسرور ESP32 را در حالت Station (STA) پیکربندی میکنیم تا صفحات وب را به هر کلاینت متصل در شبکه موجود ارائه دهد.
قبل از آپلود اسکچ، باید دو متغیر زیر را با اطلاعات شبکه خود جایگزین کنید تا ESP32 بتواند به شبکه موجود متصل شود.
const char* ssid = "YourNetworkName"; // نام شبکه خود را وارد کنید const char* password = "YourPassword"; // رمز عبور شبکه را وارد کنید
پس از اتمام، اسکچ را اجرا کنید و سپس آن را به تفصیل بررسی خواهیم کرد.
کد کامل در فایل دانلودی انتهای صفحه قرار داده شده است. در اینجا قسمت های مختلف کد را توضیح میدهیم.
دسترسی به وبسرور
پس از آپلود اسکچ، مانیتور سریال را با نرخ 115200 باز کرده و دکمه RESET روی ESP32 را فشار دهید. در صورت صحت عملکرد، آدرس IP دینامیک اختصاص یافته توسط روتر و پیام «HTTP server started» نمایش داده میشود.

سپس مرورگر را باز کرده و به آدرس IP نمایش داده شده در مانیتور سریال بروید. ESP32 صفحه وبی با دمای فعلی و رطوبت نسبی ارائه میدهد.

توضیح کد آردوینو وب سرور دما و رطوبت ESP32
این برنامه با وارد کردن کتابخانههای مورد نیاز شروع میشود:
WiFi.h: شامل متدهای مخصوص ESP32 برای اتصال به شبکه WiFi است.WebServer.h: متدهایی برای راهاندازی وبسرور و مدیریت درخواستهای HTTP فراهم میکند بدون اینکه نگران جزئیات سطح پایین باشیم.DHT.h: برای کار با سنسورهای دما و رطوبت DHT استفاده میشود.
#include <WiFi.h> #include <WebServer.h> #include "DHT.h"
سپس نوع سنسور DHT مورد استفاده را مشخص میکنیم. باید خط مربوط به سنسور خود را از حالت کامنت خارج کنید:
//#define DHTTYPE DHT11 // DHT 11 //#define DHTTYPE DHT21 // DHT 21 (AM2301) #define DHTTYPE DHT22 // DHT 22 (AM2302), AM2321
چون وبسرور ESP32 در حالت Station (STA) راهاندازی میشود، باید به شبکه WiFi موجود متصل شود. بنابراین باید نام شبکه (SSID) و رمز عبور را مشخص کنیم:
/*Put your SSID & Password*/ const char* ssid = "YourNetworkName"; // وارد کردن SSID const char* password = "YourPassword"; // وارد کردن رمز عبور
بعد، یک شیء از کتابخانه WebServer ایجاد میکنیم تا بتوانیم از توابع آن استفاده کنیم. این شیء هنگام ساخت پورت گوش دادن سرور را میگیرد. از آنجا که HTTP به طور پیشفرض از پورت ۸۰ استفاده میکند، این مقدار را مشخص میکنیم تا بدون نیاز به وارد کردن پورت در URL بتوانیم به سرور متصل شویم:
// ایجاد شیء وبسرور WebServer server(80);
سپس شماره پایه GPIO که سنسور DHT به آن متصل است را تعریف میکنیم و یک شیء DHT میسازیم تا بتوانیم از توابع مخصوص کتابخانه DHT استفاده کنیم:
// سنسور DHT uint8_t DHTPin = 4; // مقداردهی اولیه سنسور DHT DHT dht(DHTPin, DHTTYPE);
در ادامه دو متغیر float برای ذخیره دما و رطوبت تعریف میکنیم:
float Temperature; float Humidity;
درون تابع Setup()
در تابع setup() وبسرور خود را پیکربندی میکنیم. ابتدا یک ارتباط سریال برای دیباگ راهاندازی کرده و پایه GPIO را بهصورت INPUT تنظیم میکنیم. همچنین شیء DHT را مقداردهی اولیه میکنیم.
Serial.begin(115200); delay(100); pinMode(DHTPin, INPUT); dht.begin();
سپس با استفاده از تابع WiFi.begin() به شبکه WiFi متصل میشویم. این تابع SSID (نام شبکه) و رمز عبور را بهعنوان پارامتر میگیرد.
Serial.println("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password); در حین تلاش ESP32 برای اتصال به شبکه، میتوانیم وضعیت اتصال را با استفاده از WiFi.status() بررسی کنیم.
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
} پس از اتصال به شبکه، با استفاده از WiFi.localIP() آدرس IP اختصاص داده شده به ESP32 را دریافت میکنیم.
Serial.println("");
Serial.println("WiFi connected..!");
Serial.print("Got IP: ");
Serial.println(WiFi.localIP()); برای مدیریت درخواستهای HTTP، باید مشخص کنیم هنگام دسترسی به یک URL خاص چه کدی اجرا شود. برای این کار از متد .on() استفاده میکنیم که دو پارامتر میگیرد: مسیر نسبی URL و نام تابعی که هنگام بازدید آن URL اجرا میشود.
server.on("/", handle_OnConnect); اگر کلاینت به URLای مراجعه کند که مشخص نشده است، سرور باید خطای 404 (صفحه یافت نشد) را بازگرداند. برای این کار از متد server.onNotFound() استفاده میکنیم.
server.onNotFound(handle_NotFound);
در نهایت با فراخوانی server.begin() سرور را راهاندازی میکنیم.
server.begin();
Serial.println("HTTP server started"); درون تابع Loop()
در تابع loop()، درخواستهای HTTP دریافتی پردازش میشوند. برای این کار از متد handleClient() شیء سرور استفاده میکنیم.
server.handleClient();
نوشتن تابع handle_OnConnect()
این تابع که به URL ریشه (/) متصل شده است، ابتدا مقادیر دما و رطوبت را از سنسور میخواند و سپس با متد send() پاسخ HTTP را ارسال میکند.
void handle_OnConnect() {
Temperature = dht.readTemperature();
Humidity = dht.readHumidity();
server.send(200, "text/html", SendHTML(Temperature,Humidity));
} برای صفحه خطای 404 نیز تابع مشابهی نوشته شده است:
void handle_NotFound(){
server.send(404, "text/plain", "Not found");
} نمایش صفحه وب HTML
هرگاه وبسرور ESP32 درخواست از یک کلاینت وب دریافت کند، تابع SendHTML() یک صفحه وب تولید میکند. این تابع با استفاده از مقادیر دما و رطوبت، محتوای HTML را بهصورت داینامیک تولید کرده و به متد server.send() بازمیگرداند.
String SendHTML(float Temperaturestat,float Humiditystat){
String ptr = "<!DOCTYPE html> <html>\n"; تگ <meta> برای ریسپانسیو بودن صفحه استفاده میشود و تگ <title> عنوان صفحه را تعیین میکند.
ptr +="<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n"; ptr +="<title>ESP32 Weather Report</title>\n";
استایلدهی صفحه وب
CSS داخلی برای ظاهر کلی صفحه استفاده میشود؛ فونت Helvetica انتخاب شده و محتوای صفحه به صورت inline-block و مرکزچین نمایش داده میشود.
ptr +="<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n";
ptr +="body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;}\n";
ptr +="p {font-size: 24px;color: #444444;margin-bottom: 10px;}\n";
ptr +="</style>\n";
ptr +="</head>\n";
ptr +="<body>\n"; تنظیم عنوان صفحه وب
عنوان صفحه وب با تگ <h1> مشخص میشود و قابل تغییر برای کاربرد شما است.
ptr +="<div id=\"webpage\">\n"; ptr +="<h1>ESP32 Weather Report</h1>\n";
نمایش دما و رطوبت در صفحه وب
مقادیر دما و رطوبت با تگ <p> نمایش داده میشوند و برای نمایش به عدد صحیح تبدیل شدهاند.
ptr +="<p>Temperature: "; ptr +=(int)Temperaturestat; ptr +="°C</p>"; ptr +="<p>Humidity: "; ptr +=(int)Humiditystat; ptr +="%</p>"; ptr +="</div>\n"; ptr +="</body>\n"; ptr +="</html>\n"; return ptr; }
استایلدهی صفحه وب برای ظاهر حرفهایتر
برنامهنویسان معمولاً به ظاهر صفحه وب اهمیت نمیدهند، اما با کمی تلاش میتوانیم وبسایت خود را جذابتر کنیم. در این بخش، تابع SendHTML() را جایگزین نسخه قبلی کرده و از فونت و CSS پیشرفتهتری استفاده میکنیم تا دما و رطوبت با ظاهر حرفهای نمایش داده شوند.
String SendHTML(float TempCstat,float TempFstat,float Humiditystat){
String ptr = "<!DOCTYPE html> <html>\n";
ptr +="<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";
ptr +="<link href=\"https://fonts.googleapis.com/css?family=Open+Sans:300,400,600\" rel=\"stylesheet\">\n";
ptr +="<title>ESP32 Weather Report</title>\n";
ptr +="<style>html { font-family: 'Open Sans', sans-serif; display: block; margin: 0px auto; text-align: center;color: #333333;}\n";
ptr +="body{margin-top: 50px;}\n";
ptr +="h1 {margin: 50px auto 30px;}\n";
ptr +=".side-by-side{display: inline-block;vertical-align: middle;position: relative;}\n";
ptr +=".humidity-icon{background-color: #3498db;width: 30px;height: 30px;border-radius: 50%;line-height: 36px;}\n";
ptr +=".humidity-text{font-weight: 600;padding-left: 15px;font-size: 19px;width: 160px;text-align: left;}\n";
ptr +=".humidity{font-weight: 300;font-size: 60px;color: #3498db;}\n";
ptr +=".temperature-icon{background-color: #f39c12;width: 30px;height: 30px;border-radius: 50%;line-height: 40px;}\n";
ptr +=".temperature-text{font-weight: 600;padding-left: 15px;font-size: 19px;width: 160px;text-align: left;}\n";
ptr +=".temperature{font-weight: 300;font-size: 60px;color: #f39c12;}\n";
ptr +=".superscript{font-size: 17px;font-weight: 600;position: absolute;right: -20px;top: 15px;}\n";
ptr +=".data{padding: 10px;}\n";
ptr +="</style>\n";
ptr +="</head>\n";
ptr +="<body>\n";
ptr +="<div id=\"webpage\">\n";
ptr +="<h1>ESP32 Weather Report</h1>\n";
ptr +="<div class=\"data\">\n";
ptr +="<div class=\"side-by-side temperature-icon\">\n";
ptr +="<svg version=\"1.1\" id=\"Layer_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\" width=\"9.915px\" height=\"22px\" viewBox=\"0 0 9.915 22\" enable-background=\"new 0 0 9.915 22\" xml:space=\"preserve\">\n";
ptr +="<path fill=\"#FFFFFF\" d=\"M3.498,0.53c0.377-0.331,0.877-0.501,1.374-0.527C5.697-0.04,6.522,0.421,6.924,1.142 c0.237,0.399,0.315,0.871,0.311,1.33C7.229,5.856,7.245,9.24,7.227,12.625c1.019,0.539,1.855,1.424,2.301,2.491 c0.491,1.163,0.518,2.514,0.062,3.693c-0.414,1.102-1.24,2.038-2.276,2.594c-1.056,0.583-2.331,0.743-3.501,0.463 c-1.417-0.323-2.659-1.314-3.3-2.617C0.014,18.26-0.115,17.104,0.1,16.022c0.296-1.443,1.274-2.717,2.58-3.394 c0.013-3.44,0-6.881,0.007-10.322C2.674,1.634,2.974,0.955,3.498,0.53z\"/>\n";
ptr +="</svg>\n";
ptr +="</div>\n";
ptr +="<div class=\"side-by-side temperature-text\">Temperature</div>\n";
ptr +="<div class=\"side-by-side temperature\">";
ptr +=(int)TempCstat;
ptr +="<span class=\"superscript\">°C</span></div>\n";
ptr +="</div>\n";
ptr +="<div class=\"data\">\n";
ptr +="<div class=\"side-by-side humidity-icon\">\n";
ptr +="<svg version=\"1.1\" id=\"Layer_2\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\" width=\"12px\" height=\"17.955px\" viewBox=\"0 0 13 17.955\" enable-background=\"new 0 0 13 17.955\" xml:space=\"preserve\">\n";
ptr +="<path fill=\"#FFFFFF\" d=\"M1.819,6.217C3.139,4.064,6.5,0,6.5,0s3.363,4.064,4.681,6.217c1.793,2.926,2.133,5.05,1.571,7.057 c-0.438,1.574-2.264,4.681-6.252,4.681c-3.988,0-5.813-3.107-6.252-4.681C-0.313,11.267,0.026,9.143,1.819,6.217\"></path>\n";
ptr +="</svg>\n";
ptr +="</div>\n";
ptr +="<div class=\"side-by-side humidity-text\">Humidity</div>\n";
ptr +="<div class=\"side-by-side humidity\">";
ptr +=(int)Humiditystat;
ptr +="<span class=\"superscript\">%</span></div>\n";
ptr +="</div>\n";
ptr +="</div>\n";
ptr +="</body>\n";
ptr +="</html>\n";
return ptr;
}
بهبود 1 – رفرش خودکار صفحه
برای بهروزرسانی مقادیر سنسور میتوان صفحه وب را بهصورت خودکار رفرش کرد. با استفاده از تگ متای زیر در <head>، مرورگر هر دو ثانیه یکبار صفحه را بارگذاری میکند:
<meta http-equiv="refresh" content="2">
بهبود 2 – بارگذاری داینامیک دادههای سنسور با AJAX
رفرش کل صفحه برای وبسایتهای بزرگ مناسب نیست، بنابراین میتوانیم از AJAX استفاده کنیم. AJAX اجازه میدهد دادهها بدون بارگذاری مجدد صفحه، از سرور دریافت شوند.
<script>
setInterval(loadDoc,200);
function loadDoc() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("webpage").innerHTML = this.responseText;
}
};
xhttp.open("GET", "/", true);
xhttp.send();
}
</script> این اسکریپت جاوااسکریپت است و باید داخل تگ <script> قرار گیرد. با استفاده از تابع setInterval()، تابع loadDoc() هر 200 میلیثانیه اجرا میشود. در این تابع یک شیء XMLHttpRequest() ساخته شده و از آن برای ارسال درخواست GET به سرور استفاده میکنیم.
متد onreadystatechange وضعیت آمادهسازی (readyState) و وضعیت پاسخ (status) را بررسی میکند. وقتی readyState برابر 4 و status برابر 200 باشد، پاسخ کامل شده و محتوای المنت با id webpage (که دما و رطوبت را نمایش میدهد) بهروزرسانی میشود.
سپس با متدهای open() و send() درخواست HTTP اجرا میشود.
موارد موجود در فایل : سورس کامل
برای دانلود فایل ها باید حساب کاربری داشته باشید ثبت نام / ورود







