发帖
0 0 0

安信可离线语音模组 VC-01、VC-02 系列教程 【二次开发篇】串口输出

WangChong
论坛元老

83

主题

863

回帖

1万

积分

论坛元老

积分
11848
VC系列 10 0 4 小时前
本帖最后由 WangChong 于 2025-5-23 03:43 编辑

简介
在上两篇文章中, 我们实现了GPIO输出, 和PWM输出(组件介绍在前文中已经介绍过, 可以从安信可离线语音模组 VC-01、VC-02 系列教程 【总览】进行跳转
), 在本章节我们将继续进行使用语音指令控制串口输出。


配置Example默认串口数据输出

1- 首先修改/home/vc02/Downloads/uni_hb_m_solution/unione_lite_app_hb_m/user/inc/user_config.h 中的USER_RUN_DEMO_SELECT USER_DEMO_UART

Snipaste_2025-05-23_03-24-35.png

这样的话默认运行的程序就是 /home/vc02/Downloads/uni_hb_m_solution/unione_lite_app_hb_m/user/src/examples/hb_uart.c

代码如下所示
  1. #include "user_uart.h"
  2. #include "user_event.h"
  3. #include "user_player.h"
  4. #include "user_config.h"

  5. #define TAG "hb_uart_voice"

  6. static uni_pthread_t g_uart_thread_id = 0;
  7. static bool g_uart_thread_running = false;

  8. // 串口发送线程函数
  9. static void _uart_send_task(void *args) {
  10.   char buf[6] = {1, 2, 3, 4, 5, 6};
  11.   int ret;

  12.   g_uart_thread_running = true;

  13.   while (g_uart_thread_running) {
  14.     LOGT(TAG, "UART send done, ret=%d", ret);
  15.     uni_msleep(2000); // 每隔2秒发送一次
  16.   }
  17. }

  18. // 创建发送线程
  19. static Result _create_uart_thread(void) {
  20.   if (g_uart_thread_running) {
  21.     return E_OK;
  22.   }

  23.   thread_param param;
  24.   uni_memset(&param, 0, sizeof(param));
  25.   param.stack_size = STACK_SMALL_SIZE;
  26.   param.priority = OS_PRIORITY_LOW;
  27.   uni_strncpy(param.task_name, "uart_voice", sizeof(param.task_name) - 1);

  28.   if (0 != uni_pthread_create(&g_uart_thread_id, &param, _uart_send_task, NULL)) {
  29.     return E_FAILED;
  30.   }

  31.   uni_pthread_detach(g_uart_thread_id);  // 自动资源回收
  32.   return E_OK;
  33. }

  34. // 停止发送线程
  35. static void _stop_uart_thread(void) {
  36.   if (!g_uart_thread_running) return;

  37.   g_uart_thread_running = false;

  38.   if (g_uart_thread_id != 0) {
  39.     uni_pthread_destroy(g_uart_thread_id);
  40.     g_uart_thread_id = 0;
  41.   }
  42. }

  43. // 语音唤醒命令回调
  44. static void _on_wakeup_cmd_cb(USER_EVENT_TYPE event, user_event_context_t *context) {
  45.   if (context == NULL) return;

  46.   event_goto_awakend_t *awake = &context->goto_awakend;

  47.   if (strcmp(awake->cmd, "wakeup_uni") == 0) {
  48.     _create_uart_thread();  // 启动串口线程
  49.     user_player_reply_list_random(awake->reply_files);  // 播放语音回复
  50.   }
  51. }

  52. // 自定义设置命令回调
  53. static void _custom_setting_cb(USER_EVENT_TYPE event, user_event_context_t *context) {
  54.   if (context == NULL) return;

  55.   event_custom_setting_t *setting = &context->custom_setting;

  56.   if (strcmp(setting->cmd, "TurnOn") == 0) {
  57.     _stop_uart_thread();  // 停止串口线程
  58.     user_player_reply_list_random(setting->reply_files);  // 播放语音回复
  59.   }
  60. }

  61. // 注册语音事件回调
  62. static void _register_event_callback(void) {
  63.   user_event_subscribe_event(USER_GOTO_AWAKENED, _on_wakeup_cmd_cb);
  64.   user_event_subscribe_event(USER_CUSTOM_SETTING, _custom_setting_cb);
  65. }

  66. // 主入口
  67. int hb_user_uart_voice_control(void) {
  68.   if (0 != user_uart_init(NULL)) {
  69.     LOGE(TAG, "UART init failed");
  70.     return -1;
  71.   }

  72.   _register_event_callback();
  73.   return 0;
  74. }
复制代码


其默认行为为创建线程定时发送数据, 然后使用LOG打印。但是它没办法调试。 因此我们做一点简单的修改, 当接受的数据的时候再发送出去。

  1. // 串口发送线程函数
  2. static void _uart_send_task(void *args) {
  3.   char buf[6] = {1, 2, 3, 4, 5, 6};
  4.   int ret;

  5.   g_uart_thread_running = true;

  6.   while (g_uart_thread_running) {
  7.     LOGT(TAG, "Voice triggered UART send [1, 2, 3, 4, 5, 6]");
  8.     ret = user_uart_send(buf, sizeof(buf));
  9.     LOGT(TAG, "UART send done, ret=%d", ret);
  10.     uni_msleep(2000); // 每隔2秒发送一次
  11.   }
  12. }
复制代码


2- 编译和烧录

Snipaste_2025-05-23_03-08-58.png

实验现象


串口以9600的波特率持续接受到数据 01,02,03,04,05,06 和程序中的数据对应。 正确无误
360截图20250523030935.png



发送测试

