آموزش راه اندازی بلوتوث کم انرژی BLE در ESP32

Bluetooth Low Energy (BLE) این روزها بسیار همه گیر شده است. اگر اسکنری روی تلفن همراه خود اجرا کنید و در محله قدم بزنید، بدون شک ده ها و احتمالاً صدها دستگاه BLE پیدا خواهید کرد.
اگر کنجکاوید و می خواهید کار با BLE را شروع کنید، این راهنما نقطه شروع مناسبی است. یک مرور سریع از BLE ارائه می دهد (به ویژه نحوه سازماندهی داده ها در BLE و چگونگی ارتباط دو دستگاه BLE با هم) و سپس نشان می دهد چطور از BLE روی ESP32 استفاده کنید.
مبانی Bluetooth Low Energy
Bluetooth Low Energy (معمولاً با نام Bluetooth Smart نیز شناخته می شود) زیرمجموعه ای سبک از بلوتوث کلاسیک است که به عنوان بخشی از Bluetooth 4.0 معرفی شد.
BLE برای دستگاه هایی طراحی شده که مقادیر کمی داده را بندرت ارسال می کنند و با باتری های کوچک کار می کنند. این فناوری از همان پهنای باند فرکانسی بلوتوث کلاسیک یعنی باند 2.4GHz ISM استفاده می کند که نیاز به مجوز ندارد. باند ISM به 40 کانال تقسیم شده و دستگاه های BLE بین آنها پرش فرکانسی انجام می دهند تا از تداخل جلوگیری شود.
برای صرفه جویی در انرژی، توان انتقال BLE کمتر است (0.01 تا 10 mW) در مقایسه با بلوتوث کلاسیک (تا 100 mW برای class 1 و 1 mW برای class 3). روش ارسال داده مشابه است (استفاده از Gaussian Frequency Shift Keying)، اما سرعت انتقال داده کمتر است—حداکثر 1 Mb/s در مقابل حداکثر 24 Mb/s در بلوتوث کلاسیک. همچنین دستگاه های BLE می توانند سریع تر بین حالت آماده به کار و حالت فعال جابجا شوند که در مصرف انرژی باز هم صرفه جویی می کند.
خلاصه اینکه BLE با تمرکز بر مصرف پایین انرژی بسیاری از قابلیت های بلوتوث کلاسیک را ارائه می دهد و به همین دلیل فناوری استانداردی برای کاربردهای مختلف مثل روشنایی هوشمند، خانه هوشمند، beacon ها، ردیاب های ورزشی، پمپ های انسولین، سمعک و سایر کاربردهای حساس به انرژی شده است.

Bluetooth Profiles
پروفایل های بلوتوث پروتکل های اضافی هستند که روی استاندارد پایه بلوتوث ساخته می شوند و نوع داده ای که یک ماژول بلوتوث منتقل می کند را مشخص می کنند. در حالی که مشخصات بلوتوث نحوه عملکرد فناوری را تعریف می کند، پروفایل ها تعیین می کنند چه طور از آن استفاده شود.
پروفایل هایی که یک دستگاه بلوتوث از آنها پشتیبانی می کند مشخص می کند آن دستگاه برای چه کاربردهایی طراحی شده است. برای مثال، یک هدست hands free از headset profile (HSP) استفاده می کند و یک صفحه کلید بی سیم از human interface device (HID) profile. پروفایل ها توسط Bluetooth SIG یا طراحان پیرامون دستگاه ها توسعه می یابند.
پروفایل هایی که باید با آنها آشنا باشید GAP و GATT هستند، زیرا تمام پروفایل های استاندارد BLE مبتنی بر آنها ساخته می شوند. در ادامه این دو را دقیق تر بررسی می کنیم.
GAP (Generic Access Profile)
GAP کنترل کننده اتصال و تبلیغات (advertising) در بلوتوث است. GAP همان چیزی است که دستگاه شما را برای دنیای بیرون قابل دیده شدن می کند و مشخص می کند دو دستگاه چگونه یکدیگر را کشف و اتصال برقرار کنند.
GAP نقش های مختلفی برای دستگاه ها تعریف می کند، اما دو مفهوم کلیدی که باید در نظر داشته باشید عبارتند از Central و Peripheral.
Peripheral معمولاً دستگاه های کوچک، کم مصرف و با منابع محدود هستند، مانند مانیتور ضربان قلب یا تگ های مجاورت، که به یک دستگاه مرکزی قوی تر متصل می شوند.
Central معمولاً دستگاه هایی با قدرت پردازش و حافظه بیشتر مانند تلفن های همراه یا تبلت ها هستند که به آنها متصل می شوید.

