Prechádzať zdrojové kódy

feat: 蓝牙命令
蓝牙模块命令控制功能制作

kindring 1 rok pred
rodič
commit
77dc910ddf
7 zmenil súbory, kde vykonal 371 pridanie a 60 odobranie
  1. 7 1
      src/OSAL_wrist.c
  2. 4 5
      src/app_wrist.c
  3. 14 7
      src/ble/wrist_service.c
  4. 8 8
      src/control/btn.c
  5. 248 21
      src/light.c
  6. 71 6
      src/light.h
  7. 19 12
      蓝牙灯光设计.md

+ 7 - 1
src/OSAL_wrist.c

@@ -28,6 +28,9 @@
 /* btn */
 #include "btn.h"
 
+/* light */
+#include "light.h"
+
 /* Application */
 #include "app_wrist.h"
 
@@ -98,7 +101,10 @@ void osalInitTasks( void )
 
   /** 初始化按钮 事件*/
   btn_init(taskID++);
-
+  LOG("btn_init end\n");
+  /** 初始化灯光*/
+  light_init(taskID++);
+  LOG("light_init end\n");
   /* Application */
   appWristInit( taskID );
 }

+ 4 - 5
src/app_wrist.c

@@ -301,6 +301,7 @@ void appWristInit( uint8 task_id)
     GATTServApp_AddService( GATT_ALL_SERVICES ); // GATT attributes
     DevInfo_AddService( );
     ota_app_AddService();
+    // 初始化蓝牙模块
     wristProfile_AddService(wristCB);
 
     app_datetime_init();
@@ -309,13 +310,11 @@ void appWristInit( uint8 task_id)
     osal_set_event( AppWrist_TaskID, START_DEVICE_EVT );
 
     LOG("appWristInit end\n");
-    light_init();
-    LOG("light_init end\n");
+    
     // 初始化按键
-    temp_set(5500);
+    temp_set(5500, 0, NULL);
     LOG("temp_set end\n");
-    light_set(100);
-
+    light_set(100, 0, NULL);
     LOG("appWristInit end\n");
 
 }

+ 14 - 7
src/ble/wrist_service.c

@@ -380,15 +380,17 @@ int on_recieved_cmd_packet(const uint8* data, uint16 len)
   uint8 err_data = 0;
   LOG("RX Cmd:");
   print_hex(data, len);
-  int i = 0;
+  int i = 0,ret = 0;
+  uint8 *resData;
+  uint16 resLen;
   // 第一位为命令字 第二位为 值
   // cmdParse();
-  LOG("light ctrl %d\n", data[0]);
-  
-  light_set( data[0] );
-
+  resLen = parse_light_code(data, len, resData);
+  if(resLen < 1){
+    resLen = light_set(data[0], 0, resData);
+  }
   LOG("response all data");
-  cmd_response(data, len );
+  cmd_response(resData, resLen);
   return ret;
 }
 
@@ -427,6 +429,10 @@ static int cmd_response(const uint8* data, uint16 len)
   return wristProfile_Notify(&notif);
 }
 
