2025贸泽电子M-Design创意设计竞赛-懒人多端遥控灯
该项目使用了ESP32 C6 与 S3 配合Web ble,实现了多端可调懒人灯的设计,它的主要功能为:有遥控器,的同时配合运行在电脑、手机、平板的网页多端的开关 或 调整LED灯的亮度。
标签
BLE
ESP32
M-Design
Web ble
蓝牙主机、从机
XXU
更新2025-04-01
34

视频演示了懒人遥控灯的,电脑、手机端Web Bluetooth控制亮度 和 遥控器遥控亮度,多种控制方式。



一、设计简介


1、设计目标


本次活动 选择的是 任务三 蓝牙方向,我设计做一个懒人的多端遥跨平台控灯,可以在电脑、平板、手机上开关LED灯 或 调整LED灯亮度,在移动端可以无极调节亮度(调节范围0% -100%),并且配备一个遥控器实现直接遥控亮度(每次调整20%亮度,调节范围0% -100%)。


2、软硬件简介


涉及到的软、硬件、上位机平台如下:
多端跨平台上位机:Web BLE + HTML 的网页端
遥控灯遥控:ESP32 S3模块
遥控灯本体:ESP32 C6模块(安装到开发板上)
其他硬件:COB封装的LED,可调电流模块

3、软硬件功能简介


使用蓝牙技术,实现多端通讯数据交互,在这个通讯框架下,每一个通讯者的角色如下图:

Pasted image 20250323035345.png


遥控器作为蓝牙主机,需要调试的是单主机的功能,需要扫描广播、查找服务、发现handle等才能实现数据交互。

遥控灯作为蓝牙从机,需要调试的修改广播名 与 广播内容、新增自定义服务 与 自定义UUID。


二、代码实现


下面主要介绍核心代码 和 主要改动点,遥控灯代码运行框图如下:
Pasted image 20250323075818.png

1、遥控灯


遥控灯需要调试的是 修改广播名 与内容、新增自定义服务 与 UUID、PWM 。


1.1、修改广播名


广播名修改虽然简单,但是十分重要,用于区分设备(无论是手机 还是遥控器),告诉我们这个是我们要找的设备。


#define TEST_DEVICE_NAME            "xxu"

XXU 就是本人在电子森林的用户名。


1.2、修改广播内容


注:此修改是非必要修改,注释部分就是改动,作用是关闭显示信号强度、当前从机能提供的UUID等信息

//adv data
static esp_ble_adv_data_t adv_data = {
.set_scan_rsp = false,
.include_name = true,
.include_txpower = false,
// .min_interval = 0x0006, //slave connection min interval, Time = min_interval * 1.25 msec
// .max_interval = 0x0010, //slave connection max interval, Time = max_interval * 1.25 msec
.appearance = 0x00,
.manufacturer_len = 0, //TEST_MANUFACTURER_DATA_LEN,
.p_manufacturer_data = NULL, //&test_manufacturer[0],
.service_data_len = 0,
.p_service_data = NULL,
// .service_uuid_len = sizeof(adv_service_uuid128),
// .p_service_uuid = adv_service_uuid128,
// .flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT),
};

// scan response data
static esp_ble_adv_data_t scan_rsp_data = {
.set_scan_rsp = true,
.include_name = false,
.include_txpower = false,
//.min_interval = 0x0006,
//.max_interval = 0x0010,
.appearance = 0x00,
.manufacturer_len = 0, //TEST_MANUFACTURER_DATA_LEN,
.p_manufacturer_data = NULL, //&test_manufacturer[0],
.service_data_len = 0,
.p_service_data = NULL,
// .service_uuid_len = sizeof(adv_service_uuid128),
// .p_service_uuid = adv_service_uuid128,
// .flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT),
};


1.3、新增自定义服务


新增服务涉及到很多代码,我是以文件从上到下的顺序展示代码的。

TEST_C 后缀的是新增代码,新增的宏就是要用到的服务。

  • 0xFF01 是服务的UUID
  • 0xFF02 Write数据UUID(主机发送数据)
  • 0xFF03 Notify数据UUID(从机上报数据) 但是此工程不用上报数据所以没有启用
