当前位置:   article > 正文

智能家居1 -- 实现语音模块

智能家居1 -- 实现语音模块

项目整体框架: 

监听线程×4:

1. 语音监听线程:用于监听语音指令, 当有语音指令过来后, 通过消息队列的方式给消息处理线程发送指令

2. 网络监听线程:用于监听网络指令,当有网络指令过来后, 通过消息队列的方式给消息处理线程发送指令

3. 火灾检测线程:当存在煤气泄漏或者火灾闲情时, 发送警报指令给消息处理线程

4. 消息监听线程: 用于处理以上3个线程发过来的指令,并根据指令要求配置GPIO引脚状态,OLED屏显示、语音播报,还有人脸识别开门

统一的监听模块接口 -- control:

上述四个线程采用统一个对外接口接口,同时添加到监听链表中

统一的监听模块接口如下:

  1. struct control
  2. {
  3. char control_name[128]; //监听模块名称
  4. int (*init)(void); //初始化函数
  5. void (*final)(void);//结束释放函数
  6. void *(*get)(void *arg);//监听函数,如语音监听
  7. void *(*set)(void *arg); //设置函数,如语音播报
  8. struct control *next;
  9. };

//定义类似如下函数向这个统一的接口中添加

struct control *add_device_to_ctrl_list(struct control *phead, struct control *device);

统一的设备类接口

被控制的设备类也统一配置接口,同时添加到设备链表中。

统一的设备类接口如下:


 

  1. struct gdevice
  2. {
  3. char dev_name[128]; //设备名称
  4. int key; //key值,用于匹配控制指令的值
  5. int gpio_pin; //控制的gpio引脚
  6. int gpio_mode; //输入输出模式
  7. int gpio_status; //高低电平状态
  8. int check_face_status; //是否进行人脸检测状态
  9. int voice_set_status; //是否语音语音播报
  10. struct gdevice *next;
  11. };

主要代码: 


-------------------------------------------

Makefile

  1. CC  :=  aarch64-linux-gnu-gcc
  2. # SRC -- 存放所有的 .c 文件
  3. SRC :=  $(shell find src -name "*.c")
  4. # INC --  存放所有的 头文件 (包括自己写的 和 第三方)
  5. INC := ./inc \
  6.     ./3rd/usr/local/include \
  7.     ./3rd/usr/include \
  8.     ./3rd/usr/include/python3.10 \
  9.     ./3rd/usr/include/aarch64-linux-gnu/python3.10 \
  10.     ./3rd/usr/include/aarch64-linux-gnu
  11. #  把需要包含的 .c 文件,替换为.o 文件
  12. OBJ := $(subst src/,obj/,$(SRC:.c=.o))
  13. #  创建目标 , 并且指定存放位置
  14. TARGET  =  obj/smarthome
  15. #   -I./inc  -- 存放头文件路径
  16. CFLAGS := $(foreach item,$(INC),-I$(item))
  17. # -I 指定的 第三方 库文件路径
  18. LIBS_PATH := ./3rd/usr/local/lib \
  19.              ./3rd/lib/aarch64-linux-gnu \
  20.              ./3rd/usr/lib/aarch64-linux-gnu \
  21.              ./3rd/usr/lib/python3.10 \
  22.              
  23.              
  24. # -L ./3rd/usr/local/LIBS
  25. LDFLAGS := $(foreach item,$(LIBS_PATH),-L$(item))
  26. #  指定我们要链接的库
  27. LIBS := -lwiringPi -lpython3.10 -pthread -lexpat -lz -lcrypt 
  28. #  生成obj文件夹,里面包含源文件对应的.o文件
  29. obj/%.o:src/%.c
  30.     mkdir -p obj
  31.     $(CC) -o $@ -c $< $(CFLAGS)
  32. #  一来obj 下面的.o文件 编译
  33. $(TARGET) : $(OBJ)
  34.     $(CC) -o $@ $^ $(CFLAGS) $(LDFLAGS)    $(LIBS)
  35. compile : $(TARGET)
  36. clean: 
  37.     rm $(TARGET) obj &(OBJ) -rf
  38. debug:
  39.     echo $(CC) 
  40.     echo $(SRC)
  41.     echo $(INC)
  42.     echo $(OBJ)
  43.     echo $(TARGET)
  44.     echo $(CFLAGS)
  45.     echo $(LDFLAGS)
  46.     echo $(LIBS)
  47. .PHONY: clean compile debug 