+CallbackFunc light_callback(uint8 *res, uint16 len){
+  return cmd_response(res, len);
+}
+
 
 /*********************************************************************
  * PROFILE CALLBACKS
@@ -524,7 +530,8 @@ bStatus_t wristProfile_AddService(wristServiceCB_t cb)
                                           &wristProfileCBs );
 
   wristServiceCB = cb;
-
+  // 注册led回调函数
+  light_register_notify_callback(light_callback);
   return ( status );
 }
 

+ 8 - 8
src/control/btn.c

@@ -28,19 +28,19 @@ void changeTemp(int i)
 	switch (i)
 	{
 	case 0:
-		temp = temp + 50;
-		if (temp > 6500){ temp = 6500;}
+		temp = temp + TEMP_STEP;
+		if (temp > TEMP_MAX){ temp = TEMP_MAX;}
 		break;
 	case 1:
-		temp = temp - 50;
-		if (temp < 2500){ temp = 2500;}
+		temp = temp - TEMP_STEP;
+		if (temp < TEMP_MIN){ temp = TEMP_MIN;}
 		break;
 	default:
 	
 		break;
 	}
 	LOG("changeTemp temp=>>> %d\n", temp);
-	temp_set(temp);
+	temp_set(temp, 0, NULL);
 }
 
 void changeLight(int i)
@@ -49,18 +49,18 @@ void changeLight(int i)
 	switch (i)
 	{
 	case 0:
-		light = light + 5;
+		light = light + LIGHT_STEP;
 		if (light > 100){ light = 100;}
 		break;
 	case 1:
-		light = light - 5;
+		light = light - LIGHT_STEP;
 		if (light < 0){ light = 0;}
 		break;
 	default:
 	
 		break;
 	}
-	light_set(light);
+	light_set(light, 0, NULL);
 }
 
 static void key_press_evt(uint8_t i,key_evt_t key_evt)

+ 248 - 21
src/light.c

@@ -9,12 +9,18 @@
 
 #include "light.h"
 
-uint32_t _light_total = 10000;
+uint8 task_light_id;
+
+uint32_t _light_total = TOTAl_LEVEL;
 // 配置 
-light_data_t light_data = {6500, 0};
+light_data_t light_data = {true, TEMP_MAX, 100, LIGHT_MODE_DEFAULT, 100};
+
+// 通知回调函数
+CallbackFunc notify_callback = NULL;
 
-int light_init(){
+int light_init(int taskId){
     LOG("[light_init]\n");
+    task_light_id = taskId
     // 初始化pwm
     int ret = 0;
     // if()
@@ -56,34 +62,32 @@ int comLightVal(){
     // 色温范围 2500~6500
     // 暖色温最大亮度值 = 亮度值 * (色温 - 2500) / (6500 - 2500)
     // 亮度调整偏移后的暖色温亮度值 = 暖色温最大亮度值 + 亮度值 * 2500 / (6500 - 2500)
-    int tmp_warm_val = _light_total * (temp_val - 2500) / (6500 - 2500);
+    int tmp_warm_val = _light_total * (temp_val - TEMP_MIN) / (TEMP_MAX - TEMP_MIN);
     int tmp_cold_val = _light_total - tmp_warm_val;
     int warm_val = light_val * tmp_warm_val / 100;
     int cold_val = light_val * tmp_cold_val / 100;
     LOG("[comLightVal] light: %d temp: %d warm_val %d, cold_val %d \n", light_val, temp_val, warm_val, cold_val);
+    
+    if(light_data.mode == LIGHT_MODE_FULL){
+        // 全亮模式
+        warm_val = light_val * _light_total / 100;
+        cold_val = light_val * _light_total / 100;
+    }
+
+
+    if(!light_data.open){
+        // 设备已经关闭, 设置为 0
+        warm_val = 0;
+        cold_val = 0;
+    }
+
     light_ch_set(WARM_CH, warm_val);
     light_ch_set(COLD_CH, cold_val);
     return 0;
 }
 
-// 亮度调节
-int light_set(uint8_t val){
-    // LOG("[light_set] set light val to %d \n", val);
-    // 亮度值为 0~100
-    light_data.light = val;
-    comLightVal();
-    return 0;
-}
-
-// 色温调节
-int temp_set(int temp){
-    // LOG("[temp_set] set temp val to %d \n", temp);
-    light_data.temp = temp;
-    comLightVal();
-    return 0;
-}
 
-// js code
+// [DEBUG] js code
 // function comLightVal(light_val, temp_val){
 //     // 根据色温与亮度来计算冷暖灯光对应的亮度值
 //     // 亮度值范围 0~100
@@ -96,3 +100,226 @@ int temp_set(int temp){
 //     let cold_val = light_val * tmp_cold_val / 100;
 //     console.log(`[comLightVal] warm_val: ${warm_val} , cold_val: ${cold_val}\n`);
 // }
+
+// 计算命令响应码
+uint16 comCmdResCode(uint8_t cmd, uint8_t sn, uint8* data, uint16 len  uint8_t *res){
+    // 响应码 `起始码` `长度` `命令码` `sn码` `数据1` `数据2`
+    // 0x6c 0x06 0x01 0x64 
+    if (res == NULL)
+    {
+        return;
+    }
+    // 判断是否有sn码,有则为响应,没有则为通知
+    if(sn == 0)
+    {
+        res[0] = START_CODE_NOTIFY;
+    }else
+    {
+        res[0] = START_CODE_RES;
+    }
+    // 数据长度
+    res[1] = len + 2;
+    // 命令码
+    res[2] = cmd;
+    // sn码
+    res[3] = sn;
+    // 数据可能为空
+    if(len > 0){
+        memcpy(res + 4, data, len);
+    }
+    return len + 4;
+}
+
+
+// 灯光控制功能
+uint16 open_light(uint8_t sn , uint8 *res){
+    light_data.open = true;
+    comLightVal();
+    uint16 resLen = 0;
+    if(res == NULL && notify_callback != NULL)
+    {
+        resLen = comCmdResCode(CMD_CLOSE, sn, NULL, 0, res);
+        notify_callback(res, resLen);
+    }else{
+        resLen =  comCmdResCode(CMD_CLOSE, sn, NULL, 0, res);
+    }
+    return resLen;
+}
+
+// 关闭灯光
+uint16 close_light(uint8_t sn , uint8 *res){
+    light_data.open = false;
+    comLightVal();
+    uint16 resLen = 0;
+    if(res == NULL && notify_callback != NULL)
+    {
+        resLen = comCmdResCode(CMD_CLOSE, sn, NULL, 0, res);
+        notify_callback(res, resLen);
+    }else{
+        resLen = (CMD_CLOSE, sn, NULL, 0, res);
+    }
+    return resLen;
+}
+
+// 灯光状态查询
+uint16 query_light(uint8_t sn , uint8 *res){
+    uint8_t data[4] = {
+        light_data.open, 
+        light_data.temp, 
+        light_data.light, 
+        light_data.mode
+    };
+    return comCmdResCode(CMD_QUERY, sn, data, 4, res);
+}
+
+// 亮度调节
+uint16 light_set(uint8_t val, uint8_t sn , uint8 *res){
+    // LOG("[light_set] set light val to %d \n", val);
+    // 亮度值为 0~100
+    if(val > 100){
+        val = 100;
+    }
+    light_data.light = val;
+    comLightVal();
+    uint8_t data[1] = {val};
+    uint16 resLen = 0;
+    if(res == NULL && notify_callback != NULL)
+    {
+        resLen = comCmdResCode(CMD_LIGTH, sn, data, 1, res);
+        notify_callback(res, resLen);
+    }else{
+        resLen = comCmdResCode(CMD_LIGTH, sn, data, 1, res);
+    }
+    return resLen;
+}
+
+// 色温调节
+uint16 temp_set(int temp, uint8_t sn , uint8 *res){
+    // LOG("[temp_set] set temp val to %d \n", temp);
+    if(temp > TEMP_MAX){
+        temp = TEMP_MAX;
+    }
+    if(temp < TEMP_MIN){
+        temp = TEMP_MIN;
+    }
+    light_data.temp = temp;
+    comLightVal();
+    // 如果res是空,则尝试调用通知回调函数
+    uint8_t data[2] = {temp >> 8, temp & 0xff};
+    uint16 resLen = 0;
+    if(res == NULL && notify_callback != NULL)
+    {
+        resLen = comCmdResCode(CMD_TEMP, sn, data, 2, res);
+        notify_callback(res);
+    }else{
+        resLen = comCmdResCode(CMD_TEMP, sn, data, 2, res);
+    }
+    return resLen;
+}
+
+/**
+ * led 模式切换
+*/
+uint16 change_light_mode (light_cmd_start_code mode, uint8_t sn, uint8 *res)
+{
+    if (mode == light_data.mode)
+    {
+        // 模式相同, 不做处理
+        return -1;
+    }
+    light_data.mode = mode;
+    if (mode == LIGHT_MODE_FULL)
+    {
+        // 全亮模式, 切换至最大模式
+        light_data.light = 100;
+        light_data.fun = 100;
+        // 添加定时器, 1分钟后切换至默认模式 
+        osal_start_timerEx( task_light_id, LIGHT_EVT_DEFAULT_MODE, FULL_MODE_WAIT_TIME * 1000);
+    }
+    comLightVal();
+    uint8_t data[1] = {mode};
+    uint16 resLen = 0;
+    if(res == NULL && notify_callback != NULL)
+    {
+        resLen = comCmdResCode(CMD_MODE, sn, data, 1, res);
+        notify_callback(res);
+    }else{
+        resLen = comCmdResCode(CMD_MODE, sn, data, 1, res);
+    }
+    return resLen;
+}
+
+// 指令匹配
+// 指令码 `起始码` `长度` `命令码` `sn码` `数据1` `数据2`
+// 最低长度要为 4 个字节, 部分命令没有数据
+// 0x6c 0x06 0x01 0x64 
+/**
+ * 指令解析
+ * @param {uint8*} data 数据
+ * @param {uint16} len 数据长度
+ * @param {uint8*} res 返回数据 
+*/
+uint16 parse_light_code(uint8* data, uint16 len, uint8 *res)
+{
+    // 判断是否为灯光指令 
+    if (len < 4 || data[0] != START_CODE_CMD)
+    {
+        LOG("[parse_light_code] not light cmd \n");
+        return -1;
+    }
+
+    light_cmd_t light_cmd;
+
+    memcpy(&light_cmd, data, len);
+    // 现在你可以使用 light_cmd 结构体中的数据了
+    printf("Start Code: %d\n", light_cmd.startCode);
+    printf("Length: %d\n", light_cmd.len);
+    printf("Command Code: %d\n", light_cmd.cmd);
+    // 如果需要访问数据部分,可以使用 light_cmd.data 指针
+    switch (light_cmd.cmd)
+    {
+    case CMD_OPEN:
+        return open_light(light_cmd.sn, &res);
+        break;
+    case CMD_CLOSE:
+        return close_light(light_cmd.sn, &res);
+        break;
+    case CMD_QUERY:
+        return query_light(light_cmd.sn, &res);
+        break;
+    case CMD_LIGTH:
+        return light_set(light_cmd.data[0], light_cmd.sn, &res);
+        break;
+    case CMD_TEMP:
+        return temp_set(light_cmd.data[0] << 8 | light_cmd.data[1], light_cmd.sn, &res);
+        break;
+    case CMD_MODE:
+        return change_light_mode(light_cmd.data[0], light_cmd.sn, &res);
+        break;
+    }
+    return -1;
+}
+
+// 注册通知回调函数,用于再某些情况下通知上位机
+void light_register_notify_callback(CallbackFunc callback)
+{
+    notify_callback = callback;
+}
+
+
+// 定时器事件处理
+uint16 Light_ProcessEvent( uint8 task_id, uint16 events )
+{
+    if(task_id != task_light_id){
+		return 0;
+	}
+    if( events & LIGHT_EVT_DEFAULT_MODE){
+        // 切换至默认模式
+        uint8 *res;
+        change_light_mode(LIGHT_MODE_DEFAULT, 0, res);
+        if(notify_callback != NULL){
+            notify_callback(res);
+        }
+        return (events ^ LIGHT_EVT_DEFAULT_MODE);
+    }
+}