یک دستگاه peripheral با فرستادن بسته های تبلیغاتی (advertising packets) در فواصل زمانی معین اعلان حضور خود را منتشر می کند تا دستگاه های مرکزی نزدیک از وجود آن مطلع شوند. پس از برقراری اتصال بین peripheral و central، فرآیند تبلیغات متوقف می شود و GATT وارد عمل می شود تا ارتباط دو طرفه داده را امکان پذیر سازد.
GATT (Generic ATTribute Profile)
GATT مشخص می کند داده ها چگونه سازماندهی شوند و دو دستگاه BLE چگونه باید داده ها را با هم به اشتراک بگذارند. برخلاف GAP که تعاملات سطح پایین را تعریف می کند، GATT فقط با رویه ها و فرمت های انتقال داده سروکار دارد.
داده ها به صورت سلسله مراتبی در بخش هایی به نام سرویس ها (services) سازماندهی می شوند که هر سرویس مجموعه ای از اطلاعات مرتبط به نام characteristic را در خود جای می دهد، همانطور که در تصویر زیر نشان داده شده است.

Services
یک سرویس در GATT مجموعه ای از داده های مفهوماً مرتبط به نام characteristic ها است. هر سرویس می تواند یک یا چند characteristic داشته باشد و برای هر سرویس یک شناسه عددی یکتا یا UUID وجود دارد که یا 16 بیت (برای سرویس های رسمی BLE) یا 128 بیت (برای سرویس های سفارشی) است.
برای مثال، Heart Rate Service یک سرویس رسمی است که UUID 16 بیتی 0x180D دارد و می تواند تا 3 characteristic شامل Heart Rate Measurement، Body Sensor Location و Heart Rate Control Point را در بر بگیرد.
Characteristics
یک characteristic در GATT گروهی از اطلاعات به نام Attributes را تشکیل می دهد. Attributes همان اطلاعاتی هستند که بین دستگاه های BLE منتقل می شوند. یک characteristic معمولاً شامل موارد زیر است:

- Value: داده واقعی ذخیره شده در characteristic است. مقدار می تواند هر نوع داده ای باشد، مانند عدد، رشته یا آرایه ای از بایت ها.
- Descriptor: اطلاعات اضافی یا گزینه های پیکربندی برای آن characteristic را فراهم می کند.
علاوه بر مقدار، هر characteristic ویژگی های زیر را نیز دارد:
- Handle: یک عدد 16 بیتی که برای دسترسی به characteristic در دستگاه سرور استفاده می شود.
- UUID: یک شناسه یکتا که نشان می دهد characteristic نمایان گر چه چیزی است. UUID ها می توانند 16 بیت یا 128 بیت باشند.
- Permissions: مشخص می کند چه عملیاتی روی characteristic مجاز است، مانند read، write یا notify.
UUID (Universally Unique Identifier)
UUID ها تضمین می کنند که سرویس ها و characteristic ها به طور یکتا شناسایی شوند. دو نوع UUID در BLE وجود دارد:
- 16-bit UUID: برای پروفایل ها، سرویس ها و characteristic های رسمی BLE استفاده می شود و توسط Bluetooth SIG استاندارد شده است. برای مثال، Heart Rate Service شناسه 16 بیتی 0x180D را دارد و characteristic مربوط به Heart Rate Measurement شناسه 0x2A37 را دارد.
- 128-bit UUID: برای سرویس ها و characteristic های سفارشی یا vendor-specific استفاده می شود. اگر شرکتی سرویس خاص خود را تعریف کند که تحت پوشش سرویس های رسمی نیست، از یک UUID 128 بیتی یکتا استفاده می کند. نمونه ای از UUID 128 بیتی به شکل 4fafc201-1fb5-459e-8fcc-c5c9c331914b است.
GATT Server و GATT Client
از دید GATT، وقتی دو دستگاه به هم متصل می شوند، هرکدام در یکی از دو نقش زیر قرار می گیرند:
- GATT server: دستگاهی است که دیتابیس characteristic را در خود نگه می دارد.
- GATT client: دستگاهی است که از روی سرور می خواند یا روی سرور می نویسد.
در شکل نمونه اتصال BLE که در ادامه می بینید، دستگاه peripheral (در این مثال ESP32) نقش GATT server را دارد و دستگاه مرکزی (یک گوشی هوشمند) نقش GATT client را ایفا می کند.

