آموزش BLE، پکتها و پروتکل با ESP32
آموزش کامل BLE از advertising و GATT تا notification و طراحی پکتها با مثالهای عملی ESP32.
انتشار: 1405/04/05 - 13:52 | بازدید: 9
آموزش BLE، پکتها و پروتکل با مثال ESP32
در این مقاله BLE را از نگاه مهندسی بررسی میکنیم: نقش central و peripheral، advertising، GATT، سرویسها، characteristicها، روشهای read/write/notify و طراحی پکتهای سبک برای پروژههای واقعی ESP32.
BLE چیست و چرا برای ESP32 مهم است؟
BLE یا Bluetooth Low Energy نسخه کممصرف بلوتوث است که برای تبادل دادههای کوتاه، سنسورها، تجهیزات پوشیدنی، تنظیمات محلی دستگاه و کنترل نزدیک طراحی شده است. در ESP32، BLE معمولا زمانی انتخاب خوبی است که بخواهیم بدون Wi-Fi و بدون مصرف زیاد انرژی، موبایل را به برد متصل کنیم.
برخلاف بلوتوث کلاسیک که برای صدا و جریان داده پیوسته مناسبتر است، BLE ارتباط را به رویدادهای کوتاه و قابل کنترل تقسیم میکند. همین ویژگی باعث میشود برای ارسال وضعیت سنسور، فرمان روشن و خاموش، تغییر تنظیمات، یا خواندن مقدارهای دورهای عالی باشد.
نقشهای اصلی در BLE
- Peripheral: دستگاهی مثل ESP32 که سرویس ارائه میدهد و معمولا advertising میکند.
- Central: دستگاهی مثل موبایل که اسکن میکند، peripheral را پیدا میکند و اتصال را شروع میکند.
- GATT Server: طرفی که داده را در قالب سرویس و characteristic ارائه میدهد.
- GATT Client: طرفی که مقدار characteristicها را میخواند، مینویسد یا notification دریافت میکند.
Advertising و پکتهای تبلیغاتی
قبل از اتصال، peripheral بستههای advertising ارسال میکند تا central بتواند آن را پیدا کند. این بستهها کوچک هستند و معمولا شامل نام دستگاه، UUID سرویس، manufacturer data یا چند flag ساده میشوند.
| فیلد | کاربرد | نکته طراحی |
|---|---|---|
| Device Name | نمایش نام دستگاه در اسکن موبایل | کوتاه و قابل تشخیص باشد. |
| Service UUID | معرفی سرویس اصلی دستگاه | برای فیلتر کردن سریع در اپ موبایل مفید است. |
| Manufacturer Data | ارسال چند بایت اختصاصی | برای نسخه سختافزار یا وضعیت کوتاه مناسب است. |
GATT، سرویس و Characteristic
بعد از اتصال، بیشتر کار ما با GATT انجام میشود. یک سرویس مجموعهای از characteristicهاست. هر characteristic یک مقدار دارد و میتواند قابلیت read، write، notify یا indicate داشته باشد.
برای مثال اگر ESP32 یک سنسور دما باشد، میتوان یک سرویس اختصاصی ساخت و داخل آن characteristic مقدار دما را قرار داد. موبایل آن characteristic را میخواند یا برای notification مشترک میشود.
مثال ESP32: ساخت BLE Server ساده
نمونه زیر با Arduino Core برای ESP32 نوشته شده است. برد مثل یک peripheral دیده میشود، یک سرویس میسازد و مقدار characteristic را هر دو ثانیه با notification ارسال میکند.
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
#define SERVICE_UUID "8f4a0001-3b7f-4b9a-9a52-1c7e10100001"
#define SENSOR_UUID "8f4a0002-3b7f-4b9a-9a52-1c7e10100002"
BLECharacteristic *sensorChar;
bool deviceConnected = false;
class ServerCallbacks : public BLEServerCallbacks {
void onConnect(BLEServer *server) override {
deviceConnected = true;
}
void onDisconnect(BLEServer *server) override {
deviceConnected = false;
server->getAdvertising()->start();
}
};
void setup() {
Serial.begin(115200);
BLEDevice::init("LCENJ-ESP32-BLE");
BLEServer *server = BLEDevice::createServer();
server->setCallbacks(new ServerCallbacks());
BLEService *service = server->createService(SERVICE_UUID);
sensorChar = service->createCharacteristic(
SENSOR_UUID,
BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY
);
sensorChar->addDescriptor(new BLE2902());
sensorChar->setValue("{\"counter\":0}");
service->start();
BLEAdvertising *advertising = server->getAdvertising();
advertising->addServiceUUID(SERVICE_UUID);
advertising->setScanResponse(true);
advertising->start();
}
void loop() {
static uint32_t counter = 0;
String payload = "{\"counter\":" + String(counter++) + "}";
sensorChar->setValue(payload.c_str());
if (deviceConnected) {
sensorChar->notify();
}
delay(2000);
}
Read، Write و Notify چه تفاوتی دارند؟
- Read: موبایل مقدار فعلی characteristic را درخواست میکند.
- Write: موبایل مقداری را داخل characteristic مینویسد؛ مثلا فرمان تغییر حالت LED.
- Notify: ESP32 بدون درخواست مستقیم، مقدار جدید را برای موبایل میفرستد.
- Indicate: شبیه notify است، ولی دریافت آن تایید میشود و برای داده حساستر مناسب است.
مثال ESP32: دریافت دستور از موبایل
در این نمونه یک characteristic قابل نوشتن داریم. اگر موبایل مقدار LED:ON یا LED:OFF بفرستد، ESP32 خروجی را تغییر میدهد.
#define CONTROL_UUID "8f4a0003-3b7f-4b9a-9a52-1c7e10100003"
#define LED_PIN 2
class ControlCallbacks : public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *c) override {
String value = c->getValue().c_str();
value.trim();
if (value == "LED:ON") {
digitalWrite(LED_PIN, HIGH);
} else if (value == "LED:OFF") {
digitalWrite(LED_PIN, LOW);
}
Serial.println("BLE command: " + value);
}
};
// داخل setup بعد از ساخت service:
pinMode(LED_PIN, OUTPUT);
BLECharacteristic *controlChar = service->createCharacteristic(
CONTROL_UUID,
BLECharacteristic::PROPERTY_WRITE
);
controlChar->setCallbacks(new ControlCallbacks());
طراحی payload و پکت کاربردی
برای بیشتر پروژههای الکترونیکی، payload باید کوتاه، قابل تشخیص و قابل توسعه باشد. اگر داده ساده است، یک متن کوتاه مثل LED:ON کافی است. اگر چند فیلد دارید، JSON کوچک یا فرمت کلید-مقدار مناسبتر است.
{"type":"sensor","temp":27.4,"battery":83}
برای دادههای پرتکرار، ارسال JSON طولانی میتواند باعث کاهش پایداری شود. در این حالت میتوانید از پکت باینری کوتاه استفاده کنید؛ مثلا یک بایت نوع پیام، دو بایت مقدار سنسور و یک بایت checksum.
MTU، Connection Interval و سرعت واقعی
BLE برای payloadهای کوچک عالی است، اما برای فایلهای بزرگ یا stream سریع مناسب نیست مگر با طراحی دقیق. سه پارامتر مهم روی سرعت و پایداری اثر دارند:
- MTU: حداکثر اندازه داده در هر تبادل ATT. مقدار پیشفرض کوچک است و میتواند negotiate شود.
- Connection Interval: فاصله زمانی رویدادهای اتصال. interval کمتر یعنی پاسخ سریعتر و مصرف بیشتر.
- Notification Rate: اگر بیش از توان لینک notification بفرستید، صف پر میشود و داده از دست میرود.
برای سنسورها، ارسال هر 500 میلیثانیه تا چند ثانیه معمولا پایدارتر از ارسال پیوسته است. برای دادههای چندبایتی، payload را کوتاه و قابل parse نگه دارید.
فایل نمونه کد ESP32
نمونههای کامل Arduino برای BLE Server، دریافت دستور LED و اسکن دستگاهها داخل پکیج همین مقاله قرار دارد.
دانلود نمونه کدهای ESP32نکات دیباگ BLE در پروژههای واقعی
- UUIDها را ثابت و مستند نگه دارید؛ تغییر UUID باعث میشود اپ موبایل سرویس قبلی را پیدا نکند.
- بعد از disconnect دوباره advertising را start کنید.
- برای notification حتما descriptor نوع BLE2902 اضافه کنید.
- نام دستگاه را کوتاه بگذارید تا در advertising جا شود.
- حجم پیام را کنترل کنید؛ BLE جای payloadهای سنگین نیست.
جمعبندی
BLE در ESP32 بهترین انتخاب برای ارتباط کممصرف، تنظیمات محلی، سنسورهای نزدیک و کنترل ساده با موبایل است. اگر advertising، نقش central/peripheral، سرویسهای GATT و روش read/write/notify را درست طراحی کنید، ارتباط پایدار و قابل توسعهای خواهید داشت.