+ 71 - 6
src/light.h

@@ -1,5 +1,8 @@
 #ifndef __LIGHT_H__
 #define __LIGHT_H__
+#include "types.h"
+// 灯光事件
+#define LIGHT_EVT_DEFAULT_MODE 0x0008 // 恢复为默认模式事件
 
 // 暖光 pwm脚
 #define GPIO_WARM P18
@@ -14,29 +17,91 @@
 #define GPIO_FAN P23
 
 // 亮度分级 0~100 翻 100倍
-
-#define TEMP_MIN 2500
+#define TOTAl_LEVEL 100
+// 亮度更改步长
+#define LIGHT_STEP 5
+#define TEMP_MIN 2700
 #define TEMP_MAX 6500
+// 色温更改步长
+#define TEMP_STEP 50
+
 
 // adc 电源监测脚
 #define GPIO_POWER P28
 
-// 灯光数据 色温, 亮度
+// 工作模式
+// 默认工作模式
+#define LIGHT_MODE_DEFAULT 0
+
+// 全亮模式(色温无效)
+#define LIGHT_MODE_FULL 1
+// 全亮模式下自动关闭时间 秒
+#define FULL_MODE_WAIT_TIME 60
+
+// 灯光控制命令起始码
+typedef enum {
+    // 下发命令
+    START_CODE_CMD = 0x6c,
+    // 响应命令
+    START_CODE_RES = 0xc6,
+    // 通知命令
+    START_CODE_NOTIFY = 0x66
+}light_cmd_start_code;
+
+// 灯光控制命令码
+typedef enum{
+    CMD_OPEN = 0x01,
+    CMD_CLOSE = 0x02,
+    CMD_QUERY = 0x03,
+    CMD_LIGTH = 0x04,
+    CMD_TEMP = 0x05,
+    CMD_FUN = 0x06,
+    CMD_MODE = 0x07,
+    CMD_CH = 0x08,
+    CMD_HZ = 0x09,
+}light_cmd_code;
+
+// 灯光数据 色温, 亮度, 工作模式, 风扇
 typedef struct {
+    bool open;
     int temp;
     int light;
+    int mode;
+    int fan;
 } light_data_t;
 