نکته مهم این است که نقش های GATT (client/server) مستقل از نقش های GAP (peripheral/central) هستند. یک peripheral می تواند GATT client یا GATT server یا هر دو باشد و به طور مشابه یک central هم می تواند نقش client یا server یا هر دو را داشته باشد.
استفاده از BLE روی ESP32
حالا که با Bluetooth Low Energy، قابلیت ها و پروفایل های آن و نحوه ارتباط دستگاه ها آشنا شده اید، زمان آن رسیده که آن را عملی امتحان کنید.
برای ESP32 چند نمونه اسکچ در کتابخانه ESP32 BLE موجود است. این کتابخانه هنگام نصب ESP32 core در Arduino IDE اضافه می شود.
برای دسترسی به مثال ها به مسیر File > Examples > ESP32 BLE Arduino بروید.
لیستی از مثال ها را مشاهده خواهید کرد. می توانید هر کدام را انتخاب کرده و در IDE بارگذاری کنید. ما با مثال BLE_server شروع می کنیم.
این مثال ESP32 را به عنوان یک BLE server با یک سرویس و یک characteristic مشخص پیکربندی می کند و سپس آن سرویس را تبلیغ می کند تا توسط BLE client هایی مثل گوشی شما قابل کشف و دسترسی باشد.
#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"
void setup() {
Serial.begin(115200);
Serial.println("Starting BLE work!");
BLEDevice::init("MyESP32"); // set the device name
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(); // this still is working for backward compatibility
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();
Serial.println("Characteristic defined! Now you can read it in your phone!");
}
void loop() {
// put your main code here, to run repeatedly:
delay(2000);
} توضیح کد
ابتدا کتابخانه های لازم برای عملیات BLE روی ESP32 شامل <BLEDevice.h>، <BLEUtils.h> و <BLEServer.h> وارد می شوند.
#include <BLEDevice.h> #include <BLEUtils.h> #include <BLEServer.h>
سپس UUID های سرویس و characteristic تعریف می شوند. UUID ها شناسه های یکتایی هستند که سرویس ها و characteristic ها در BLE را مشخص می کنند.
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" #define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
در بخش setup ارتباط سریال با نرخ 115200 برقرار می شود.
Serial.begin(115200);
سپس یک دستگاه BLE با نام MyESP32 ایجاد می شود. شما می توانید نام را به هر چیزی که می خواهید تغییر دهید.
// Create the BLE Device
BLEDevice::init("MyESP32"); بعد از آن، دستگاه BLE به عنوان یک سرور تنظیم می شود.
BLEServer *pServer = BLEDevice::createServer();
سپس سرویس جدیدی برای سرور با UUID تعریف شده ایجاد می شود.
BLEService *pService = pServer->createService(SERVICE_UUID);
بعد از آن یک characteristic جدید برای سرویس ایجاد می شود. خصوصیات characteristic در این مثال READ و WRITE هستند که همراه با CHARACTERISTIC_UUID به تابع createCharacteristic ارسال می شوند.
BLECharacteristic *pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE
); پس از ایجاد characteristic، با متد setValue() مقدار آن تنظیم می شود. در این مثال مقدار متن “Hello World!” تعیین شده است. شما می توانید این مقدار را به هر چیزی تغییر دهید؛ مثلاً مقدار یک سنسور یا وضعیت یک دکمه.
pCharacteristic->setValue("Hello World!"); در ادامه سرویس با فراخوانی start() راه اندازی می شود تا به درخواست های ورودی پاسخ دهد.
pService->start();
در نهایت پارامترهای تبلیغات پیکربندی شده و سرویس تبلیغ می شود تا ESP32 توسط دستگاه های BLE دیگر قابل کشف باشد. در این مثال از addServiceUUID، setScanResponse(true) و setMinPreferred استفاده شده و سپس startAdvertising فراخوانی می شود.
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising(); pAdvertising->addServiceUUID(SERVICE_UUID); pAdvertising->setScanResponse(true); pAdvertising->setMinPreferred(0x06); pAdvertising->setMinPreferred(0x12); BLEDevice::startAdvertising();
این مثال یک نمونه ساده برای راه اندازی BLE server است، بنابراین در loop() کاری اجرا نمی شود. بسته به کاربرد می توانید اقداماتی را هنگام اتصال یک کلاینت جدید انجام دهید (برای نمونه های مربوط به notify به مثال BLE_notify مراجعه کنید).
void loop() {
delay(2000);
} آزمایش با nRF Connect
برای آزمایش اتصال BLE باید ESP32 را با گوشی خود جفت کنید و یک اپلیکیشن دیباگ بلوتوث روی گوشی نصب داشته باشید. یکی از برنامه های محبوب برای این کار nRF Connect است که برای iOS و Android موجود است و امکان اسکن، بررسی و برقراری ارتباط با دستگاه های BLE را فراهم می کند.
- به Google Play یا App Store بروید و “nRF Connect for Mobile” را نصب کنید.

