در این آموزش یاد خواهید گرفت که چگونه با استفاده از فریمورک میکروپایتون برای کنترل خروجیهای ESP32 / ESP8266، یک وب سرور بسازید. به عنوان مثال، ما برای کنترل LED روی برد ESP32 یا ESP8266، یک وب سرور را با دکمههای ON و OFF میسازیم.
ما از socketها و Python socket API استفاده میکنیم.
پیشنیازها
برای پروگرم کردن ESP32 یا ESP8266، ما از uPyCraft IDE به عنوان محیط برنامه نویسی استفاده میکنیم.
قطعات مورد نیاز
برد ESP32 DEVKIT DOIT
ESP8266-12E NodeMCU Kit
آمادهسازی فایلها
برد ESP32/ESP8266 را به کامپیوتر متصل کنید. uPyCraft IDE را باز کنید و به مسیر زیر بروید و پورت serial را انتخاب کنید.
Tools > Serial
شما باید فایل های روی برد ESP32/ESP8266 را در پوشه دستگاه ببینید. به طور پیش فرض وقتی شما فریمور میکروپایتون را استفاده میکنید، یک فایل boot.py ایجاد میشود.
برای این پروژه شما به یک فایل boot.py و main.py احتیاج دارید. فایل boot.py دارای کدی است که فقط یک بار در بوت اجرا میشود. این فایل شامل وارد کردن کتابخانهها، اعتبارنامه شبکه، اتصال به شبکه شما و پیکرهبندیهای دیگر میباشد.
فایل main.py حاوی کدی است که وب سرور را اجرا میکند تا فایلها ارائه شوند و دستورات بر اساس درخواستهای دریافت شده از کاربر، انجام شوند.
ایجاد فایل main.py در برد
برای ایجاد فایل جدید، روی New file کلیک کنید.
برای ذخیره فایل روی کامپیوترتان روی Save file کلیک کنید.
یک پنجره جدید باز میشود، فایل main.py را نامگذاری کنید و آن را در کامپیوترتان ذخیره کنید:
بعد از آن، شما باید تصویر زیر را در uPyCraft IDE ببینید: (boot.py در دستگاه شما و یک تب جدید با فایل main.py)
برای آپلود فایل در برد ESP، روی Download and run کلیک کنید:
حال دستگاه باید مستقیما فایل main.py را لود کند. ESP شما فایل main.py را ذخیره کرده است.
boot.py
کد زیر را در فایل boot.py کپی کنید.
try:
import usocket as socket
except:
import socket
from machine import Pin
import network
import esp
esp.osdebug(None)
import gc
gc.collect()
ssid = 'REPLACE_WITH_YOUR_SSID'
password = 'REPLACE_WITH_YOUR_PASSWORD'
station = network.WLAN(network.STA_IF)
station.active(True)
station.connect(ssid, password)
while station.isconnected() == False:
pass
print('Connection successful')
print(station.ifconfig())
led = Pin(2, Pin.OUT
همان طور که قبلا اشاره شد، ما وب سرور خود با استفاده از socketها و Python socket API ایجاد میکنیم.
طبق اسناد( documentations) رسمی، کتابخانه socket به شکل زیر وارد میشود:
try:
import usocket as socket
except:
import socket
برای این که بتوانید از GPIOها استفاده کنید، نیاز دارید که کلاس Pin را از ماژول machine وارد کنید:
from machine import Pin
بعد از وارد کردن ماژول socket، نیاز است که ماژول network را هم وارد کنید. ماژول network این امکان را میدهد که برد ESP32/ESP8266 را به یک شبکه Wi-Fi متصل کنید:
import network
خطوط زیر پیامهای اشکالزدایی (debugging) vendor سیستم عامل را خاموش میکنند:
import esp
esp.osdebug(None)
سپس، یک garbage collector را اجرا کنید:
import gc
gc.collect()
یک garbage collector نوعی مدیریت خودکار حافظه است. این یک راه برای بازیابی حافظهی اشغال شده توسط اشیایی است که دیگر در برنامه استفاده نمیشوند و برای ذخیرهی فضا در flash memory کاربرد دارد.
متغیرهای زیر اعتبار شبکه شما را نگه میدارند:
ssid = 'REPLACE_WITH_YOUR_SSID'
password = 'replace_with_your_password'
شما باید SSID شبکهتان و پسورد را به جای این کلمات قرار دهید؛ بنابراین ESP میتواند به روتر شما وصل شود.
سپس، ESP32 یا ESP8266 را به عنوان ایستگاه(station) Wi-Fi تنظیم کنید:
station = network.WLAN(network.STA_IF)
بعد از آن station را فعال کنید:
station.active(True)
در آخر، ESP32 یا ESP8266 را با استفاده از SSID و پسوردی که بالاتر برای آن تعریف کردید، به روتر خود متصل کنید:
station.connect(ssid, password)
عبارت زیر اطمینان میدهد تا زمانی که ESP به شبکهتان متصل نشده باشد، کد ادامه پیدا نکند:
while station.isconnected() == False:
pass
پس از اتصال موفقیتآمیز، پارامترهای رابط شبکه مانند آدرس IP برد ESP32/ESP8266 را چاپ کنید.
از متد ()ifconfig در آبجکت station استفاده کنید:
print('Connection successful')
print(station.ifconfig())
یک آبجکت Pin که خروجی است و led نام دارد و به GPIO 2 برد ESP32/ESP8266 مربوط است، را بسازید:
led = Pin(2, Pin.OUT)
main.py
کد زیر را در فایل main.py برد ESP کپی کنید:
def web_page():
if led.value() == 1:
gpio_state="ON"
else:
gpio_state="OFF"
html = """<html><head> <title>ESP Web Server</title> <meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:,"> <style>html{font-family: Helvetica; display:inline-block; margin: 0px auto; text-align: center;}
h1{color: #0F3376; padding: 2vh;}p{font-size: 1.5rem;}.button{display: inline-block; background-color: #e7bd3b; border: none;
border-radius: 4px; color: white; padding: 16px 40px; text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}
.button2{background-color: #4286f4;}</style></head><body> <h1>ESP Web Server</h1>
<p>GPIO state: <strong>""" + gpio_state + """</strong></p><p><a href="/?led=on"><button class="button">ON</button></a></p>
<p><a href="/?led=off"><button class="button button2">OFF</button></a></p></body></html>"""
return html
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('', 80))
s.listen(5)
while True:
conn, addr = s.accept()
print('Got a connection from %s' % str(addr))
request = conn.recv(1024)
request = str(request)
print('Content = %s' % request)
led_on = request.find('/?led=on')
led_off = request.find('/?led=off')
if led_on == 6:
print('LED ON')
led.value(1)
if led_off == 6:
print('LED OFF')
led.value(0)
response = web_page()
conn.send('HTTP/1.1 200 OK\n')
conn.send('Content-Type: text/html\n')
conn.send('Connection: close\n\n')
conn.sendall(response)
conn.close()
نوشتن کد با ایجاد یک تابع به نام ()web_page شروع میشود. این تابع یک متغیر به نام html را برمیگرداند که شامل متن HTML برای ساختن صفحه وب است:
def web_page():
َصفحه وب، وضعیت کنونی GPIO را نشان میدهد؛ بنابراین قبل از ایجاد متن HTML، نیاز دارید که وضعیت LED را چک کنید. وضعیت آن در متغیر gpio_state ذخیره میشود:
if led.value() == 1:
gpio_state="ON"
else:
gpio_state="OFF"
پس از آن، متغیر gpio_state با استفاده از علامت “+” برای متصل کردن رشتهها، در متن HTML گنجانده شده است:
html = """<html><head> <title>ESP Web Server</title> <meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:,"> <style>html{font-family: Helvetica; display:inline-block; margin: 0px auto; text-align: center;}
h1{color: #0F3376; padding: 2vh;}p{font-size: 1.5rem;}.button{display: inline-block; background-color: #e7bd3b; border: none;
border-radius: 4px; color: white; padding: 16px 40px; text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}
.button2{background-color: #4286f4;}</style></head><body> <h1>ESP Web Server</h1>
<p>GPIO state: <strong>""" + gpio_state + """</strong></p><p><a href="/?led=on"><button class="button">ON</button></a></p>
<p><a href="/?led=off"><button class="button button2">OFF</button></a></p></body></html>"""
ایجاد یک socket server
بعد از ایجاد HTML برای ساختن صفحه وب، برای گوش کردن به درخواستهای ورودی و فرستادن متن HTML در پاسخ، باید یک socket شنوایی ایجاد کنید.
برای این که بهتر متوجه شوید، شکل زیر دیاگرام ایجاد socketها برای تعامل سرور و مشتری را نشان میدهد:
با استفاده از ()socket.socket، یک socket ایجاد کنید و نوع آن را مشخص کنید. یک آبجکت socket جدید به نام s با خانواده آدرس داده شده و نوع socket بسازید. این یک STREAM TCP socket است:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
با استفاده از متد ()bind، سوکت را به یک آدرس(رابط شبکه و شماره پورت) متصل کنید.
متد ()bind، یک متغیر تاپل با آدرس IP و شماره پورت را به عنوان آرگومان میپذیرد:
s.bind(('', 80))
در این مثال، ما یک رشته خالی را به عنوان آدرس IP و پورت ۸۰ ارسال میکنیم و رشته خالی به آدرس IP localhost (یعنی آدرس IP برد ESP) اشاره میکند.
خط بعد، سرور را برای پذیرفتن اتصالات فعال میکند و یک سوکت شنوایی ایجاد میکند.
آرگومان listen هم حداکثر تعداد اتصالاتِ در صف را مشخص میکند و برابر ۵ است.
s.listen(5)
در حلقه while، به درخواستها گوش میدهیم و پاسخها را میفرستیم. وقتی که یک کاربر متصل میشود، سرور، متد ()accept را برای پذیرفتن اتصال صدا میزند.بهعلاوه، وقتی که یک کاربر متصل میشود، یک آبجکت جدید سوکت برای پذیرش و ارسال دادهها، در متغیر conn ذخیره میشود.
همچنین آدرس کاربر برای اتصال به سرور در متغیر addr ذخیره میشود.
conn, addr = s.accept()
سپس آدرس کاربر که در متغیر addr ذخیره شده است را چاپ کنید:
print('Got a connection from %s' % str(addr))
دادهها بین کاربر و سرور با استفاده از دو متد ()send و ()recv رد و بدل میشوند.
خط زیر درخواستهای دریافت شده در سوکت جدید ایجاد شده را میگیرد و آن را در متغیر request ذخیره میکند:
request = conn.recv(1024)
متد ()recv دادهها را از سوکت کاربر دریافت میکند(به یاد داشته باشید که ما یک آبجکت جدید سوکت را در متغیر conn ایجاد کردهایم).
آرگومان متد ()recv، حداکثر دادهای که میتواند یکباره دریافت شود را مشخص میکند.
خط بعدی به سادگی محتوای درخواست را چاپ میکند:
print('Content = %s' % str(request))
سپس یک متغیر با نام response بسازید که شامل متن HTML باشد که تابع ()web_page برگردانده است.
response = web_page()
در نهایت با استفاده از متد ()send و ()sendall، پاسخها به سوکت کاربر فرستاده میشوند:
conn.send('HTTP/1.1 200 OK\n')
conn.send('Content-Type: text/html\n')
conn.send('Connection: close\n\n')
conn.sendall(response)
در آخر سوکت ایجاد شده را ببندید:
conn.close()
تست کردن Web Server
فایلهای main.py و boot.py را در برد ESP آپلود کنید.پوشه device شما باید شامل دو فایل باشد: boot.py و main.py.
بعد از آپلود فایلها، دکمه EN/RST روی برد را فشار دهید.
بعد از چند ثانیه، یک اتصال با روتر شما برقرار میشود و آدرس IP در صفحه Shell چاپ میشود:
مرورگر خود را باز کنید، آدرس IP برد ESP که تازه به دست آوردهاید را تایپ کنید.
شما باید صفحه web server را همان طور که در شکل زیر آمده است را ببینید:
وقتی شما دکمه ON را فشار میدهید، آدرس IP ESP مبنی بر led=on?/ را درخواست میکنید. LED روی برد ESP32/ESP8266 روشن میشود و وضعیت GPIO در صفحه بهروز میشود.
توجه: گاهی اوقات LEDهای روی برد ESP8266 با فرمان OFF روشن میشوند و با فرمان ON خاموش میشوند.
وقتی که دکمه OFF را میزنید، آدرس IP ESP مبنی بر led=off?/ را درخواست میکنید. LED خاموش میشود و وضعیت GPIO بهروز میشود.
توجه: برای سادگی آموزش، ما LEDهای مربوط به GPIO 2 را کنترل کردیم. شما میتوانید با استفاده از همین روش، هر GPIO دیگری را با هر خروجی دیگری(مثلا رله) کنترل کنید.
همچنین شما میتوانید کد را برای کنترل چند GPIO بهبود دهید و یا متن HTML را برای ایجاد یک صفحه وب متفاوت، تغییر دهید.
جمعبندی
در این آموزش به شما یاد دادیم که چگونه با میکروپایتون و با استفاده از socketها و ماژول socket، برای کنترل GPIOهای ESP32/ESP8266، یک وب سرور ساده بسازید.
امیدواریم این آموزش برای شما مفید بوده باشد.