#define GATTS_SERVICE_UUID_TEST_B   0x00EE
#define GATTS_CHAR_UUID_TEST_B 0xEE01
#define GATTS_DESCR_UUID_TEST_B 0x2222
#define GATTS_NUM_HANDLE_TEST_B 4

#define GATTS_SERVICE_UUID_TEST_C 0xFF01
#define GATTS_CHAR_UUID_TEST_C 0xFF02
#define GATTS_CHAR_UUID_TEST_C_2 0xFF03
#define GATTS_DESCR_UUID_TEST_C 0x1111
#define GATTS_NUM_HANDLE_TEST_C 7



修改服务的总数 和 新增服务的编号,编号用于区分不同的服务

#define PROFILE_NUM 3 //服务的总数
#define PROFILE_A_APP_ID 0
#define PROFILE_B_APP_ID 1
#define PROFILE_C_APP_ID 2 //新增服务的编号



PROFILE_C_APP_ID 为新增代码,用于注册新增的服务,在蓝牙初始化时默认调用,这里非常非常重要!!!

static struct gatts_profile_inst gl_profile_tab[PROFILE_NUM] = {
[PROFILE_A_APP_ID] = {
.gatts_cb = gatts_profile_a_event_handler,
.gatts_if = ESP_GATT_IF_NONE,
/* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */
},
[PROFILE_B_APP_ID] = {
.gatts_cb = gatts_profile_b_event_handler,
/* This demo does not implement, similar as profile A */
.gatts_if = ESP_GATT_IF_NONE,
/* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */
},

[PROFILE_C_APP_ID] = {
.gatts_cb = gatts_profile_c_event_handler,
/* This demo does not implement, similar as profile A */
.gatts_if = ESP_GATT_IF_NONE,
/* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */
},

};



c_prepare_write_env 是新增的写事件的结构体

static prepare_type_env_t a_prepare_write_env;
static prepare_type_env_t b_prepare_write_env;
static prepare_type_env_t c_prepare_write_env;



gatts_profile_c_event_handler 是模仿其他服务的事件回调函数写的,主要是在接收数据处添加设置PWM的代码,在接受数据后,直接设置PWM大小。