+typedef struct {
+    light_cmd_start_code startCode;
+    uint8_t len;
+    light_cmd_code cmd;
+    uint8_t sn;
+    uint8_t *data;
+} light_cmd_t;
+
 extern light_data_t light_data;
-extern int light_init(void);
+
+// 通知回调函数
+typedef void (*CallbackFunc)(uint8 *res, uint16 len);
+
+
+extern int light_init(int taskId);
 
 extern int light_ch_set(uint8_t ch, uint16_t val);
 
 // 计算冷暖光的亮度值,并且设置
 int comLightVal();
 
-extern int light_set(uint8_t val);
-extern int temp_set(int temp);
+extern uint16 light_set(uint8_t val, uint8_t sn, uint8 *res);
+extern uint16 temp_set(int temp, uint8_t sn, uint8 *res);
+extern uint16 open_light(uint8_t sn, uint8 *res);
+extern uint16 close_light(uint8_t sn, uint8 *res);
+extern uint16 change_light_mode (light_cmd_start_code mode, uint8_t sn, uint8 *res);
+extern uint16 parse_light_code(uint8* data, uint16 len, uint8 *res);
+
+extern void light_register_notify_callback(CallbackFunc callback);
+
+
+
 
 
 

+ 19 - 12
蓝牙灯光设计.md

