چگونه از BLE در ESP32 استفاده کنیم؟ آموزش ESP32 BLE (بلوتوث کمانرژی)
در این آموزش، ما در مورد ویژگی BLE در ESP32 مطالبی را یاد خواهیم گرفت و همچنین راه اندازی بلوتوث کم انرژی با ماژول ESP32 را بررسی خواهیک کرد .ESP32 دارای بلوتوث کمانرژی یا BLE همراه با بلوتوث کلاسیک است.ESP32 BLE با هدف راهاندازی باتری، ارتباط بیسیم کمتوان بین دستگاههای BLE مختلف است. ما همه ملزومات را به منظور استفاده از BLE در ESP32 یاد میگیریم و آن را هم به عنوان یک سرور و هم به عنوان یک گیرنده پیکربندی میکنیم. همچنین خواهیم دید چگونه میتوانیم یک تلفن هوشمند را به سرور ESP32 BLE متصل کنیم.
یادداشت کوتاه در مورد BLE (بلوتوث کمانرژی)
ویژگی اصلی بلوتوث ۴.۰ ترکیب (بلوتوث کمانرژی) یا BLE ساده بود.BLE (که قبلا به عنوان بلوتوث اسمارت به بازار عرضه میشد) یک نسخه بسیار بهینه از بلوتوث کلاسیک است که به طور خاص برای ارتباطات بیسیم با توان پایین طراحی شدهاست.
در حالی که بلوتوث کلاسیک (که اغلب به عنوان نرخ پایه و نرخ Dara پیشرفته در اسناد فنی با اشاره به سرعت داده آن توصیف میشود) به عنوان جایگزینی برای ارتباطات سیمی با ارتباطات بیسیم کوتاهبرد طراحی شدهاست. این سیستم با سرعت داده در ذهن طراحی شدهاست و کاربردهای آن شامل انتقال فایل بزرگ، صوت بیسیم و غیره است.
از سوی دیگر، BLE برای مصرف توان پایین به جای نرخ داده بالاتر بهینه شدهاست و با در نظر گرفتن IoT و برنامههای کاربردی با باتری طراحی شدهاست. برخی از کاربردهای محبوب BLE، ساعتهای هوشمند، ردیابهای تناسب اندام، دستگاههای نظارت بر سلامت،beacon های رادیویی و غیره هستند.
بر خلاف بلوتوث کلاسیک، که همیشه متصل است،BLE معمولا در حالت آمادهبهکار (بیکار) است و در صورت نیاز شروع به کار می کند. از این رو، مصرف برق بسیار پایین است.
بلوتوث انرژی پایین نیز بر روی همان باند فرکانسی 2/4 گیگاهرتز ISM کار میکند. این بدان معنی است که یک آنتن واحد میتواند برای Wi – Fi و هر دو نسخه بلوتوث مورد استفاده قرار گیرد.
لایههای مختلف در BLE
یک دستگاه BLE از سهلایه تشکیل شدهاست:
• کنترل کننده(controller)
• میزبان (Host)
• برنامه کاربردی (Application)
controller شامل موارد زیر است:
• PHY – لایه فیزیکی میزبان
• LL – لایه لینک
• HCI – رابط کنترل کننده میزبان بخش controller
host شامل موارد زیر است:
• HCI – رابط کنترل کننده میزبان بخش Host
• L2CAP -کنترل لینک منطقی و پروتکل برنامه
• SM – مدیریت امنیت
• ATT – پروتکل attribute
• GAP – پروفایل دسترسی عمومی
• GATT – پروفایل attribute عمومی
لایه برنامه کاربردی بالاترین لایهای است که شامل رابط کاربر، جابجایی دادهها و جنبههای منطقی برنامه است.
اصطلاحات مهم در BLE
اجازه دهید به طور خلاصه برخی از اصطلاحات مهم مرتبط با BLE را مشاهده کنیم.
• :GATT برای مشخصات Attribute عمومی کوتاه است. خصوصیاتی را برای انتقال داده بین دستگاههای BLE با استفاده از خدمات و مشخصه ها تعریف میکند.
• مشخصه ها: مشخصه ها گروهی از اطلاعات به نام Attribute است و Attribute گروهی از اطلاعات منتقلشده بین دستگاهها است. یک مشخصه معمولا شامل Attribute های زیر است:
• مقدار :(Value) مقدار داده مشخصه ها
تعریف(Declaration) : ویژگیهای مشخصه (مکان، نوع مانند خواندن، نوشتن، اطلاع دادن، نشان دادن و غیره)
شرح ASCII :(Description) ، رشتهای که این ویژگی را توصیف میکند.
خدمات: مجموعهای از مشخصه های یک سرویس نامیده میشود. هر سرویس یک شناسه ۱۶ بیتی یا ۱۲۸ بیتی منحصر به فرد به نام UUID دارد.
:UUID شناسه منحصر به فرد جهانی، یک شناسه ۱۲۸ بیتی است که به هر سرویس و مشخصه در یک پروفایل داده شدهاست. از تولیدکننده UUID وب سایت برای تولید ID های منحصر به فرد استفاده کنید. هر سرویس و مشخصه دارای یک ID ۱۶ بیتی یا ۱۲۸ بیتی منحصر به فرد به نام UUID است. یک نمونه UUID چیزی شبیه به این است:
o 583f8b30-74b4-47578143-56048fd88b25
حالت های BLE
یک دستگاه BLE میتواند ۵ حالت ممکن داشته باشد.
• نیمه خاموش(standby)
• اعلان کردن(Advertising)
• اسکن کردن(Scanning)
• آغاز کردن (Initiating)
• متصل شدن (Connected)
توپولوژی شبکه BLE
ارتباط بین دو دستگاه BLE میتواند یک نوع پخش برنامه یا یک نوع اتصال باشد. دستگاه BLE در “پخش برنامه”، دادهها را به هر دستگاه BLE ناظر ارسال میکند. این یک انتقال داده یک طرفه است.
برای ارتباط دوطرفه، شما به یک “اتصال” بین دستگاههای BLE نیاز دارید. یک دستگاه BLE مرکزی (Master) به طور مکرر بستههای داده تبلیغاتی را از دستگاه BLE محیطی(Slave) اسکن میکند که بستهها را ارسال میکند.
بلوتوث کم انرژی با ماژول ESP32
میدانیم که ویژگی اصلی ESP32، Wi – Fi است. اما علاوه بر این، ESP32 SoC نیز از بلوتوث پشتیبانی میکند. بلوتوث ESP32 یک سیستم دو حالته است. این بدان معنی است که ESP32 هم از بلوتوث کلاسیک و هم (بلوتوث کمانرژی) BLEپشتیبانی میکند.
به طور خاص، سیستم بلوتوث در ESP32 با بلوتوث v4 سازگار است. در آموزش قبلی، ما چگونگی استفاده از بلوتوث کلاسیک ESP32 را با چند مثال بررسی کردیم. ما ویژگی BLE ESP32 را در این آموزش بررسی خواهیم کرد.
مدل سرویس گیرنده(Client) سرویس دهنده(Server)، BLE ESP32
هر دستگاه BLE میتواند به عنوان یک سرور یا یک Client پیکربندی شود. ESP32 نیز از این امر مستثنی نیست، یعنی ESP32 میتواند یک سرور باشد، که حضور خود را اعلان میکند به طوری که clientها بتوانند دادههای آن را بخوانند یا به عنوان یک Client، که برای سرورها اسکن میکند و یک اتصال برای دریافت دادهها از سرور ایجاد میکند.
در این پروژه، ما از دو برد ESP32 استفاده میکنیم که یکی به عنوان سرور BLE و دیگری به عنوان BLE Slave برنامهریزی شدهاند.
سرور BLE ESP32
اول، خواهیم دید که چگونه یک ESP32 را به عنوان یک سرور BLE راهاندازی کنیم. یک برد ESP32 بردارید و آن را به کامپیوتر متصل کنید. ما این برد را “ESP32- BLE_Server” مینامیم. باز کردن IDE آردوینو و اطمینان از اینکه برد توسعه ESP32 و پرت COM مناسب انتخاب شده است.
کد
این یک کد کمی اصلاحشده از مثال “BLE _ server” است.کد را برای توضیح همه چیزهای ضروری قرار دادم.
این کد را روی دستگاه Server ESP32 آپلود کنید.
#include <BLEDevice.h> #include <BLEUtils.h> #include <BLEServer.h> // See the following for generating UUIDs: // https://www.uuidgenerator.net/ #define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" #define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" /* BLEServer *pServer = BLEDevice::createServer(); BLEService *pService = pServer->createService(SERVICE_UUID); BLECharacteristic *pCharacteristic = pService->createCharacteristic( CHARACTERISTIC_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE ); */ BLEServer *pServer; BLEService *pService; BLECharacteristic *pCharacteristic; void setup() { Serial.begin(115200); Serial.println("Starting BLE Server!"); BLEDevice::init("ESP32-BLE-Server"); pServer = BLEDevice::createServer(); pService = pServer->createService(SERVICE_UUID); pCharacteristic = pService->createCharacteristic( CHARACTERISTIC_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE ); /* BLEServer *pServer = BLEDevice::createServer(); BLEService *pService = pServer->createService(SERVICE_UUID); BLECharacteristic *pCharacteristic = pService->createCharacteristic( CHARACTERISTIC_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE );*/ pCharacteristic->setValue("Hello, World!"); pService->start(); //BLEAdvertising *pAdvertising = pServer->getAdvertising(); BLEAdvertising *pAdvertising = BLEDevice::getAdvertising(); pAdvertising->addServiceUUID(SERVICE_UUID); pAdvertising->setScanResponse(true); pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue pAdvertising->setMinPreferred(0x12); BLEDevice::startAdvertising(); //pAdvertising->start(); Serial.println("Characteristic defined! Now you can read it in the Client!"); } void loop() { std::string value = pCharacteristic->getValue(); Serial.print("The new characteristic value is: "); Serial.println(value.c_str()); delay(2000); }
ESP32 BLE Client
برد ESP32 دیگری را بردارید که ما آن را (ESP32 – BLE _ Client) مینامیم و آن را به کامپیوتر متصل کنید. در IDE آردوینو، پرت COM را برای این برد انتخاب کنید.
کد
نمونه ای به نام ” BLE_ client” به عنوان بخشی از کتابخانه ESP32 BLE وجود دارد. من در اینجا از همان کد (با تغییرات کوچک) استفاده کردم. این کد را روی دستگاه Client ESP32 آپلود کنید. برای یادگیری کد نویسی میتوانید از کلاس رباتیک چالیک استفاده کنید.
#include "BLEDevice.h" /* Specify the Service UUID of Server */ static BLEUUID serviceUUID("4fafc201-1fb5-459e-8fcc-c5c9c331914b"); /* Specify the Characteristic UUID of Server */ static BLEUUID charUUID("beb5483e-36e1-4688-b7f5-ea07361b26a8"); static boolean doConnect = false; static boolean connected = false; static boolean doScan = false; static BLERemoteCharacteristic* pRemoteCharacteristic; static BLEAdvertisedDevice* myDevice; static void notifyCallback(BLERemoteCharacteristic* pBLERemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify) { Serial.print("Notify callback for characteristic "); Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str()); Serial.print(" of data length "); Serial.println(length); Serial.print("data: "); Serial.println((char*)pData); } class MyClientCallback : public BLEClientCallbacks { void onConnect(BLEClient* pclient) { } void onDisconnect(BLEClient* pclient) { connected = false; Serial.println("onDisconnect"); } }; /* Start connection to the BLE Server */ bool connectToServer() { Serial.print("Forming a connection to "); Serial.println(myDevice->getAddress().toString().c_str()); BLEClient* pClient = BLEDevice::createClient(); Serial.println(" - Created client"); pClient->setClientCallbacks(new MyClientCallback()); /* Connect to the remote BLE Server */ pClient->connect(myDevice); // if you pass BLEAdvertisedDevice instead of address, it will be recognized type of peer device address (public or private) Serial.println(" - Connected to server"); /* Obtain a reference to the service we are after in the remote BLE server */ BLERemoteService* pRemoteService = pClient->getService(serviceUUID); if (pRemoteService == nullptr) { Serial.print("Failed to find our service UUID: "); Serial.println(serviceUUID.toString().c_str()); pClient->disconnect(); return false; } Serial.println(" - Found our service"); /* Obtain a reference to the characteristic in the service of the remote BLE server */ pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID); if (pRemoteCharacteristic == nullptr) { Serial.print("Failed to find our characteristic UUID: "); Serial.println(charUUID.toString().c_str()); pClient->disconnect(); return false; } Serial.println(" - Found our characteristic"); /* Read the value of the characteristic */ /* Initial value is 'Hello, World!' */ if(pRemoteCharacteristic->canRead()) { std::string value = pRemoteCharacteristic->readValue(); Serial.print("The characteristic value was: "); Serial.println(value.c_str()); } if(pRemoteCharacteristic->canNotify()) { pRemoteCharacteristic->registerForNotify(notifyCallback); } connected = true; return true; } /* Scan for BLE servers and find the first one that advertises the service we are looking for. */ class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { /* Called for each advertising BLE server. */ void onResult(BLEAdvertisedDevice advertisedDevice) { Serial.print("BLE Advertised Device found: "); Serial.println(advertisedDevice.toString().c_str()); /* We have found a device, let us now see if it contains the service we are looking for. */ if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) { BLEDevice::getScan()->stop(); myDevice = new BLEAdvertisedDevice(advertisedDevice); doConnect = true; doScan = true; } } }; void setup() { Serial.begin(115200); Serial.println("Starting Arduino BLE Client application..."); BLEDevice::init("ESP32-BLE-Client"); /* Retrieve a Scanner and set the callback we want to use to be informed when we have detected a new device. Specify that we want active scanning and start the scan to run for 5 seconds. */ BLEScan* pBLEScan = BLEDevice::getScan(); pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); pBLEScan->setInterval(1349); pBLEScan->setWindow(449); pBLEScan->setActiveScan(true); pBLEScan->start(5, false); } void loop() { /* If the flag "doConnect" is true, then we have scanned for and found the desired BLE Server with which we wish to connect. Now we connect to it. Once we are connected we set the connected flag to be true. */ if (doConnect == true) { if (connectToServer()) { Serial.println("We are now connected to the BLE Server."); } else { Serial.println("We have failed to connect to the server; there is nothin more we will do."); } doConnect = false; } /* If we are connected to a peer BLE Server, update the characteristic each time we are reached with the current time since boot */ if (connected) { String newValue = "Time since boot: " + String(millis()/2000); Serial.println("Setting new characteristic value to \"" + newValue + "\""); /* Set the characteristic's value to be the array of bytes that is actually a string */ pRemoteCharacteristic->writeValue(newValue.c_str(), newValue.length()); /* You can see this value updated in the Server's Characteristic */ } else if(doScan) { BLEDevice::getScan()->start(0); // this is just example to start scan after disconnect, most likely there is better way to do it in arduino } delay(2000); /* Delay a second between loops */ }
کارکرد
پس از آپلود کدها به هر دو برد ESP32، شما باید پرت های سریال هر دو برد را بررسی کنید تا ارتباط را ببینید. برای این کار، من از نمایشگر سریالIDE آردوینو برای باز کردن پرت COM دستگاه سرویس گیرنده ESP32 BLE و نرمافزار دیگری مانند Putty) یا (Terminaبرای باز کردن پرت سریال دستگاه سرور ESP32 BLE استفاده میکنم.
پس از باز کردن پایانههای پرت سریال مربوطه هم برای سرور ESP32 BLE و هم برای Client، با فشار دادن دکمههای جدول (EN) مربوطه، هر دو صفحه را مجددا تنظیم کنید.
دستگاه سرور ESP32 BLE سرور BLE را راهاندازی میکند و شروع به اعلان خدمات خود میکند. اگر کد Client ESP32 را مشاهده میکنید، از UUID خدمات سرور ESP32 و UUID مشخصه استفاده میکنیم. این بدین معنی است که Client ESP32 دستگاههای BLE را اسکن میکند و اگر BLE با UUID های خاص پیدا شود، یک اتصال ایجاد میکند.
وقتی اتصال برقرار شد، مشتری ESP32 BLE ابتدا مقدار مشخصه را از سرور میخواند (ما این را با عنوان ” سلام، جهان! در کد سرور تنظیم می کنیم) و آن را بر روی پایانه چاپ می کنیم.
پس از آن، Client تلاش میکند تا مقدار مشخصه سرور را با یک مقدار جدید در هر دو ثانیه تنظیم کند. شما میتوانید پایانه client را در تصویر زیر ببینید.
به سرور بروید، زمانی که سرور BLE را شروع میکند، مقدار مشخصه خود را هر چند ثانیه یکبار میخواند. شما میتوانید تصویر زیر را ببینید که مقدار مشخصه سرور توسط client به روز رسانی شده و توسط سرور خوانده میشود.
آزمایش کردن سرور ESP32 BLE با تلفن هوشمند
حال ببینیم چگونه میتوانیم تلفنهای هوشمند خود را به یک سرور ESP32 BLE متصل کنیم. با آپلود کد زیر در برد ESP32، که ما سرور ESP32 BLE مینامیم و نمایشگر سریال را در IDE آردوینو باز میکنیم.
#include <BLEDevice.h> #include <BLEUtils.h> #include <BLEServer.h> // See the following for generating UUIDs: // https://www.uuidgenerator.net/ #define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" #define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" class MyCallbacks: public BLECharacteristicCallbacks { void onWrite(BLECharacteristic *pCharacteristic) { std::string value = pCharacteristic->getValue(); if (value.length() > 0) { Serial.println("*********"); Serial.print("New value: "); for (int i = 0; i < value.length(); i++) { Serial.print(value[i]); } Serial.println(); Serial.println("*********"); } } }; void setup() { Serial.begin(115200); Serial.println("1- Download and install an BLE scanner app in your phone"); Serial.println("2- Scan for BLE devices in the app"); Serial.println("3- Connect to ESP32-BLE_Server"); Serial.println("4- Go to CUSTOM CHARACTERISTIC in CUSTOM SERVICE and write something"); Serial.println("5- See the magic =)"); BLEDevice::init("ESP32-BLE-Server"); BLEServer *pServer = BLEDevice::createServer(); BLEService *pService = pServer->createService(SERVICE_UUID); BLECharacteristic *pCharacteristic = pService->createCharacteristic( CHARACTERISTIC_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE ); pCharacteristic->setCallbacks(new MyCallbacks()); pCharacteristic->setValue("Hello World"); pService->start(); BLEAdvertising *pAdvertising = pServer->getAdvertising(); pAdvertising->start(); } void loop() { delay(2000); }
در تلفن هوشمند، به قسمت فروشگاه بروید و برنامه “BLE Scanner” را از طریق Bluepixel Technologies نصب کنید. بعد از نصب، برنامه را باز کنید ( اگر بلوتوث ESP32 قبلا جفت شده باشد، دیگر نیازی به جفت شدن نیست). به کار انداختن بلوتوث و مکان یاب (هر دو برای اسکن دستگاههای BLE مورد نیاز هستند( در تلفن (برنامه همین کار را انجام خواهد داد). بر روی آیکون “scan” در گوشه بالا سمت راست کلیک کنید.
این برنامه شروع به اسکن کردن برای دستگاههای BLE کرده و آنها را لیست میکند. شما میتوانید “ESP32BLE – Server” را در این فهرست ببینید.
روی “CONNECT” کلیک کنید و تلفن هوشمند ارتباط با سرور ESP32 BLE را آغاز میکند. پس از اتصال، فهرست خدمات و مشخصه های آن را نشان خواهد داد. از آنجا که ما ESP32 را تنها با یک سرویس راهاندازی کردهایم، میتوانید UUID خدمات و همچنین UUID مشخصه را تحت بخش “CUSTOM SERVICE” ببینید.
برای خواندن ارزش مشخصه اولیه خدمات، بر روی آیکون “R” در بخش “CUSTOM SERVICE” کلیک کنید. این برنامه مقدار را از سرور میخواند و آن را در گزینه “Value” نمایش میدهد.
از آنجا که ما خواص مشخصه را به صورت خواندن و نوشتن تنظیم میکنیم، میتوانیم مقدار مشخصه را تنها با ضربه زدن به آیکون “W” تنظیم کنیم. یک باکس متن باز میشود تا مقدار را بنویسد. پس از ورود مقدار، روی “OK” کلیک کنید. مقدار در سرور تنظیم شده است.
اگر نمایشگر سریال سرور را باز کنید، خواهید دید که سرور مقدار تنظیمشده جدید را چاپ میکند. به جای نظارت مداوم بر فیلد “value” مانند آنچه که در مثال قبلی انجام دادیم، ما در واقع از ویژگی بازخوانی کتابخانه BLE استفاده میکنیم، که اگر و تنها اگر یک گزینه “write” آغاز شود، نامیده خواهد شد.
نتیجه گیری
یک راهنمای کامل مبتدی برای درک BLE در .ESP32 شما برخی از ویژگیهای اصلی بلوتوث کمانرژی، نحوه استفاده از ویژگی ESP32 BLE، چگونگی راهاندازی ESP32 به عنوان سرور BLE و Client و همچنین چگونگی اتصال یک تلفن هوشمند به سرور ESP32 BLE را یاد میگیرید.
این تنها یک پروژه مقدماتی با توجه به بلوتوث ESP32 کمانرژی است. در آینده، ما از BLE در ESP32 برای پیادهسازی چندین برنامه کاربردی مرتبط با IoT استفاده خواهیم کرد.