case ESP_GATTS_WRITE_EVT: {
ESP_LOGI(GATTS_TAG, "GATT_WRITE_EVT, conn_id %d, trans_id %" PRIu32 ", handle %d", param->write.conn_id, param->write.trans_id, param->write.handle);
if (!param->write.is_prep){
ESP_LOGI(GATTS_TAG, "GATT_WRITE_EVT, value len %d, value :", param->write.len);
esp_log_buffer_hex(GATTS_TAG, param->write.value, param->write.len);

update_duty(param->write.value[0]);



gatts_profile_c_event_handler 所有代码

static void gatts_profile_c_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) {
switch (event) {
case ESP_GATTS_REG_EVT:
ESP_LOGI(GATTS_TAG, "REGISTER_APP_EVT, status %d, app_id %d", param->reg.status, param->reg.app_id);
gl_profile_tab[PROFILE_C_APP_ID].service_id.is_primary = true;
gl_profile_tab[PROFILE_C_APP_ID].service_id.id.inst_id = 0x00;
gl_profile_tab[PROFILE_C_APP_ID].service_id.id.uuid.len = ESP_UUID_LEN_16;
gl_profile_tab[PROFILE_C_APP_ID].service_id.id.uuid.uuid.uuid16 = GATTS_SERVICE_UUID_TEST_C;

esp_ble_gatts_create_service(gatts_if, &gl_profile_tab[PROFILE_C_APP_ID].service_id, GATTS_NUM_HANDLE_TEST_C);
break;
case ESP_GATTS_READ_EVT: {
// ESP_LOGI(GATTS_TAG, "GATT_READ_EVT, conn_id %d, trans_id %" PRIu32 ", handle %d", param->read.conn_id, param->read.trans_id, param->read.handle);
// esp_gatt_rsp_t rsp;
// memset(&rsp, 0, sizeof(esp_gatt_rsp_t));
// rsp.attr_value.handle = param->read.handle;
// rsp.attr_value.len = 4;
// rsp.attr_value.value[0] = 0xde;
// rsp.attr_value.value[1] = 0xed;
// rsp.attr_value.value[2] = 0xbe;
// rsp.attr_value.value[3] = 0xef;
// esp_ble_gatts_send_response(gatts_if, param->read.conn_id, param->read.trans_id,
// ESP_GATT_OK, &rsp);
break;
}
case ESP_GATTS_WRITE_EVT: {
ESP_LOGI(GATTS_TAG, "GATT_WRITE_EVT, conn_id %d, trans_id %" PRIu32 ", handle %d", param->write.conn_id, param->write.trans_id, param->write.handle);
if (!param->write.is_prep){
ESP_LOGI(GATTS_TAG, "GATT_WRITE_EVT, value len %d, value :", param->write.len);
esp_log_buffer_hex(GATTS_TAG, param->write.value, param->write.len);
update_duty(param->write.value[0]);



if (gl_profile_tab[PROFILE_C_APP_ID].descr_handle == param->write.handle && param->write.len == 2){
uint16_t descr_value= param->write.value[1]<<8 | param->write.value[0];
if (descr_value == 0x0001){
if (b_property & ESP_GATT_CHAR_PROP_BIT_NOTIFY){
ESP_LOGI(GATTS_TAG, "notify enable");
uint8_t notify_data[15];
for (int i = 0; i < sizeof(notify_data); ++i)
{
notify_data[i] = i%0xff;
}
//the size of notify_data[] need less than MTU size
esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id, gl_profile_tab[PROFILE_C_APP_ID].char_handle,
sizeof(notify_data), notify_data, false);
}
}else if (descr_value == 0x0002){
if (b_property & ESP_GATT_CHAR_PROP_BIT_INDICATE){
ESP_LOGI(GATTS_TAG, "indicate enable");
uint8_t indicate_data[15];
for (int i = 0; i < sizeof(indicate_data); ++i)
{
indicate_data[i] = i%0xff;
}
//the size of indicate_data[] need less than MTU size
esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id, gl_profile_tab[PROFILE_C_APP_ID].char_handle,
sizeof(indicate_data), indicate_data, true);
}
}
else if (descr_value == 0x0000){
ESP_LOGI(GATTS_TAG, "notify/indicate disable ");
}else{
ESP_LOGE(GATTS_TAG, "unknown value");
}

}
}
example_write_event_env(gatts_if, &c_prepare_write_env, param);
break;
}
case ESP_GATTS_EXEC_WRITE_EVT:
ESP_LOGI(GATTS_TAG,"ESP_GATTS_EXEC_WRITE_EVT");
esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, ESP_GATT_OK, NULL);
example_exec_write_event_env(&c_prepare_write_env, param);
break;
case ESP_GATTS_MTU_EVT:
ESP_LOGI(GATTS_TAG, "ESP_GATTS_MTU_EVT, MTU %d", param->mtu.mtu);
break;
case ESP_GATTS_UNREG_EVT:
break;
case ESP_GATTS_CREATE_EVT:
ESP_LOGI(GATTS_TAG, "CREATE_SERVICE_EVT, status %d, service_handle %d", param->create.status, param->create.service_handle);
gl_profile_tab[PROFILE_C_APP_ID].service_handle = param->create.service_handle;
gl_profile_tab[PROFILE_C_APP_ID].char_uuid.len = ESP_UUID_LEN_16;
gl_profile_tab[PROFILE_C_APP_ID].char_uuid.uuid.uuid16 = GATTS_CHAR_UUID_TEST_C;

esp_ble_gatts_start_service(gl_profile_tab[PROFILE_C_APP_ID].service_handle);
b_property = ESP_GATT_CHAR_PROP_BIT_READ | ESP_GATT_CHAR_PROP_BIT_WRITE | ESP_GATT_CHAR_PROP_BIT_NOTIFY;
esp_err_t add_char_ret =esp_ble_gatts_add_char( gl_profile_tab[PROFILE_C_APP_ID].service_handle, &gl_profile_tab[PROFILE_C_APP_ID].char_uuid,
ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,
b_property,
NULL, NULL);
if (add_char_ret){
ESP_LOGE(GATTS_TAG, "add char failed, error code =%x",add_char_ret);
}