@@ -32,16 +32,20 @@
 响应命令格式: `起始码` `长度` `命令码` `sn码` `数据1` `数据2`
 响应: 0xc6
 
+### 长度定义
+程序中定义的长度都是指数据长度,不包含起始码和长度本身
+数据全长应该是 `长度` + 2
+比如: `0x6c 0x06 0x01 0x01 0x01 0x01` 长度为 4, 数据全长为 6
 
 ### 下发命令定义
 | 起始码 | 命令码 | sn码 | 数据1 |
-| :----: | :----: | :--: | :--: | 
-| 1字节   | 1字节  | 1字节 | 基于命令确定 | 
+| :----: | :----: | :----: | :--: | :--: | 
+| 1字节   | 1字节 | 1字节 | 1字节 | 基于命令确定 | 
 
 ### 设备响应定义
-| 起始码 | 命令码 | 数据1 | 数据2 |
-| :----: | :----: | :--: | :--: |
-| 0xc6   | 0x05   | 0x01 | 0x01 |
+| 起始码 | 长度 | 命令码 | 数据1 | 数据2 |
+| :----: | :----: | :----: | :--: | :--: |
+| 0xc6   | 0x01 | 0x05   | 0x01 | 0x01 |
 
 ## 命令码
 > 所有命令码都是 1 字节, 值需要转换为hex  
@@ -51,12 +55,13 @@
 | 0x01   | 唤醒设备   | 0 |  | 将设备从待机模式下唤醒,进入工作状态 |
 | 0x02   | 关闭设备   | 0 |  | 将设备从工作状态下关闭,进入待机模式 |
 | 0x03   | 查询 | 1 | 命令码 | 查询命令码对应的值 |
-| 0x03   | 亮度调节   | 1 | 0 - 100 | 调节设备的亮度 |
-| 0x04   | 色温调节   | 0 | 2700 - 6500  | 调节色温 |
-| 0x05   | 风扇调节   | 1 | 0-100  | 调节风扇速度, 0关闭, 0以上为开启 |
-| 0x06   | 单路调节   | 2 | 通道,值  | 调节指定通道的pwm值,调试用 |
-| 0x06   | 频率调节   | 1 | 1-7 | 可选频率, 1-7, 1最高, 7最低 |
-| 0x07   | 线性变换亮度   | 2 | 亮度, 变换时间 | 亮度线性变换, 亮度值为0-100, 变换时间为0-255, 0为立即变换 |
+| 0x04   | 亮度调节   | 1 | 0 - 100 | 调节设备的亮度 |
+| 0x05   | 色温调节   | 0 | 2700 - 6500  | 调节色温 |
+| 0x06   | 风扇调节   | 1 | 0-100  | 调节风扇速度, 0关闭, 0以上为开启 |
+| 0x07  | 模式切换 | 1 | 0-3 | 0: 默认模式 1: 强亮模式(无法调节色温)  |
+| 0x08   | 单路调节   | 2 | 通道,值  | 调节指定通道的pwm值,调试用 |
+| 0x09   | 频率调节   | 1 | 1-7 | 可选频率, 1-7, 1最高, 7最低 |
+
 
 
 ### 命令码说明
@@ -68,7 +73,9 @@
 > 关闭设备后,需要再次唤醒设备才能进行操作
 
 #### 查询
-
+1. 查询设备状态
+> 亮度,色温,风扇速度,设备模式
+2. 查询设备版本号
 
 ### 示例
 #### 单路调节