之后我们尝试向其发送数据, 查看VC02是否能正常转发回来。 (关闭HEX显示, 发送的数据和接受的一致)
360截图20250523031138.png


使用语音命令控制串口发送数据

1- 创建文件hb_user_uart_testing.c 在 /home/vc02/Downloads/uni_hb_m_solution/unione_lite_app_hb_m/user/src/examples 下
  1. #include "user_uart.h"
  2. #include "user_event.h"
  3. #include "user_player.h"
  4. #include "user_config.h"

  5. #define TAG "hb_uart_voice"

  6. static uni_pthread_t g_uart_thread_id = 0;
  7. static bool g_uart_thread_running = false;

  8. // 串口发送线程函数
  9. static void _uart_send_task(void *args) {
  10.   char buf[6] = {1, 2, 3, 4, 5, 6};
  11.   int ret;

  12.   g_uart_thread_running = true;

  13.   while (g_uart_thread_running) {
  14.     LOGT(TAG, "Voice triggered UART send [1, 2, 3, 4, 5, 6]");
  15.     ret = user_uart_send(buf, sizeof(buf));
  16.     LOGT(TAG, "UART send done, ret=%d", ret);
  17.     uni_msleep(2000); // 每隔2秒发送一次
  18.   }
  19. }

  20. // 创建发送线程
  21. static Result _create_uart_thread(void) {
  22.   if (g_uart_thread_running) {
  23.     return E_OK;
  24.   }

  25.   thread_param param;
  26.   uni_memset(&param, 0, sizeof(param));
  27.   param.stack_size = STACK_SMALL_SIZE;
  28.   param.priority = OS_PRIORITY_LOW;
  29.   uni_strncpy(param.task_name, "uart_voice", sizeof(param.task_name) - 1);

  30.   if (0 != uni_pthread_create(&g_uart_thread_id, &param, _uart_send_task, NULL)) {
  31.     return E_FAILED;
  32.   }

  33.   uni_pthread_detach(g_uart_thread_id);  // 自动资源回收
  34.   return E_OK;
  35. }

  36. // 停止发送线程
  37. static void _stop_uart_thread(void) {
  38.   if (!g_uart_thread_running) return;

  39.   g_uart_thread_running = false;

  40.   if (g_uart_thread_id != 0) {
  41.     uni_pthread_destroy(g_uart_thread_id);
  42.     g_uart_thread_id = 0;
  43.   }
  44. }

  45. // 语音唤醒命令回调
  46. static void _on_wakeup_cmd_cb(USER_EVENT_TYPE event, user_event_context_t *context) {
  47.   if (context == NULL) return;

  48.   event_goto_awakend_t *awake = &context->goto_awakend;

  49.   if (strcmp(awake->cmd, "wakeup_uni") == 0) {
  50.     _create_uart_thread();  // 启动串口线程
  51.     user_player_reply_list_random(awake->reply_files);  // 播放语音回复
  52.   }
  53. }

  54. // 自定义设置命令回调
  55. static void _custom_setting_cb(USER_EVENT_TYPE event, user_event_context_t *context) {
  56.   if (context == NULL) return;

  57.   event_custom_setting_t *setting = &context->custom_setting;

  58.   if (strcmp(setting->cmd, "TurnOn") == 0) {
  59.     _stop_uart_thread();  // 停止串口线程
  60.     user_player_reply_list_random(setting->reply_files);  // 播放语音回复
  61.   }
  62. }

  63. // 注册语音事件回调
  64. static void _register_event_callback(void) {
  65.   user_event_subscribe_event(USER_GOTO_AWAKENED, _on_wakeup_cmd_cb);
  66.   user_event_subscribe_event(USER_CUSTOM_SETTING, _custom_setting_cb);
  67. }

  68. // 主入口
  69. int hb_user_uart_voice_control(void) {
  70.   if (0 != user_uart_init(NULL)) {
  71.     LOGE(TAG, "UART init failed");
  72.     return -1;
  73.   }

  74.   _register_event_callback();
  75.   return 0;
  76. }
复制代码


输入上述代码, 其主要的业务逻辑就是通过唤醒命令来触发串口的定时输出,然后通过TrunOn的命令来关闭串口输出的线程


2- 添加编译支持 在 /home/vc02/Downloads/uni_hb_m_solution/unione_lite_app_hb_m/build/user/src/examples 下的 subdir.mk 添加对当前编译文件的引用。
Snipaste_2025-05-23_03-31-32.png

3- 修改/home/vc02/Downloads/uni_hb_m_solution/unione_lite_app_hb_m/user/inc/user_config.h 文件, 增加对应的demo宏支持
Snipaste_2025-05-23_03-32-32.png

4 - 修改/home/vc02/Downloads/uni_hb_m_solution/unione_lite_app_hb_m/user/src/user_main.c 增加对上述自定义宏的支持
Snipaste_2025-05-23_03-34-01.png

5- 编译并且烧录固件
Snipaste_2025-05-23_03-08-58.png


实验现象

烧录完成之后, 串口默认不输出任何数据


Snipaste_2025-05-23_03-37-27.png

当识别到语音命令“你好小美” 之后将开始打印 01, 02, 03, 04, 05

360截图20250523033926.png

当识别到语音命令“打开灯光” 的时候将删除串口发送进程, 串口停止输出

360截图20250523034033.png

如上图所示, 此时的串口处于打开状态,但是没有输出数据。



附件:
上传的附件: uni_app_release_update.zip (882.64 KB, 下载次数: 0)





──── 0人觉得很赞 ────

使用道具 举报

您需要登录后才可以回帖 立即登录
高级模式
返回
统计信息
  • 会员数: 28792 个
  • 话题数: 41609 篇