break;
case ESP_GATTS_ADD_INCL_SRVC_EVT:
break;
case ESP_GATTS_ADD_CHAR_EVT:
ESP_LOGI(GATTS_TAG, "ADD_CHAR_EVT, status %d, attr_handle %d, service_handle %d",
param->add_char.status, param->add_char.attr_handle, param->add_char.service_handle);

gl_profile_tab[PROFILE_C_APP_ID].char_handle = param->add_char.attr_handle;
gl_profile_tab[PROFILE_C_APP_ID].descr_uuid.len = ESP_UUID_LEN_16;
gl_profile_tab[PROFILE_C_APP_ID].descr_uuid.uuid.uuid16 = ESP_GATT_UUID_CHAR_CLIENT_CONFIG;
esp_ble_gatts_add_char_descr(gl_profile_tab[PROFILE_C_APP_ID].service_handle, &gl_profile_tab[PROFILE_C_APP_ID].descr_uuid,
ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,
NULL, NULL);
break;
case ESP_GATTS_ADD_CHAR_DESCR_EVT:
gl_profile_tab[PROFILE_C_APP_ID].descr_handle = param->add_char_descr.attr_handle;
ESP_LOGI(GATTS_TAG, "ADD_DESCR_EVT, status %d, attr_handle %d, service_handle %d",
param->add_char_descr.status, param->add_char_descr.attr_handle, param->add_char_descr.service_handle);
break;
case ESP_GATTS_DELETE_EVT:
break;
case ESP_GATTS_START_EVT:
ESP_LOGI(GATTS_TAG, "SERVICE_START_EVT, status %d, service_handle %d",
param->start.status, param->start.service_handle);
break;
case ESP_GATTS_STOP_EVT:
break;
case ESP_GATTS_CONNECT_EVT:
ESP_LOGI(GATTS_TAG, "CONNECT_EVT, conn_id %d, remote %02x:%02x:%02x:%02x:%02x:%02x:",
param->connect.conn_id,
param->connect.remote_bda[0], param->connect.remote_bda[1], param->connect.remote_bda[2],
param->connect.remote_bda[3], param->connect.remote_bda[4], param->connect.remote_bda[5]);
gl_profile_tab[PROFILE_C_APP_ID].conn_id = param->connect.conn_id;
break;
case ESP_GATTS_CONF_EVT:
ESP_LOGI(GATTS_TAG, "ESP_GATTS_CONF_EVT status %d attr_handle %d", param->conf.status, param->conf.handle);
if (param->conf.status != ESP_GATT_OK){
esp_log_buffer_hex(GATTS_TAG, param->conf.value, param->conf.len);
}
break;
case ESP_GATTS_DISCONNECT_EVT:
case ESP_GATTS_OPEN_EVT:
case ESP_GATTS_CANCEL_OPEN_EVT:
case ESP_GATTS_CLOSE_EVT:
case ESP_GATTS_LISTEN_EVT:
case ESP_GATTS_CONGEST_EVT:
default:
break;
}
}


1.4、PWM初始化 与 设置


PWM用于设置恒流模块的电流大小

初始化代码

#define LEDC_TIMER              LEDC_TIMER_0
#define LEDC_MODE LEDC_LOW_SPEED_MODE
#define LEDC_OUTPUT_IO (16) // Define the output GPIO
#define LEDC_CHANNEL LEDC_CHANNEL_0
#define LEDC_DUTY_RES LEDC_TIMER_13_BIT // Set duty resolution to 13 bits
#define LEDC_DUTY (4096) // Set duty to 50%. (2 ** 13) * 50% = 4096
#define LEDC_FREQUENCY (2000) // Frequency in Hertz. Set frequency at 4 kHz


static void example_ledc_init(void)
{
// Prepare and then apply the LEDC PWM timer configuration
ledc_timer_config_t ledc_timer = {
.speed_mode = LEDC_MODE,
.timer_num = LEDC_TIMER,
.duty_resolution = LEDC_DUTY_RES,
.freq_hz = LEDC_FREQUENCY, // Set output frequency at 4 kHz
.clk_cfg = LEDC_AUTO_CLK
};
ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));