- مطمئن شوید بلوتوث گوشی روشن است.

- در اپ روی دکمه “SCAN” ضربه بزنید. اپلیکیشن شروع به اسکن دستگاه های BLE نزدیک می کند.

- لیستی از دستگاه های در دسترس همراه با قدرت سیگنال نمایش داده می شود. به دنبال “MyESP32” بگردید و دکمه “Connect” کنار آن را انتخاب کنید.

- به نمای “Services” منتقل می شوید. در آنجا فهرستی از سرویس های در دسترس را می بینید. روی “Unknown Service” کلیک کنید—رشته UUID باید با SERVICE_UUID در کد شما مطابقت داشته باشد.

- برای دیدن characteristic های مربوط به یک سرویس روی آن ضربه بزنید. کنار هر characteristic دو آیکون وجود دارد؛ فلش رو به پایین برای خواندن و فلش رو به بالا برای نوشتن.

- برای خواندن characteristic روی فلش رو به پایین ضربه بزنید. UUID مربوطه، خواص READ و WRITE و مقدار “Hello World!” را دقیقاً همانطور که در کد تعیین کرده ایم خواهید دید.

سوالات متداول
تفاوت GAP و GATT چیست؟
GAP وظیفه کنترل اتصال و تبلیغات را دارد و مشخص می کند دستگاه ها چگونه یکدیگر را کشف و اتصال برقرار کنند؛ همچنین نقش هایی مانند Central و Peripheral را تعریف می کند. GATT مسئول سازماندهی داده ها و رویه های تبادل داده است و داده ها را در قالب سرویس ها و characteristic ها مدیریت می کند.
چرا از UUID های 16 بیت و 128 بیت استفاده می شود؟
UUID های 16 بیت برای سرویس ها و characteristic های رسمی و استاندارد شده توسط Bluetooth SIG به کار می روند (مثلاً Heart Rate Service با UUID 0x180D). UUID های 128 بیت برای سرویس ها و characteristic های سفارشی یا vendor-specific استفاده می شوند؛ مانند نمونه 4fafc201-1fb5-459e-8fcc-c5c9c331914b.
مراحل اصلی برای راه اندازی ESP32 به عنوان یک BLE server چیست؟
مراحل شامل نصب ESP32 core در Arduino IDE، باز کردن File > Examples > ESP32 BLE Arduino و انتخاب مثال BLE_server، وارد کردن کتابخانه های مورد نیاز، تعریف SERVICE_UUID و CHARACTERISTIC_UUID، فراخوانی Serial.begin(115200)، مقدار دهی BLEDevice::init(“MyESP32”)، ایجاد سرور با createServer، ایجاد سرویس با createService، ایجاد characteristic با خصوصیات مناسب (مثلاً PROPERTY_READ | PROPERTY_WRITE)، تنظیم مقدار با setValue، فراخوانی pService->start() و در نهایت پیکربندی و شروع تبلیغات با startAdvertising است.