============================

main.c 

  1. #include <stdio.h>
  2. #include <pthread.h>
  3. #include <stdlib.h>
  4. #include "control.h"
  5. #include "mq_queue.h"
  6. #include "voice_interface.h"
  7. #include "global.h"
  8. // msg_queue_create
  9. int main() {
  10. pthread_t thread_id;
  11. struct control *control_phead = NULL;
  12. struct control *pointer = NULL;
  13. ctrl_info_t *ctrl_info = NULL;
  14. ctrl_info = (ctrl_info_t *)malloc(sizeof(ctrl_info_t));
  15. ctrl_info->ctrl_phead = NULL;
  16. ctrl_info->mqd = -1;
  17. int node_num = 0; // 统计节点数
  18. // 创建消息队列
  19. ctrl_info->mqd = msg_queue_create();
  20. if(-1 == ctrl_info->mqd)// 创建消息队列失败
  21. {
  22. printf("%s|%s|%d, mqd= %d\n",__FILE__,__func__,__LINE__,ctrl_info->mqd);
  23. return -1;
  24. }
  25. ctrl_info->ctrl_phead = add_voice_to_ctrl_list(ctrl_info->ctrl_phead);
  26. //ctrl_info->ctrl_phead = add_socket_to_ctrl_list(ctrl_info->ctrl_phead);
  27. //ctrl_info->ctrl_phead = add_fire_to_ctrl_list(ctrl_info->ctrl_phead);
  28. pointer = ctrl_info->ctrl_phead;
  29. while(NULL!=pointer) // 对所有控制结构体初始化,并且统计节点数
  30. {
  31. if(NULL != pointer->init)
  32. {
  33. pointer->init();
  34. }
  35. pointer = pointer->next;
  36. node_num++; // 统计节点数
  37. }
  38. // 根据节点的总数 --> 创建对应数目的线程
  39. pthread_t *tid = (pthread_t *)malloc(sizeof(int) *node_num);
  40. pointer = ctrl_info->ctrl_phead;
  41. for(int i=0;i<node_num;++i)
  42. {
  43. if(NULL != pointer->get)
  44. pthread_create(&tid[i],NULL,(void *)pointer->get,(void *)ctrl_info); // 传入这个结构体参数,方便同时调用多组线程里面的API
  45. }
  46. for(int i=0;i<node_num;++i)
  47. {
  48. pthread_join(tid[i],NULL);
  49. }
  50. for(int i=0;i<node_num;++i)
  51. {
  52. if(NULL != pointer->final)
  53. pointer->final(); // 接打开的使用接口关闭
  54. pointer = pointer->next;
  55. }
  56. msq_queue_final(ctrl_info->mqd);
  57. }

