یک چراغ DMX را با استفاده از ESP32 از طریق بلوتوث کنترل کنید

این پروژه یک مثال ساده از نحوه کنترل یک چراغ DMX با استفاده از ESP32 از طریق بلوتوث است. ESP32 داده های DMX را از طریق بلوتوث دریافت می کند و با استفاده از ماژول RS485 به دستگاه نور DMX ارسال می کند.
مقدمه
هدف من کنترل یک چراغ DMX با استفاده از ارتباط سریال و React Native برای یک پروژه بود. پس از چند روز تحقیق متوجه شدم که ارتباط سریال برای React Native ایدهآل نیست، بنابراین تصمیم گرفتم از ESP32 برای اتصال از طریق بلوتوث استفاده کنم و اجازه دهم ESP32 ارتباط سریال را با چراغ DMX انجام دهد.
ماژول RS485 مورد نیاز است زیرا داده های DMX با استفاده از پروتکل EIA-485 که به نام RS-485 نیز شناخته می شود، منتقل می شود و ESP32 رابط RS-485 ندارد.
چراغ های مورد استفاده در این مثال چهار نقطه LED PARty TCL از Eurolite هستند. در زیر نمودار DMX وجود دارد که کانال های DMX اختصاص داده شده به هر چراغ و عملکردهای مربوط به آنها را نشان می دهد.
هر کانال یک ویژگی خاص از لامپ را کنترل می کند، مانند رنگ، شدت یا حرکت، که امکان تنظیمات دقیق برای جلوه های نور پویا را فراهم می کند.
ESP32
ESP32 یک میکروکنترلر کم هزینه و کم مصرف با Wi-Fi یکپارچه و بلوتوث دو حالته است. این یک انتخاب عالی برای پروژه های اینترنت اشیا است زیرا دارای ویژگی های زیادی است و به راحتی برنامه ریزی می شود. ESP32 با استفاده از Arduino IDE برنامه ریزی شده است، یک پلت فرم کاربر پسند که به طور خاص برای پروژه های میکروکنترلر طراحی شده است. پشتیبانی گسترده از کتابخانه و رابط ساده آن، آن را برای برنامههای IoT ایدهآل میکند و توسعهدهندگان را قادر میسازد تا پروژهها را بهطور کارآمد نمونهسازی و اجرا کنند.
esp_dmx
در این پروژه، من از کتابخانه esp_dmx برای ارسال داده های DMX به دستگاه نور استفاده می کنم. کتابخانه esp_dmx یک کتابخانه ساده است که به شما امکان می دهد داده های DMX را با استفاده از پروتکل RS-485 ارسال کنید. استفاده از کتابخانه آسان است و نمونه های زیادی دارد که نحوه استفاده از آن را به شما نشان می دهد.
سخت افزار
برای ساخت این پروژه به سخت افزار زیر نیاز دارید:
-
1x یک برد توسعه ESP32 (من از ESP32 WROOM-32D استفاده کردم)
این میکروکنترلر به عنوان مغز پروژه عمل می کند و ارتباط بلوتوث و تولید سیگنال DMX را امکان پذیر می کند. -
1x LED آبی
به عنوان نشانگر وضعیت برای نشان دادن وضعیت اتصال بلوتوث یا فعالیت داده استفاده می شود. -
مقاومت 1×220 اهم
جریان را به LED آبی محدود می کند و از آسیب جلوگیری می کند. -
1x ماژول RS485
سیگنال های UART ESP32 را به پروتکل DMX512 تبدیل می کند که برای کنترل وسایل DMX ضروری است. -
1 کانکتور XLR/DMX زن
از طریق کابل استاندارد DMX با چراغ های سازگار با DMX ارتباط برقرار می کند.
-** 1x کابل USB-C**
برق ESP32 را تامین می کند و امکان برنامه نویسی را فراهم می کند. - چند سیم جامپر
-
لامپ سازگار با DMX
دستگاه خروجی که به سیگنال های DMX ارسال شده توسط ESP32 پاسخ می دهد. -
کابل XLR/DMX
برای اتصال ESP32 به چراغ برق
نرم افزار
برای ساخت این پروژه به آردوینو IDE نیاز دارید. هنگامی که Arduino IDE را نصب کردید، ابتدا باید بسته هسته ESP32 را نصب کنید. BOARDS MANAGER را از منوی سمت چپ باز کنید و ESP32 را جستجو کنید. بسته ESP32 by Espressif Systems را نصب کنید.
من برخی مشکلات سازگاری با کتابخانه esp_dmx و ESP32 داشتم، بنابراین مجبور شدم بسته هسته ESP32 را به نسخه 2.0.17 تنزل دهم.
اگر با خطای زیر مواجه شدید همین کار را انجام دهید:
esp-dmx_ble.ino:92:52: error: conversion from 'String' to non-scalar type 'std::string' {aka 'std::__cxx11::basic_string' } requested
92 | std::string rxValue = pCharacteristic->getValue();
| ~~~~~~~~~~~~~~~~~~~~~~~~~^~
exit status 1
Compilation error: conversion from 'String' to non-scalar type 'std::string' {aka 'std::__cxx11::basic_string' } requested
پس از نصب برد، ESP خود را وصل کنید و برد و پورت را در Arduino IDE انتخاب کنید.
برای نصب کتابخانه esp_dmx به LIBRARY MANAGER رفته و esp_dmx را جستجو کنید. کتابخانه esp_dmx توسط Mitch Weisbrod را نصب کنید.
کد
#include
#include
#include
#include "esp_dmx.h"
ابتدا کتابخانه های لازم را گنجانده ایم. برای بخش بلوتوث، ما از کتابخانههای BLEDevice، BLEUtils و BLEServer استفاده میکنیم. برای قسمت DMX از کتابخانه esp_dmx استفاده می کنیم.
// BLE Configuration
#define LED_PIN 2
#define SERVICE_UUID "afe16d0c-ce27-4ffb-8943-5c3228cffabb"
#define CHARACTERISTIC_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
// DMX Configuration
const dmx_port_t dmx_num = DMX_NUM_1;
const int TX_PIN = 16; // DI pin
const int RX_PIN = -1; // Not used since we're only transmitting
const int EN_PIN = 21; // DE & RE pins
سپس پیکربندی قطعات بلوتوث و DMX را تنظیم می کنیم. LED_PIN پینی است که برای کنترل LED آبی استفاده می کنیم، این پین برای نشان دادن وضعیت اتصال بلوتوث استفاده می شود. SERVICE_UUID و CHARACTERISTIC_UUID UUID هایی هستند که برای شناسایی سرویس و ویژگی بلوتوث استفاده می کنیم.
dmx_num پورت DMX است که ما از آن استفاده می کنیم، در این مورد از DMX_NUM_1 استفاده می کنیم.
TX_PIN: پینی که برای انتقال داده های DMX استفاده می کنیم.
RX_PIN: پینی که برای دریافت دادههای DMX استفاده میکنیم، در این مورد، ما از آن استفاده نمیکنیم زیرا فقط در حال ارسال هستیم.
EN_PIN: – پینی که برای فعال کردن پینهای Receiver (RE) و Driver (DE) ماژول RS485 استفاده میکنیم.
// BLE variables
BLEServer *pServer = NULL;
BLEService *pService = NULL;
BLECharacteristic *pCharacteristic = NULL;
bool deviceConnected = false;
bool oldDeviceConnected = false;
ما متغیرهای BLE را که برای ایجاد سرویس و ویژگی بلوتوث استفاده می کنیم، اعلام می کنیم. همچنین متغیرهای deviceConnected و oldDeviceConnected را که برای پیگیری وضعیت اتصال بلوتوث استفاده می کنیم، اعلام می کنیم.
// DMX variables
uint8_t dmxData[DMX_PACKET_SIZE] = {0};
const unsigned long DMX_TRANSMIT_INTERVAL = 25; // 40Hz refresh rate
unsigned long lastDmxTransmit = 0;
dmxData: آرایه ای که برای ذخیره داده های DMX که از طریق بلوتوث دریافت می کنیم استفاده می کنیم.
DMX_TRANSMIT_INTERVAL: بازهای که در آن دادههای DMX را به دستگاه نور میفرستیم، در این حالت، دادهها را هر ۲۵ میلیثانیه ارسال میکنیم (نرخ تازهسازی ۴۰ هرتز).
lastDmxTransmit: زمانی که آخرین بار داده های DMX را ارسال کردیم.
class MyServerCallbacks : public BLEServerCallbacks
{
void onConnect(BLEServer *pServer)
{
deviceConnected = true;
}
void onDisconnect(BLEServer *pServer)
{
deviceConnected = false;
}
};
برای نظارت بر وضعیت اتصال بلوتوث، کلاسی به نام MyServerCallbacks ایجاد می کنیم که کلاس BLEServerCallbacks را گسترش می دهد. روشهای onConnect و onDisconnect را نادیده میگیریم تا متغیر deviceConnected را هنگام اتصال یا قطع شدن دستگاه روی true یا false تنظیم کنیم.
class MyCallbacks : public BLECharacteristicCallbacks
{
void onWrite(BLECharacteristic *pCharacteristic)
{
std::string rxValue = pCharacteristic->getValue();
if (rxValue.length() > 0)
{
for (size_t i = 0; i < rxValue.length() - 1; i += 2)
{
uint8_t channel = rxValue[i];
uint8_t value = rxValue[i + 1];
if (channel < DMX_PACKET_SIZE)
{
dmxData[channel] = value;
}
}
// Blink LED to indicate received data
digitalWrite(LED_PIN, HIGH);
delay(10);
digitalWrite(LED_PIN, LOW);
}
}
};
برای دریافت داده های DMX از طریق بلوتوث، کلاسی به نام MyCallbacks ایجاد می کنیم که کلاس BLECharacteristicCallbacks را گسترش می دهد. به این ترتیب میتوانیم متد onWrite را لغو کنیم تا دادههایی را که از طریق بلوتوث دریافت میکنیم بخوانیم و در آرایه dmxData ذخیره کنیم. همچنین LED را چشمک می زنیم تا نشان دهد که داده دریافت کرده ایم.
void setup()
{
// Setup BLE
BLEDevice::init("DMX Controller");
pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
pService = pServer->createService(SERVICE_UUID);
pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE);
pCharacteristic->setCallbacks(new MyCallbacks());
pService->start();
pServer->getAdvertising()->start();
// Setup DMX
dmx_config_t config = DMX_CONFIG_DEFAULT;
dmx_driver_install(dmx_num, &config, NULL, 0);
dmx_set_pin(dmx_num, TX_PIN, RX_PIN, EN_PIN);
pinMode(LED_PIN, OUTPUT);
}
در تابع setup، دستگاه BLE را مقداردهی اولیه می کنیم و سرویس و مشخصه بلوتوث را ایجاد می کنیم. ما همچنین سرویس DMX را ایجاد می کنیم و پین هایی را که برای انتقال داده های DMX استفاده می کنیم تنظیم می کنیم. همچنین LED_PIN را به عنوان پین خروجی تنظیم کردیم.
void loop()
{
// Handle BLE connection status
if (!deviceConnected && oldDeviceConnected)
{
delay(500);
pServer->startAdvertising();
oldDeviceConnected = deviceConnected;
}
if (deviceConnected && !oldDeviceConnected)
{
oldDeviceConnected = deviceConnected;
}
// Continuously send DMX data at regular intervals
unsigned long currentMillis = millis();
if (currentMillis - lastDmxTransmit >= DMX_TRANSMIT_INTERVAL)
{
lastDmxTransmit = currentMillis;
dmx_write(dmx_num, dmxData, DMX_PACKET_SIZE);
dmx_send(dmx_num);
dmx_wait_sent(dmx_num, DMX_TIMEOUT_TICK);
}
// Blink LED when not connected
if (!deviceConnected)
{
digitalWrite(LED_PIN, HIGH);
delay(200);
digitalWrite(LED_PIN, LOW);
delay(200);
}
}
در تابع حلقه، وضعیت اتصال BLE را کنترل می کنیم و داده های DMX را در فواصل زمانی معین به فیکسچر نور ارسال می کنیم. همچنین زمانی که دستگاه متصل نیست، LED را چشمک می زنیم.
برای اشکال زدایی اختیاری، می توانید کد زیر را به پروژه اضافه کنید:
#define DEBUG true // Set to false to disable debug printing
void printDmxDebug(uint8_t channel, uint8_t value)
{
if (!DEBUG)
return;
Serial.print("DMX Channel ");
if (channel < 10)
Serial.print("00");
else if (channel < 100)
Serial.print("0");
Serial.print(channel);
Serial.print(" set to ");
if (value < 100)
Serial.print(" ");
if (value < 10)
Serial.print(" ");
Serial.print(value);
Serial.print(" (");
Serial.print(float(value) / 255.0 * 100.0, 1);
Serial.println("%)");
}
/* Change the MyCallbacks class to this: */
class MyCallbacks : public BLECharacteristicCallbacks
{
void onWrite(BLECharacteristic *pCharacteristic)
{
std::string rxValue = pCharacteristic->getValue();
if (rxValue.length() > 0)
{
if (DEBUG)
{
Serial.println("\n--- New DMX Values Received ---");
}
for (size_t i = 0; i < rxValue.length() - 1; i += 2)
{
uint8_t channel = rxValue[i];
uint8_t value = rxValue[i + 1];
if (channel < DMX_PACKET_SIZE)
{
dmxData[channel] = value;
printDmxDebug(channel, value);
}
}
if (DEBUG)
{
Serial.println("---------------------------\n");
}
// Blink LED to indicate received data
digitalWrite(LED_PIN, HIGH);
delay(10);
digitalWrite(LED_PIN, LOW);
}
}
};
/* Add this to the setup function: */
Serial.begin(115200);
به این ترتیب می توانید داده های DMX را که از طریق بلوتوث دریافت می کنید در مانیتور سریال مشاهده کنید که می تواند برای اشکال زدایی مفید باشد.
کد کامل
#include
#include
#include
#include "esp_dmx.h"
// BLE Configuration
#define LED_PIN 2
#define SERVICE_UUID "afe16d0c-ce27-4ffb-8943-5c3228cffabb"
#define CHARACTERISTIC_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
// DMX Configuration
const dmx_port_t dmx_num = DMX_NUM_1;
const int TX_PIN = 16; // DI pinz
const int RX_PIN = -1; // Not used since we're only transmitting
const int EN_PIN = 21; // DE & RE pins
// BLE variables
BLEServer *pServer = NULL;
BLEService *pService = NULL;
BLECharacteristic *pCharacteristic = NULL;
bool deviceConnected = false;
bool oldDeviceConnected = false;
// DMX variables
uint8_t dmxData[DMX_PACKET_SIZE] = {0};
const unsigned long DMX_TRANSMIT_INTERVAL = 25; // 40Hz refresh rate
unsigned long lastDmxTransmit = 0;
#define DEBUG true // Set to false to disable debug printing
void printDmxDebug(uint8_t channel, uint8_t value)
{
if (!DEBUG)
return;
Serial.print("DMX Channel ");
if (channel < 10)
Serial.print("00");
else if (channel < 100)
Serial.print("0");
Serial.print(channel);
Serial.print(" set to ");
if (value < 100)
Serial.print(" ");
if (value < 10)
Serial.print(" ");
Serial.print(value);
Serial.print(" (");
Serial.print(float(value) / 255.0 * 100.0, 1);
Serial.println("%)");
}
class MyServerCallbacks : public BLEServerCallbacks
{
void onConnect(BLEServer *pServer)
{
deviceConnected = true;
}
void onDisconnect(BLEServer *pServer)
{
deviceConnected = false;
}
};
/*
Example byte array format for sending DMX data over BLE:
For a simple RGB light starting at DMX address 1:
[1, 255, // Channel 1 (Red) = 255
2, 128, // Channel 2 (Green) = 128
3, 64] // Channel 3 (Blue) = 64
For multiple lights:
[1, 255, // Light 1: Red = 255
2, 128, // Light 1: Green = 128
3, 64, // Light 1: Blue = 64
4, 255, // Light 1: Dimmer = 255
5, 0, // Light 1: Strobe = 0
6, 255, // Light 2: Red = 255
7, 0, // Light 2: Green = 0
8, 0] // Light 2: Blue = 0
Each pair represents: [DMX_Channel, Value]
Channel: 1-512
Value: 0-255
*/
class MyCallbacks : public BLECharacteristicCallbacks
{
void onWrite(BLECharacteristic *pCharacteristic)
{
std::string rxValue = pCharacteristic->getValue();
if (rxValue.length() > 0)
{
if (DEBUG)
{
Serial.println("\n--- New DMX Values Received ---");
}
for (size_t i = 0; i < rxValue.length() - 1; i += 2)
{
uint8_t channel = rxValue[i];
uint8_t value = rxValue[i + 1];
if (channel < DMX_PACKET_SIZE)
{
dmxData[channel] = value;
printDmxDebug(channel, value);
}
}
if (DEBUG)
{
Serial.println("---------------------------\n");
}
// Blink LED to indicate received data
digitalWrite(LED_PIN, HIGH);
delay(10);
digitalWrite(LED_PIN, LOW);
}
}
};
void setup()
{
Serial.begin(115200);
// Setup BLE
BLEDevice::init("DMX Controller");
pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
pService = pServer->createService(SERVICE_UUID);
pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE);
pCharacteristic->setCallbacks(new MyCallbacks());
pService->start();
pServer->getAdvertising()->start();
// Setup DMX
dmx_config_t config = DMX_CONFIG_DEFAULT;
dmx_driver_install(dmx_num, &config, NULL, 0);
dmx_set_pin(dmx_num, TX_PIN, RX_PIN, EN_PIN);
pinMode(LED_PIN, OUTPUT);
}
void loop()
{
// Handle BLE connection status
if (!deviceConnected && oldDeviceConnected)
{
delay(500);
pServer->startAdvertising();
oldDeviceConnected = deviceConnected;
}
if (deviceConnected && !oldDeviceConnected)
{
oldDeviceConnected = deviceConnected;
}
// Continuously send DMX data at regular intervals
unsigned long currentMillis = millis();
if (currentMillis - lastDmxTransmit >= DMX_TRANSMIT_INTERVAL)
{
lastDmxTransmit = currentMillis;
dmx_write(dmx_num, dmxData, DMX_PACKET_SIZE);
dmx_send(dmx_num);
dmx_wait_sent(dmx_num, DMX_TIMEOUT_TICK);
}
// Blink LED when not connected
if (!deviceConnected)
{
digitalWrite(LED_PIN, HIGH);
delay(200);
digitalWrite(LED_PIN, LOW);
delay(200);
}
}
تست کردن
پس از آپلود کد در ESP32، می توانید با استفاده از یک برنامه ترمینال بلوتوث مانند nRF Connect به ESP32 متصل شوید. سپس می توانید داده های DMX را با استفاده از برنامه به ESP32 ارسال کنید و چراغ را کنترل کنید. همچنین میتوانید از نمایشگر سریال در Arduino IDE برای مشاهده دادههای DMX که از طریق بلوتوث دریافت میکنید استفاده کنید، اگر کد اشکالزدایی اختیاری را اضافه کنید.
نحوه ارسال اطلاعات DMX از طریق بلوتوث
برای ارسال داده های DMX از طریق بلوتوث، باید یک آرایه بایت با کانال DMX و جفت های مقدار ارسال کنید. به عنوان مثال، برای تنظیم کانال های قرمز و کم نور روی 255، باید آرایه بایت زیر را ارسال کنید: 01FF04FF
نتیجه نهایی
برای محصول نهایی، یک کیس سفارشی طراحی کردم که دارای کانکتور ESP32، RS485، led و XLR است. این کیس به صورت سه بعدی پرینت شده و با آهنربا برای دسترسی آسان به ماژول ESP32 و RS485 در کنار هم قرار گرفته است. کانکتور XLR در جلوی کیس تعبیه شده است و پورت USB ESP از پشت کیس با یک سوراخ برای LED قابل دسترسی است.
چند عکس از نتیجه نهایی