// Prepare and then apply the LEDC PWM channel configuration
ledc_channel_config_t ledc_channel = {
.speed_mode = LEDC_MODE,
.channel = LEDC_CHANNEL,
.timer_sel = LEDC_TIMER,
.intr_type = LEDC_INTR_DISABLE,
.gpio_num = LEDC_OUTPUT_IO,
.duty = 0, // Set duty to W0%
.hpoint = 0
};
ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
}



自行封装的设置PWM API

void update_duty(uint8_t duty){

// 设置占空比为50%
ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, ((8192*duty)/100));
// 更新通道占空比
ledc_update_duty(LEDC_MODE, LEDC_CHANNEL);

}



1.5、烧录与实验


添加上述所有代码后,烧录运行的效果如下:


广播
Pasted image 20250323054048.png


服务 与 特征
Pasted image 20250323054108.png


2、遥控器


遥控器是蓝牙主机,使用gatt_client示例上修改。遥控器端代码运行框图如下:
Pasted image 20250323075231.png

2.1、添加目标服务UUID


目标服务的短UUID,用于查找、发现服务的handle,确认后才能正常发送数据。

#define REMOTE_SERVICE_UUID       0xFF01
#define REMOTE_WRITE_CHAR_UUID 0xFF02


2.2、修改目标广播名


目标的广播名,用于筛选、确认是正确的目标设备

static const char remote_device_name[] = "xxu";


2.3、添加写数据的特征结构体


添加写数据的特征结构体 用于查找特征时,输入的目标服务的UUID。

static esp_bt_uuid_t write_char_uuid = {
.len = ESP_UUID_LEN_16,
.uuid = {.uuid16 = REMOTE_WRITE_CHAR_UUID,},
};


2.4、确认写数据的handle


确认写数据的handle,这一段非常非常重要,如果handle错误了,从机永远接收不到数据,即使API回调显示发送成功。

status = esp_ble_gattc_get_char_by_uuid( gattc_if,
p_data->search_cmpl.conn_id,
gl_profile_tab[PROFILE_A_APP_ID].service_start_handle,
gl_profile_tab[PROFILE_A_APP_ID].service_end_handle,
write_char_uuid,
char_elem_result,
&count);
if (status != ESP_GATT_OK){
ESP_LOGE(GATTC_TAG, "esp_ble_gattc_get_char_by_uuid error----");
free(char_elem_result);
char_elem_result = NULL;
break;
}
ESP_LOGE(GATTC_TAG, "write handle start %x ",char_elem_result[0].properties);

/* Every service have only one char in our 'ESP_GATTS_DEMO' demo, so we used first 'char_elem_result' */
/*ESP_GATT_CHAR_PROP_BIT_WRITE_NR*/
if (count > 0 && (char_elem_result[0].properties & ESP_GATT_CHAR_PROP_BIT_WRITE )){
write_handle = char_elem_result[0].char_handle;
ESP_LOGE(GATTC_TAG, "write handle = %x", write_handle);
}


添加在:

static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) {

。。。。略。。。。
case ESP_GATTC_SEARCH_CMPL_EVT:{
添加到这里!
}
break
。。。。略。。。。
}


2.5、自封装数据发送API


在原厂基础的API上,重新封装,简化输出的参数,用起来更方便。

static uint8_t test_data[1] = {0};

void ble_write_data(uint8_t *data,uint8_t len){
ESP_LOGE(GATTC_TAG, "len %u data %x", len, data[0]);

esp_ble_gattc_write_char( gl_profile_tab[PROFILE_A_APP_ID].gattc_if,
gl_profile_tab[PROFILE_A_APP_ID].conn_id,
write_handle,
len,
(uint8_t*)data,
ESP_GATT_WRITE_TYPE_RSP,
ESP_GATT_AUTH_REQ_NONE);
}



三、硬件简介 与 连接


本端介绍使用到的硬件 和 硬件与开发板连接的示意图


1、硬件简介


COB封装的LDE灯条,看似一颗LED其实是多颗LED封装在同一块铝基板上,如下图每一颗小黑点就是一颗LED。


image.png


LED可调恒流驱动模块
Pasted image 20250323061400.png

