บทความต้นฉบับจาก www.jaredwolff.com
ผมพ่ายแพ้ - ใช้คืนเต็มพยายามทำ Bluetooth Low Energy Project ให้ทำงาน ไม่ได้ ระดม ไม่ว่า จะยอมแพ้
นั่นตอนแรกๆ ของ BLE ตอนนี้ง่ายขึ้นเยอะ Particle Mesh Bluetooth Library ไม่ใช่ข้อยกเว้น
บทความนี้สอนใช้ Particle Bluetooth API ตั้งค่า LED 3 สี ดูการเปลี่ยนแปลงบนอุปกรณ์ทุกตัวใน Mesh Network ใช้ Argon และ Xenon
พร้อมไหม? เริ่มเลย!
P.S. บทความยาว ต้องการ PDF ดาวน์โหลดได้จากลิงก์ต้นฉบับ
Stage 1: ตั้งค่า Bluetooth
- Download/Install Particle Workbench
- สร้าง Project ใหม่ ชื่อ
ble_mesh - เปิด
/src/<project>.ino - ตั้ง DeviceOS version > 1.3.0
เขียนโค้ด
ตั้งค่า Service พร้อม 3 Characteristics สำหรับความเข้มของ RGB LED
- ใน
Setup()เปิด App Control LED:RGB.control(true); - ตั้ง UUIDs ที่บนสุดไฟล์:
const char* serviceUuid = "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"; const char* red = "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"; const char* green = "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"; const char* blue = "6E400004-B5A3-F393-E0A9-E50E24DCCA9E";UUID = Unique Identifier แยก Service/Characteristics สร้างเองได้ด้วย
uuidgen(macOS) หรือ Online GUID Generator - ใน
Setup()เริ่มต้น Service:BleUuid rgbService(serviceUuid); - เริ่มต้น Characteristics ใน
Setup():BleCharacteristic redCharacteristic("red", BleCharacteristicProperty::WRITE_WO_RSP, red, serviceUuid, onDataReceived, (void*)red); BleCharacteristic greenCharacteristic("green", BleCharacteristicProperty::WRITE_WO_RSP, green, serviceUuid, onDataReceived, (void*)green); BleCharacteristic blueCharacteristic("blue", BleCharacteristicProperty::WRITE_WO_RSP, blue, serviceUuid, onDataReceived, (void*)blue);ใช้ Property
WRITE_WO_RSP(เขียนไม่ต้อง Response) Callback =onDataReceivedContext = UUID String เพื่อแยกว่าตัวไหนถูกเขียน - เพิ่ม Characteristics:
BLE.addCharacteristic(...)ทั้ง 3 ตัว - ตั้ง Callback Function (นอก Setup):
static void onDataReceived(const uint8_t* data, size_t len, const BlePeerDevice& peer, void* context) { } - ตั้ง Advertising ท้าย
Setup():BleAdvertisingData advData; advData.appendServiceUUID(rgbService); BLE.advertise(&advData);สร้าง Adv Data เพิ่ม Service UUID เริ่ม Advertising -> Discoverable!
ทดสอบ Stage 1
- เพิ่ม
SYSTEM_MODE(MANUAL); บนสุด ไปไม่ให้เชื่อม Mesh (กันกระพริบสีน้ำเงิน) - Flash DeviceOS 1.3.0-rc.1 (ดาวน์โหลดจาก GitHub Assets)
- DFU Mode: กด Mode + คลิก Reset ถึงกระพริบเหลือง
- Flash System Part:
particle flash --usb xenon-system-part1@1.3.0-rc.1.bin - VS Code: Compile Application (Local) แก้ Error
- Flash Application (Local)
- โทรศัพท์: เปิด BLE App (nRF Connect หรือ LightBlue Explorer)
- หา Device "Xenon-<id>" กดเข้าไปดู Services/Characteristics
- ตรวจ Service UUID
6E400001-B5A3-F393-E0A9-E50E24DCCA9Eและ Characteristics 3 ตัว
Stage 2: จัดการข้อมูล (Handling Data)
เขียนโค้ด
- Struct เก็บ State LED (บนสุดไฟล์):
typedef struct { uint8_t red; uint8_t green; uint8_t blue; } led_level_t; static led_level_t m_led_level; - ใน
onDataReceivedตรวจ Error: รับแค่ 1 Byte (if(len != 1) return;) - เช็ค Characteristic จาก Context:
if( context == red ) m_led_level.red = data[0]; else if( context == green ) m_led_level.green = data[0]; else if( context == blue ) m_led_level.blue = data[0]; - ตั้งค่า RGB LED:
RGB.color(m_led_level.red, m_led_level.green, m_led_level.blue);
ทดสอบ Stage 2
- DFU Mode, Flash Application (Local)
- เชื่อมต่อด้วย LightBlue Explorer
- กด Characteristic Red -> Write
FF-> LED แดงสว่าง - Write
00-> LED แดงดับ - ทำซ้ำ Green, Blue -> ควบคุม RGB ครบ 3 สีผ่าน BLE แล้ว!
Stage 3: แชร์ผ่าน Mesh Network
เขียนโค้ด
- ลบ/Comment
SYSTEM_MODE(MANUAL); - ตัวแปร Track Publish:
static bool m_publish = false;(Init ใน Setup) - ใน
onDataReceivedหลังตั้ง RGB:m_publish = true; - ใน
loop():if( m_publish ) { m_publish = false; Mesh.publish("red", String::format("%d", m_led_level.red)); Mesh.publish("green", String::format("%d", m_led_level.green)); Mesh.publish("blue", String::format("%d", m_led_level.blue)); } - Subscribe ใน
Setup():Mesh.subscribe("red", meshHandler); Mesh.subscribe("green", meshHandler); Mesh.subscribe("blue", meshHandler); - Mesh Handler (บนสุดไฟล์):
static void meshHandler(const char *event, const char *data) { String eventString = String(event); String dataString = String(data); if( eventString.equals("red") ) m_led_level.red = dataString.toInt(); else if( eventString.equals("green") ) m_led_level.green = dataString.toInt(); else if( eventString.equals("blue") ) m_led_level.blue = dataString.toInt(); else return; RGB.color(m_led_level.red, m_led_level.green, m_led_level.blue); }
ทดสอบ Stage 3
- Particle App: Setup Argon ก่อน (กระพริบสีน้ำเงิน -> Pairing จด Admin Password Mesh)
- Flash Argon Firmware 1.3.0 แล้ว Flash Tinker App
- Argon Solid Cyan (Cloud Connected) -> Cloud Flash โค้ด (เลือก DeviceOS 1.3.0-rc1, Argon, Device Name)
- Setup Xenon เข้า Mesh Network ผ่าน Phone App
- Cloud Flash Xenon ด้วยโค้ดเดียวกัน
- LightBlue Explorer: เชื่อม Argon หรือ Xenon
- เปลี่ยนค่า Characteristic LED -> LED เปลี่ยนสีบนอุปกรณ์ทุกตัว!
โค้ดเต็ม (Final Code)
/* Project ble_mesh - BLE + Mesh Example - Jared Wolff */
const char* serviceUuid = "6E400001-B5A3-F393-E0A9-E50E24DCCA9E";
const char* red = "6E400002-B5A3-F393-E0A9-E50E24DCCA9E";
const char* green = "6E400003-B5A3-F393-E0A9-E50E24DCCA9E";
const char* blue = "6E400004-B5A3-F393-E0A9-E50E24DCCA9E";
BleUuid rgbService(serviceUuid);
typedef struct { uint8_t red; uint8_t green; uint8_t blue; } led_level_t;
static led_level_t m_led_level;
static bool m_publish;
static void meshHandler(const char *event, const char *data) {
String eventString = String(event);
String dataString = String(data);
if( eventString.equals("red") ) m_led_level.red = dataString.toInt();
else if( eventString.equals("green") ) m_led_level.green = dataString.toInt();
else if( eventString.equals("blue") ) m_led_level.blue = dataString.toInt();
else return;
RGB.color(m_led_level.red, m_led_level.green, m_led_level.blue);
}
static void onDataReceived(const uint8_t* data, size_t len, const BlePeerDevice& peer, void* context) {
if( len != 1 ) return;
if( context == red ) m_led_level.red = data[0];
else if( context == green ) m_led_level.green = data[0];
else if( context == blue ) m_led_level.blue = data[0];
RGB.color(m_led_level.red, m_led_level.green, m_led_level.blue);
m_publish = true;
}
void setup() {
RGB.control(true);
m_led_level.red = m_led_level.green = m_led_level.blue = 0;
m_publish = false;
Mesh.subscribe("red", meshHandler);
Mesh.subscribe("green", meshHandler);
Mesh.subscribe("blue", meshHandler);
BleCharacteristic redCharacteristic("red", BleCharacteristicProperty::WRITE_WO_RSP, red, serviceUuid, onDataReceived, (void*)red);
BleCharacteristic greenCharacteristic("green", BleCharacteristicProperty::WRITE_WO_RSP, green, serviceUuid, onDataReceived, (void*)green);
BleCharacteristic blueCharacteristic("blue", BleCharacteristicProperty::WRITE_WO_RSP, blue, serviceUuid, onDataReceived, (void*)blue);
BLE.addCharacteristic(redCharacteristic);
BLE.addCharacteristic(greenCharacteristic);
BLE.addCharacteristic(blueCharacteristic);
BleAdvertisingData advData;
advData.appendServiceUUID(rgbService);
BLE.advertise(&advData);
}
void loop() {
if( m_publish ) {
m_publish = false;
Mesh.publish("red", String::format("%d", m_led_level.red));
Mesh.publish("green", String::format("%d", m_led_level.green));
Mesh.publish("blue", String::format("%d", m_led_level.blue));
}
}
สรุป
บทช่วยสอนนี้สอนเพิ่ม Bluetooth ใน Particle Mesh Project ความเป็นไปได้ไม่มีที่สิ้นสุด - เพิ่ม User/Admin Apps เข้าไปใน Experience ได้
ติดตามเนื้อหาเพิ่มเติมในหนังสือ The Ultimate Guide to Particle Mesh สมัครรับข่าวสารได้ลิงก์ต้นฉบับ