

في هذا الدرس سنتعلّم كيفية عرض حالة الطقس على صفحة ويب باستخدام لوحة راسبيري باي بيكو، بحيث يتم عرض درجة الحرارة ونسبة الرطوبة ومقياس جودة الهواء على صفحة ويب يمكن الوصول إليها من خلال أي حاسوب أو هاتف ذكي متصل بنفس شبكة الواي فاي المتصلة بها لوحة الراسبيري باي بيكو.

قم بتوصيل الأسلاك بين لوحة الراسبيري باي بيكو وحساس الحرارة والرطوبة وحساس جودة الهواء MQ135 كما هو موضح في الصورة التالية:

التوصيلات من لوحة راسبيرى باى بيكو 2W :
•نقوم بتوصيل منفذ ال VBUS بلوحة راسبيرى باى بيكو2W ← المنافذ الموجبة بلوحة التجارب
•منفذ ال GND بلوحة راسبيرى باى بيكو2W ←المنافذ السالبة بلوحة التجارب
التوصيلات من حساس الحرارة والرطوبة :
• منفذ ال VCC للحساس ← المنافذ الموجبة بلوحة التجارب
• منفذ ال GND للحساس ← المنافذ السالبة بلوحة التجارب
• منفذ الأشارة للحساس ← منفذ رقم 27 فى لوحة راسبيرى باى بيكو 2W
التوصيلات من حساس جودة الهواء MQ135:
• منفذ الVCC لحساس جودة الهواء ← المنافذ الموجبة للوحة التجارب
• منفذ ال GNDلحساس جودة الهواء ← المنافذ السالبة للوحة التجارب
• منفذ A0 لحساس جودة الهواء ← الطرف الحر للمقاومة ال10 كيلو أوم
• الطرف الأوسط بين المقاومة 10 كيلو اوم والمقاومة 20 كيلو اوم ← منفذ رقم 26 في بلوحة راسبيرى باى بيكو 2W
• الطرف الأخر للمقاومة ال 20 كيلو اوم ← المنافذ السالبة بلوحة التجارب
وظيفة الكود البرمجى التالى هى عرض درجة الحرارة ونسبة الرطوبة ومقياس جودة الهواء على صفحة ويب يمكن الوصول إليها من خلال أي حاسوب أو هاتف ذكي متصل بنفس شبكة الواي فاي المتصلة بها لوحة الراسبيري باي بيكو.
# استيراد الوحدات المطلوبة
import network
import socket
import time
from time import sleep
from machine import Pin, ADC
import dht
import sys
for i in range(3, 0, -1):
print(f"⏳ {i}...")
time.sleep(1)
print("✅ جاري بدء تشغيل محطة الطقس...\n")
# إعدادات WiFi
ssid = 'VOLTAAT' # اسم شبكتك
password = '12345678' # كلمة مرور WiFi
# تهيئة حساس DHT11
dht_pin = Pin(27, Pin.IN, Pin.PULL_UP)
sensor = dht.DHT11(dht_pin)
# تهيئة حساس MQ-135 لجودة الهواء (متصل بالمنفذ 26 - ADC0)
mq135_pin = ADC(Pin(26)) # تحويل المنفذ الرقمي إلى منفذ تماثلي
# في Pico، مدى ADC هو 0-3.3V ويعطي قيم من 0-65535 (16-bit)
# مؤشر LED مدمج للإشارة إلى حالة الاتصال
led = Pin("LED", Pin.OUT)
def read_mq135():
"""قراءة بيانات حساس جودة الهواء MQ-135"""
try:
# قراءة القيمة التماثلية (0-65535 في Pico)
raw_value = 65535 - mq135_pin.read_u16() # استخدام read_u16() بدلاً من read()
# تحويل القيمة إلى نسبة مئوية (0-100%)
# القيم الأعلى تعني تلوث أقل (هذا يعتمد على توصيل الحساس)
air_quality_percent = (raw_value / 65535) * 100
# تحديد مستوى جودة الهواء
if air_quality_percent > 80:
status = "ممتازة"
icon = "🌟"
color = "#00ff00"
description = "الهواء نقي جداً"
elif air_quality_percent > 60:
status = "جيدة"
icon = "👍"
color = "#90ff00"
description = "الهواء جيد ومنعش"
elif air_quality_percent > 40:
status = "متوسطة"
icon = "😐"
color = "#ffff00"
description = "بعض الملوثات موجودة"
elif air_quality_percent > 20:
status = "سيئة"
icon = "⚠️"
color = "#ff9900"
description = "ينصح بتجنب التعرض الطويل"
else:
status = "خطيرة"
icon = "🚨"
color = "#ff0000"
description = "خطر! تجنب البقاء في هذا المكان"
return {
'raw_value': raw_value,
'percentage': air_quality_percent,
'status': status,
'icon': icon,
'color': color,
'description': description
}
except Exception as e:
print(f'خطأ في قراءة MQ-135: {e}')
return None
def get_air_quality_text():
"""الحصول على نص جودة الهواء المنسق"""
mq135_data = read_mq135()
if mq135_data:
# تحويل نسبة الجودة إلى مستوى تلوث (عكسي للعرض)
pollution_level = 100 - mq135_data['percentage']
# تحديد مستوى التلوث بالكلمات
if pollution_level < 20:
pollution_desc = "منخفض جداً"
elif pollution_level < 40:
pollution_desc = "منخفض"
elif pollution_level < 60:
pollution_desc = "متوسط"
elif pollution_level < 80:
pollution_desc = "مرتفع"
else:
pollution_desc = "مرتفع جداً"
air_text = f"""
<div style="margin-top: 10px;">
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px;">
<span style="font-size: 2em;">{mq135_data['icon']}</span>
<span style="background: {mq135_data['color']}; padding: 5px 15px; border-radius: 20px; color: black; font-weight: bold;">
{mq135_data['status']}
</span>
</div>
<div style="text-align: right; margin: 15px 0;">
<div><strong>الوصف:</strong> {mq135_data['description']}</div>
<div><strong>القيمة الخام:</strong> {mq135_data['raw_value']}</div>
<div><strong>نقاء الهواء:</strong> {mq135_data['percentage']:.1f}%</div>
<div><strong>نسبة التلوث:</strong> {pollution_level:.1f}% ({pollution_desc})</div>
</div>
<div style="width:100%; height:20px; background:#ddd; border-radius:10px; margin:15px 0; overflow:hidden;">
<div style="width:{mq135_data['percentage']}%; height:100%; background:{mq135_data['color']}; border-radius:10px; transition: width 0.5s;"></div>
</div>
<div style="display: flex; justify-content: space-between; font-size: 0.8em; color: #ddd;">
<span>تلوث مرتفع</span>
<span>نقاء مرتفع</span>
</div>
</div>
"""
return air_text
else:
return """
<div style="text-align: center; color: #ff9999; padding: 20px;">
<span style="font-size: 3em;">❌</span>
<h3 style="margin: 10px 0;">خطأ في قراءة حساس جودة الهواء</h3>
<p style="font-size: 0.9em;">تأكد من توصيل MQ-135 بالمنفذ 26 بشكل صحيح</p>
<p style="font-size: 0.8em; background: rgba(255,0,0,0.2); padding: 5px; border-radius: 5px;">
⚡ يحتاج الحساس إلى جهد تغذية 5V
</p>
</div>
"""
def connect():
"""الاتصال بشبكة WiFi"""
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
# التأكد من عدم وجود اتصال سابق
if wlan.isconnected():
wlan.disconnect()
print('جاري الاتصال بالشبكة...')
wlan.connect(ssid, password)
# وميض LED أثناء محاولة الاتصال
timeout = 0
while not wlan.isconnected() and timeout < 20: # مهلة 20 ثانية
led.toggle()
print('.', end='')
sleep(0.5)
timeout += 1
if wlan.isconnected():
led.on() # تشغيل LED عند نجاح الاتصال
ip = wlan.ifconfig()[0]
print(f'\nتم الاتصال بنجاح!')
print(f'عنوان IP: {ip}')
return ip
else:
print('\nفشل الاتصال بالشبكة')
led.off()
return None
def open_socket(ip):
"""فتح socket للاتصالات"""
if ip is None:
return None
try:
address = (ip, 80)
connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connection.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
connection.bind(address)
connection.listen(1)
print('تم فتح منفذ 80 للاتصالات')
return connection
except Exception as e:
print(f'خطأ في فتح socket: {e}')
return None
def webpage(reading, air_quality_html):
"""قالب صفحة HTML مع إضافة جودة الهواء"""
html = f"""
<!DOCTYPE html>
<html dir="rtl">
<head>
<title>محطة الطقس وجودة الهواء - Raspberry Pi Pico 2W</title>
<meta charset="UTF-8">
<meta http-equiv="refresh" content="4"> <!-- تم التغيير من 10 إلى 4 -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body {{
font-family: 'Segoe UI', Arial, sans-serif;
margin: 0;
padding: 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
color: white;
}}
.container {{
max-width: 1000px;
margin: 0 auto;
}}
h1 {{
text-align: center;
margin-bottom: 30px;
font-size: 2.5em;
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
}}
.sensors-grid {{
display: grid;
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
gap: 25px;
margin: 20px 0;
}}
.sensor-card {{
background: rgba(255, 255, 255, 0.15);
backdrop-filter: blur(10px);
padding: 25px;
border-radius: 20px;
box-shadow: 0 8px 32px rgba(0,0,0,0.3);
border: 1px solid rgba(255,255,255,0.2);
transition: transform 0.3s;
}}
.sensor-card:hover {{
transform: translateY(-5px);
}}
.sensor-card h2 {{
margin-top: 0;
font-size: 1.5em;
border-bottom: 2px solid rgba(255,255,255,0.3);
padding-bottom: 15px;
text-align: center;
}}
.reading {{
font-size: 1.2em;
line-height: 2;
text-align: right;
}}
.update-badge {{
background: rgba(0,0,0,0.3);
padding: 8px 20px;
border-radius: 30px;
font-size: 0.9em;
display: inline-block;
margin: 0 auto 20px;
text-align: center;
}}
.footer {{
text-align: center;
margin-top: 30px;
padding: 20px;
background: rgba(0,0,0,0.2);
border-radius: 15px;
font-size: 0.9em;
}}
.note {{
background: rgba(255,215,0,0.2);
padding: 15px;
border-radius: 10px;
margin-top: 20px;
font-size: 0.9em;
border-right: 4px solid gold;
}}
.warning {{
color: #ffd700;
font-weight: bold;
}}
@media (max-width: 600px) {{
.sensors-grid {{
grid-template-columns: 1fr;
}}
h1 {{
font-size: 1.8em;
}}
}}
</style>
</head>
<body>
<div class="container">
<h1>🌡️ محطة الطقس وجودة الهواء</h1>
<div style="text-align: center;">
<div class="update-badge">
🔄 يتم التحديث كل 4 ثوانٍ <!-- تم التغيير من 10 إلى 4 -->
</div>
</div>
<div class="sensors-grid">
<div class="sensor-card">
<h2>🌡️ حساس DHT11</h2>
<div class="reading">
{reading}
</div>
<div style="font-size:0.8em; color:#ddd; margin-top:15px; text-align:center;">
نطاق القياس: 0-50°C, 20-90%
</div>
</div>
<div class="sensor-card">
<h2>🌬️ حساس MQ-135</h2>
<div class="reading">
{air_quality_html}
</div>
<div style="font-size:0.8em; color:#ddd; margin-top:15px; text-align:center;">
يقيس: CO₂, الأمونيا, البنزين, الدخان, أكاسيد النيتروجين
</div>
</div>
</div>
<div class="note">
<span class="warning">⚠️ ملاحظات مهمة:</span><br>
• DHT11 يحتاج إلى مقاومة سحب 10kΩ بين VCC و DATA<br>
• MQ-135 يحتاج إلى 5V للتغذية (لا يكفي 3.3V من Pico)<br>
• MQ-135 يحتاج 5-10 دقائق للتسخين بعد التشغيل للحصول على قراءات دقيقة<br>
• قراءات MQ-135 نسبية وتحتاج معايرة حسب البيئة
</div>
<div class="footer">
Raspberry Pi Pico 2W | آخر تحديث: {get_time_string()}<br>
<span style="font-size:0.8em; opacity:0.7;">عنوان IP: {ip}</span>
</div>
</div>
</body>
</html>
"""
return html
def read_dht11():
"""قراءة بيانات حساس DHT11 مع معالجة الأخطاء"""
try:
# قياس القراءات من الحساس
sensor.measure()
# الحصول على القراءات
temp = sensor.temperature()
humidity = sensor.humidity()
# التحقق من صحة القراءات
if humidity > 100 or humidity < 0:
raise ValueError("قراءة رطوبة غير صحيحة")
if temp > 60 or temp < -10:
raise ValueError("قراءة حرارة غير صحيحة")
# تنسيق القراءات مع أيقونات مناسبة
weather_icon = "☀️" if humidity < 40 else "☁️" if humidity < 70 else "🌧️"
# حساب نقطة الندى
dew_point = temp - ((100 - humidity) / 5)
reading = f"""
<div style="text-align: right;">
<div style="font-size: 3em; text-align: center; margin-bottom: 15px;">{weather_icon}</div>
<table style="width:100%; border-spacing: 5px;">
<tr>
<td>🌡️ درجة الحرارة:</td>
<td><strong>{temp}°C</strong></td>
</tr>
<tr>
<td>💧 الرطوبة:</td>
<td><strong>{humidity}%</strong></td>
</tr>
<tr>
<td>💦 نقطة الندى:</td>
<td><strong>{dew_point:.1f}°C</strong></td>
</tr>
</table>
</div>
"""
return reading
except Exception as e:
print(f'خطأ في قراءة الحساس: {e}')
return f"""
<div style="text-align: center; color: #ff9999; padding: 20px;">
<span style="font-size: 3em;">⚠️</span>
<h3>خطأ في قراءة DHT11</h3>
<p>تأكد من توصيل الحساس بشكل صحيح</p>
<p style="font-size:0.8em;">{str(e)}</p>
</div>
"""
def get_time_string():
"""الحصول على نص الوقت الحالي"""
try:
import machine
rtc = machine.RTC()
current_time = rtc.datetime()
return f"{current_time[3]}:{current_time[4]:02d}:{current_time[5]:02d}"
except:
return "غير متوفر"
def serve(connection):
"""تشغيل خادم الويب"""
if connection is None:
print('لا يمكن بدء الخادم بسبب خطأ في الاتصال')
return
print('\n' + '='*50)
print('تم بدء تشغيل خادم الويب بنجاح!')
print('='*50)
print(f'📱 افتح المتصفح على العنوان: http://{ip}')
print('='*50 + '\n')
last_reading_time = 0
reading = "بانتظار القراءة الأولى..."
air_quality_html = get_air_quality_text()
read_count = 0
# اختبار أولي للحساسات
print('🔍 فحص الحساسات:')
print(' DHT11: ', end='')
if "خطأ" in reading:
print('❌ غير متصل')
else:
print('✓ متصل')
print(' MQ-135: ', end='')
if "خطأ" in air_quality_html:
print('❌ غير متصل')
else:
print('✓ متصل')
print()
while True:
try:
# قراءة بيانات الحساسات كل دورة (كل ثانيتين)
if read_count % 1 == 0: # كل دورة = كل ثانيتين
reading = read_dht11()
air_quality_html = get_air_quality_text()
print(f'📊 [{get_time_string()}] تم تحديث القراءات (كل ثانيتين)')
# قبول اتصال جديد
client, addr = connection.accept()
print(f'👤 تم الاتصال من: {addr[0]}')
# استقبال الطلب
request = client.recv(1024)
# إرسال صفحة الويب (التي يتم تحديثها كل 10 ثواني بواسطة meta refresh)
html = webpage(reading, air_quality_html)
client.send('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n')
client.send(html)
client.close()
read_count += 1
sleep(2) # انتظار ثانيتين قبل الدورة التالية
except Exception as e:
print(f'⚠️ خطأ في معالجة الطلب: {e}')
try:
client.close()
except:
pass
sleep(1)
# البرنامج الرئيسي
print('='*50)
print('🌟 نظام محطة الطقس وجودة الهواء')
print('='*50)
print('\n⚙️ جاري بدء التشغيل...')
print('\n📌 ملاحظات هامة:')
print(' • DHT11 متصل بالمنفذ 27')
print(' • MQ-135 متصل بالمنفذ 26 (ADC)')
print(' • MQ-135 يحتاج 5V للتغذية')
print(' • انتظر 5-10 دقائق لتسخين MQ-135\n')
try:
# الاتصال بشبكة WiFi
ip = connect()
if ip:
# فتح socket
connection = open_socket(ip)
if connection:
# بدء خادم الويب
serve(connection)
else:
print('❌ فشل في فتح socket')
else:
print('❌ فشل في الاتصال بشبكة WiFi')
print(' تأكد من صحة اسم الشبكة وكلمة المرور')
except KeyboardInterrupt:
print('\n\n🛑 تم إيقاف البرنامج بواسطة المستخدم')
led.off()
try:
connection.close()
except:
pass
machine.reset()
except Exception as e:
print(f'❌ خطأ غير متوقع: {e}')
machine.reset()
بعد تشغيل النص البرمجي، انتظر في خانة Shell حتى تتصل لوحة الراسبيري باي بيكو بشبكة الواي فاي الخاصة بك بنجاح. بعد ذلك سيظهر لك رابط الموقع الذي ستُعرض عليه حالة الطقس. قم بنسخ هذا الرابط ولصقه في أي متصفح، سواء على الحاسوب أو الهاتف، وستجد أنه يتم عرض درجة الحرارة ونسبة الرطوبة وحالة جودة الهواء على صفحة الويب.