调整LED亮度的方法主要有两种:

  • 调整电压:
    调整电压,是通过不断的关断、导通电源,在单位时间内调整平均电压 。

    通过改变LED两端的电压来调节亮度。理论上,当LED两端电压增加时,其电流也会增加,从而亮度变亮。但是LED是一种非线性器件,其伏安特性曲线比较陡峭。在正常工作电压范围内,电压的微小变化可能会导致电流的较大变化。例如,对于一个额定电压为3.2V的蓝色LED,当电压从3.2V增加到3.3V时,电流可能会从20mA增加到30mA左右,亮度会有明显变化。

    所以会有亮度突变 与 频闪问题


  • 调整电流
    使用恒流源电路来为LED供电。恒流源可以精确地控制输出电流。常见的恒流源有线性恒流源和开关型恒流源。

    线性恒流源:通过调整功率管的导通程度来控制电流。控制流过LED的电流,实现亮度调节。

    开关型恒流源:利用MOS高频开关来调节电流,利用储能元件,电感、电容组成的电路,不断的导通与闭合维持LED电流的连续性。通过改变控制信号的占空比,可以调节流过LED的平均电流,从而实现亮度调节。

    所以选择线性恒流源可以避免亮度突变 与 频闪问题。

2、接线图


LED可调恒流驱动模块接线示意图
Pasted image 20250323061417.png

整体接线图 模拟
Pasted image 20250323073810.png


整体接线图 实物与各模块的功用

image.png


四、Web Bluetooth

1、概念

Web Bluetooth API是一种浏览器提供的 JavaScript API,允许网页应用直接与蓝牙低功耗(BLE)设备进行通信。它使得 Web 开发者能够构建出能够与各种蓝牙设备通信的 Web 应用,从而扩展了 Web 的功能范围。

但是目前 Web Bluetooth API 规范尚未最终确定,还处于实验室验证阶段。

2、功能

  • 设备扫描:可以扫描附近的蓝牙设备,发现周围可用的 BLE 设备。
  • 设备连接:能够连接到指定的蓝牙设备,建立通信链路。
  • 服务和特征读取:读取蓝牙设备的服务和特征值,了解设备提供的功能。
  • 数据读写:可以读取和写入蓝牙设备的数据,实现对设备的控制和数据交互。
  • 事件监听:可以监听蓝牙设备的状态变化和数据更新,及时响应设备的事件。

3、工作原理

  • 基于 GATT 协议:Web Bluetooth API 基于通用属性配置文件(GATT)协议工作,该协议定义了设备之间进行数据交换的一种通用方式。在通信过程中,Web 应用作为 GATT 客户端,而蓝牙设备作为 GATT 服务器。
  • 设备交互:通过 navigator.bluetooth 对象来发现、连接并与 BLE 设备进行通信。可以使用 requestDevice 方法请求匹配特定过滤条件的 BLE 设备,然后使用 gatt.connect 方法连接到设备,再通过 getPrimaryServicegetCharacteristic 等方法获取所需的服务和特征,最后进行数据读写等操作。

4、优势

  • 跨平台:支持多种操作系统和浏览器,如 Chrome、三星 Internet 等,具有良好的兼容性。
  • 安全性:通过浏览器的安全机制,如用户授权等,确保数据传输的安全性。
  • 易用性:提供简单易用的 API,降低了开发难度,方便开发者快速上手。
  • 低功耗:支持 BLE 设备,降低了设备的能耗,适用于需要长时间运行的物联网设备。

5、我的Web Bluetooth

比较简单,但是能用
Pasted image 20250323080826.png

综上Web Bluetooth 目前可以满足一些简单的应用,仅仅跨平台这一点就非常非常香了,希望尽快推进规范落地!!


五、GIF演示

以下是多端控制灯光亮度的GIF


5.1、电脑WebBle控制



5.2、手机 Web Ble控制



5.3、蓝牙遥控器控制



六、总结

感谢贸泽 与 电子森林联合举办的本次活动,让我有幸参加。

本次的开发遇到了不少难点,虽然晚上可以查到相关的资料,但是都不太详细,所以本次记录的比较详细。

软硬件
电路图
附件下载
M-Design 提交资料.zip
懒人灯遥控器 与 懒人灯 的代码 ,Web Bluetooth 的html文件
团队介绍
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号