实现语言控制模块-- voice_interface.c

  1. #if 0
  2. struct control
  3. {
  4. char control_name[128]; //监听模块名称
  5. int (*init)(void); //初始化函数
  6. void (*final)(void);//结束释放函数
  7. void *(*get)(void *arg);//监听函数,如语音监听
  8. void *(*set)(void *arg); //设置函数,如语音播报
  9. struct control *next;
  10. };
  11. #endif
  12. #include <pthread.h>
  13. #include <stdio.h>
  14. #include "voice_interface.h"
  15. #include "mq_queue.h"
  16. #include "uartTool.h"
  17. #include "global.h"
  18. static int serial_fd = -1; // static 这个 变量只在当前文件有效
  19. static int voice_init(void )
  20. {
  21. serial_fd = myserialOpen(SERIAL_DEV,BAUD); // 初始化并且打开串口
  22. printf("%s|%s|%d serial_fd = %d\n",__FILE__,__func__,__LINE__,serial_fd);
  23. return serial_fd;
  24. }
  25. static void voice_final(void)
  26. {
  27. if(-1 != serial_fd) // 打开串口成功
  28. {
  29. close(serial_fd); // 关闭我们打开的串口
  30. serial_fd = -1; // 复位
  31. }
  32. }
  33. // 接收语言指令
  34. static void* voice_get(void *arg)// mqd 通过arg 传参获得
  35. {
  36. int len = 0;
  37. mqd_t mqd = -1;
  38. ctrl_info_t * ctrl_info = NULL;
  39. if(NULL != arg)
  40. ctrl_info = (ctrl_info_t*)arg;
  41. unsigned char buffer[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; // 初始化 buffer
  42. if (-1 == serial_fd)
  43. {
  44. //打开串口
  45. serial_fd = voice_init();// 尝试打开串口
  46. if (-1 == serial_fd){ //还是打开失败
  47. printf("%s | %s | %d:open serial failed\n", __FILE__, __func__, __LINE__); // 三个宏的含义: 文件名 - main.c,函数名 - pget_voice ,行号 - 138
  48. pthread_exit(0);
  49. } // 串口打开失败 -->退出
  50. }
  51. mqd = ctrl_info->mqd; //为实现
  52. if((mqd_t)-1 == mqd)
  53. {
  54. pthread_exit(0);
  55. }
  56. pthread_detach(pthread_self());// 与父线程分离
  57. printf("%s thread start\n",__func__);
  58. while (1)
  59. {
  60. len = serialGetstring(serial_fd, buffer); // 通过串口获得语言输入
  61. printf("%s|%s|%d, 0x%x,0x%x,0x%x,0x%x,0x%x,0x%x\n",__FILE__,__func__,__LINE__,buffer[0],buffer[1],buffer[2],buffer[3],buffer[4],buffer[5]);
  62. printf("%s|%s|%d:len = %d\n",__FILE__,__func__,__LINE__,len);
  63. if (len > 0) // 判断是否 接到识别指令
  64. {
  65. if(buffer[0] == 0xAA && buffer[1] == 0x55
  66. &&buffer[4]==0x55 && buffer[5]==0xAA)
  67. {
  68. printf("%s|%s|%d, send: 0x%x,0x%x,0x%x,0x%x,0x%x,0x%x\n",__FILE__,__func__,__LINE__,buffer[0],buffer[1],buffer[2],buffer[3],buffer[4],buffer[5]);
  69. send_msg(mqd,buffer,len); // 注意获取len长度不能使用strlen() --> 0x00 会识别为截止位-->只能读取到三个字节(但不是我们实际的截止位(0x55 0xAA ))
  70. }
  71. memset(buffer,0,sizeof(buffer)); // 复位buffer
  72. }
  73. }
  74. pthread_exit(0);
  75. }
  76. static void* voice_set(void *arg)
  77. {
  78. }
  79. struct control voice_control ={
  80. .control_name = "voice",
  81. .init = voice_init,
  82. .final = voice_final,
  83. .get = voice_get,
  84. .set = voice_set,
  85. .next = NULL
  86. };
  87. struct control *add_voice_to_ctrl_list(struct control *phead)
  88. {
  89. //头插法实现 添加链表节点
  90. struct control *pnew = NULL;
  91. if(NULL == phead)
  92. {
  93. phead = &voice_control; // 直接传入我们的 voice_control
  94. }
  95. else// 头结点非空 - 链表有数据
  96. {
  97. voice_control.next = phead; //把新的节点的next指向头结点
  98. voice_control = *phead; // 让心节点成为头结点
  99. }
  100. return phead;
  101. };

编译: 

// 注意我们的Makefile 里面指定了使用交叉编译工具链:  aarch64-linux-gnu-gcc

所以我们生成的文件在×86上是没法运行的,需要scp 传送到arm-64的系统上,

比如我们的orangepi02

编译命令: 

make complie

或者

make

传送


scp obj/smarthome  orangepi@192.168.1.11:/home/orangepi

切换到我们的香橙派上: 

执行

sudo -E ./smarthome

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/567368
推荐阅读
相关标签
  

闽ICP备14008679号