Files
banban/ql-application/threadx/interface/custom_app/ws_talkingq.c
T
chenjintao 3e463f57b6 Add verified WSS transport for legacy boards
Configure the default WSS endpoint and ISRG Root X2 trust anchor, preserve device authentication, enforce TLS and WebSocket handshake validation, and handle partial I/O and connection cleanup. Add host tests, server probes, package verification and integration documentation. Backend 443 /ws still returns HTTP 404; on-device validation remains pending.
2026-09-16 11:23:33 +08:00

1223 lines
31 KiB
C

#include <stdlib.h>
#include <string.h>
#include <librws.h>
#include "cJSON.h"
#include "app_inc.h"
#include "talkingq_wss.h"
#include "ql_rtos.h"
#include "ql_application.h"
#include "ql_audio.h"
#include "ql_gpio.h"
#include "sockets.h"
#include "ql_uart.h"
#include "ql_func.h"
#include "netdb.h"
#include "ql_sim.h"
#include "ql_dev.h"
#include "ql_wtd.h"
#include "ql_fs.h"
#include "ql_fota.h"
#include "ql_sim.h"
#include "ql_ping.h"
#include "ql_nw.h"
#include "ql_atcmd.h"
#include "layer3.h"
#include "ql_uart.h"
#include "hal_api.h"
#include "fvad.h"
#define PCM_FRAME_LENGTH 1152
static int g_sim_ready = 0;
int g_mic_working = 0;
static ql_queue_t player_queue = NULL;
ql_sem_t sender_sem = NULL;
ql_sem_t vad_check_sem = NULL;
//ql_sem_t check_connect_sem = NULL;
struct ringbuffer *ring_sender = NULL;
static uint8_t sender_buf[PCM_FRAME_LENGTH]={0};
static int16 mp3_buff[2*PCM_FRAME_LENGTH];
static int16 *ppbuff=mp3_buff;
static int onmessage_status = -1;
volatile int tts_playing = 0;
unsigned char wss_connect_start = 0;
static int player_mq_couter = 0;
static unsigned char audio_in_task_cancel = 0;
static unsigned char xiaoice_wss_connect_status = 0;
static unsigned char wss_connect_counter = 0;
int fota_start = 0;
int mqtt_task_run_flag = 0;
extern int sub_topic_status;
extern int mqtt_task_run_flag;
static unsigned char shine_init = 0;
extern int fisrt_net_start;
extern u8 power_down_status;
int audio_packet_type = 0;
typedef struct ice_work_item_s
{
int type; //0-tts_out 1-session_continue 2-session_end 3-error
int error;
int del_url;
void *data;
}ice_work_item;
static uint32_t gSequenceNumber = 0;
#define DEVICE_ID_SIZE 22
#define SESSION_ID_SIZE 33
typedef struct {
uint32_t sequenceNumber;
uint8_t packetType;
uint32_t dataSize;
uint8_t *data;
} AudioPacket;
char talkingq_host[64]={0};
char deviceId[DEVICE_ID_SIZE] = {0};
char sessionId[SESSION_ID_SIZE] = {0};
char serialNumber[SESSION_ID_SIZE] = {0};
struct librws_app
{
int init;
rws_socket *socket;
};
typedef struct librws_app* librws_app_t;
static struct librws_app _app = {0};
static librws_app_t app = &_app;
static ql_sem_t ws_connect_sem = NULL;
extern int fota_start;
static int tts_play_status = 0;//1播放中 2播放完成
int xiaoice_get_tts_play_flag(void)
{
return tts_play_status;
}
int xiaoice_get_play_counter(void)
{
return player_mq_couter;
}
int tts_playing_ret(void)
{
return tts_playing;
}
unsigned char get_xiaoice_connected_status(void)
{
return xiaoice_wss_connect_status;
}
static void onopen(rws_socket socket)
{
if (socket != app->socket || !app->init) return;
wss_connect_counter=0;
LOG_D("websocket connected. ");
if(ws_connect_sem){
ql_rtos_semaphore_release(ws_connect_sem);
}
sendAuthentication();
}
static void onclose(rws_socket socket)
{
rws_error error = rws_socket_get_error(socket);
int error_code = rws_error_get_code(error);
if(error)
{
LOG_E("websocket disconnect, error: %i, %s ", error_code, rws_error_get_description(error));
}
else
{
LOG_D("websocket disconnect! ");
}
if (socket != app->socket) return;
xiaoice_wss_connect_status = 0;
app->socket = NULL;
/* The worker owns and releases this socket after the callback. */
RLOGD("onclose end\r\n");
app->init = 0;
if (ws_connect_sem) ql_rtos_semaphore_release(ws_connect_sem);
hal_led_b_off();
}
// 安全修复:添加最大长度限制防止越界
static void remove_underscores(char *str) {
if (str == NULL) return; // 安全检测
char *src = str; // 源指针(读位置)
char *dst = str; // 目标指针(写位置)
size_t max_len = 128; // 最大处理长度限制
size_t processed = 0;
while (*src && processed < max_len) {
if (*src != '_') {
*dst = *src; // 非下划线字符:复制并移动目标指针
dst++;
}
src++; // 始终移动源指针
processed++;
}
*dst = '\0'; // 添加字符串结束符
}
void add_file_to_play_player_queue(char *path, int type)
{
ice_work_item *item = NULL;
// 安全修复:检查path参数有效性
if(path == NULL || strlen(path) == 0){
printf("add_file_to_play_player_queue: invalid path\n");
return;
}
item = malloc(sizeof(ice_work_item));
if(item == NULL){
printf("add_file_to_play_player_queue: malloc item failed\n");
return;
}
item->data = malloc(strlen(path)+1);
if(item->data == NULL){
printf("add_file_to_play_player_queue: malloc data failed\n");
free(item);
return;
}
item->type = type;
// 安全修复:使用strncpy替代sprintf防止溢出
snprintf((char *)item->data, strlen(path)+1, "%s", path);
printf("play file:%s\r\n", (char *)item->data);
if (ql_rtos_queue_release(player_queue, sizeof(item), &item, 0xFFFFFFFF) != 0)
{
printf("player_mq rt_mq_send ERR\n");
free(item->data);
item->data=NULL;
free(item);
item = NULL;
}else{
player_mq_couter++;
printf("player_mq_couter:%d\r\n", player_mq_couter);
}
}
//如果录音后 超过10s 还没有收到信息 提示本轮对话结束
static void onmessage_text(rws_socket socket, const char *text, const unsigned int len)
{
// printf("message(txt), %d(byte): %s ", len, text);
char url_buff[128]={0x00};
char url_buff_bak[128]={0x00};
if(strstr(text, "_START")){
printf("TTS_START\r\n");
//语音交互音频开始
tts_playing = 1;
tts_play_status = 1;
// aofei_mp3_player_init();
}else if(strstr(text, "TTS_END")){
printf("TTS_END\r\n");
//语音交互音频结束
// tts_playing = 0;
tts_play_status = 2;
}else if(strstr(text, "END_ACK")||strstr(text, "START_ACK")){
}else{//TTS_URL:audio_url
wss_connect_start = 0;
char *ret_str1= strstr(text, "http:");
char *ret_str2= strstr(text, ".mp3");
int len = 0;
if(ret_str1!=NULL && ret_str2!=NULL){
len = strlen(ret_str1)-strlen(ret_str2);
strncpy(url_buff, ret_str1, len);
sprintf(url_buff+len, "%s", ".mp3");
memcpy(url_buff_bak, url_buff, strlen(url_buff)+1);
//先把文件下载存入到60k的全局数组里面 然后启动mp3播放
remove_underscores(url_buff_bak);
//TARGET_DEVICE_REGISTERED_URL:{sound_url}
if(strstr(text, "PROMPT_SOUND_URL")==NULL&&strstr(text, "TARGET_DEVICE_REGISTERED_URL")==NULL \
&&strstr(text, "NFC_SOUND_URL")==NULL){//提示音不需要判断sessionID
if(strstr(url_buff_bak,sessionId)==NULL){
return;
}
}else{
tts_play_status = 2;
}
if(strstr(url_buff, "tts_error")!=NULL || strstr(url_buff, "message_ok")!=NULL){
printf("tts_error or message_ok\n");
vad_set_speech_timeout_flag(1);
}
//如果打断了 就不去做播放
if(tts_playing==2) return;
add_file_to_play_player_queue(url_buff, 0);
}
}
}
static void onmessage_bin(rws_socket socket, const void * data, const unsigned int len)
{
LOG_D("message(bin): %d ", len);
return;
}
int talkingq_ws_connect()
{
int result;
int ret = -1;
char wss_path[TALKINGQ_PATH_CAPACITY]={0};
char ws_talkingq_url[TALKINGQ_URL_CAPACITY]={0};
int port;
// char session_id[56]={0};
// char ip_name[56]={0};
RLOGD("talkingq_ws_connect 1\r\n");
if(app->init == 1)
{
LOG_E("the websocket connection has been opened. ");
return (-1);
}
RLOGD("talkingq_ws_connect 2\r\n");
app->init = 1;//防止重复进入等待
xiaoice_wss_connect_status = 1;
if(ws_connect_sem==NULL){
ql_rtos_semaphore_create(&ws_connect_sem, 0);
}
if (ws_connect_sem) while (ql_rtos_semaphore_wait(ws_connect_sem, 0) == 0) {}
hal_os_get_talkingq_url(ws_talkingq_url);
printf("ws_talkingq_url %s",ws_talkingq_url);
if (talkingq_parse_wss_url(ws_talkingq_url, talkingq_host, sizeof(talkingq_host),
&port, wss_path, sizeof(wss_path)) != 0) {
LOG_E("Invalid WSS endpoint; cleartext fallback is disabled\n");
app->init = 0;
xiaoice_wss_connect_status = 0;
return -1;
}
app->socket = rws_socket_create();
if(app->socket == NULL)
{
app->init = 0;
xiaoice_wss_connect_status = 0;
LOG_E("librws socket create failed. ");
return (-1);
}
printf("talkingq host %s , port %d",talkingq_host,port);
/* Server-authenticated TLS; application authentication stays in onopen. */
rws_socket_set_url(app->socket, "wss", talkingq_host, port, wss_path);
rws_socket_set_server_cert(app->socket, talkingq_root_ca, talkingq_root_ca_size);
rws_socket_set_on_connected(app->socket, &onopen);
rws_socket_set_on_disconnected(app->socket, &onclose);
rws_socket_set_on_received_text(app->socket, &onmessage_text);
rws_socket_set_on_received_bin(app->socket, &onmessage_bin);
//rws_socket_set_custom_mode(app->socket);
ret = rws_socket_connect(app->socket);
if(ret == rws_false)
{
rws_socket_disconnect_and_release(app->socket);
app->socket = NULL;
app->init = 0;
LOG_E("rws_socket_connect failed. ");
ret = -1;
}
else
{
ret = -1;
LOG_D("try rws_socket_connect. ");
if(ws_connect_sem){
result = ql_rtos_semaphore_wait(ws_connect_sem, 45000);
if(result == 0 && app->init && app->socket && rws_socket_is_connected(app->socket)){
LOG_I("ws connect okay");
wss_connect_counter = 0;
ret = 0;
}else{
/* Stop the pending worker before allowing a new attempt. */
if (app->socket) rws_socket_disconnect_and_release(app->socket);
app->socket = NULL;
app->init = 0;
LOG_E("ws connect failed or wait timeout");
wss_connect_counter++;
}
}else{
app->init = 0;
LOG_E("ws_connect_sem null");
}
}
if(ret == 0 && app->init == 1){
xiaoice_wss_connect_status = 2;
hal_led_b_on();
}
else{
xiaoice_wss_connect_status = 0;
hal_led_b_off();
}
return ret;
}
int talkingq_ws_disconnect()
{
audio_packet_type = 0;
g_mic_working = 0;
hal_led_b_off();
if(app->init == 0)
{
LOG_W("no websocket connection. ");
return (-1);
}
rws_socket_disconnect_and_release(app->socket);
LOG_I("try disconnect websocket connection. ");
app->socket = NULL;
app->init = 0;
return 0;
}
int talkingq_ws_send_text(char *senddata)
{
if(app->init == 0)
{
LOG_W("no websocket connection. ");
return (-1);
}
LOG_D("talkingq_wss_send_text string = %s, len = %d", senddata, strlen(senddata));
rws_socket_send_text(app->socket, senddata);
return 0;
}
int talkingq_ws_send_bin(uint8_t *senddata, int len)
{
if(app->init == 0)
{
LOG_W("no websocket connection. ");
return (-1);
}
// LOG_D("talkingq_wss_send_bin, len = %d", len);
rws_socket_send_bin(app->socket, senddata, len,2,rws_true);
return 0;
}
void sendRecordingAudioPacket(AudioPacket *packet);
/*串口AT指令做设置*/
void generateDeviceId(void) {
char temp_DeviceId[32];
memset(temp_DeviceId, 0, sizeof(temp_DeviceId));
hal_os_get_device_id(temp_DeviceId);
snprintf(deviceId, strlen(temp_DeviceId)+1, temp_DeviceId);
printf("temp_DeviceId:%s\n",temp_DeviceId);
}
void generateSerialNumber(void) {
char temp_SerialNumber[32];
memset(temp_SerialNumber, 0, sizeof(temp_SerialNumber));
hal_os_get_device_sn(temp_SerialNumber);
snprintf(serialNumber, strlen(temp_SerialNumber)+1, temp_SerialNumber);
printf("temp_SerialNumber:%s\n",temp_SerialNumber);
}
#if 0
#define INT_MAX 2147483647
#define INT_MIN (-1 - INT_MAX)
void int_to_digits_str(int num, char *buf, int buf_size) {
if (buf_size < 2) { // 至少需要存储空字符
if (buf_size > 0) buf[0] = '\0';
return;
}
unsigned int n; // 用于存储绝对值
// 处理INT_MIN特殊情况(避免溢出)
if (num == INT_MIN) {
n = (unsigned int)INT_MAX + 1; // 2147483648
}
// 处理其他负数
else if (num < 0) {
n = (unsigned int)(-num); // 取绝对值
}
// 处理正数和0
else {
n = (unsigned int)num;
}
// 处理0特殊情况
if (n == 0) {
buf[0] = '0';
buf[1] = '\0';
return;
}
// 临时存储数字(逆序)
char temp[11]; // 足够存储10位数字
int temp_index = 0;
// 提取数字(逆序)
while (n > 0) {
temp[temp_index++] = '0' + (n % 10);
n /= 10;
}
temp[temp_index] = '\0';
// 计算所需空间:数字位数 + 结束符
int required_size = temp_index + 1;
// 检查缓冲区大小
if (buf_size < required_size) {
buf[0] = '\0';
return;
}
// 反转数字到缓冲区
for (int i = 0; i < temp_index; i++) {
buf[i] = temp[temp_index - 1 - i];
}
buf[temp_index] = '\0'; // 终止字符串
}
void generateSessionId(void) {
static int rand_num = 0;
int index = 0;
if(rand_num==0){
rand_num = hal_os_get_rand();
}else{
rand_num++;
}
if(rand_num<10000000) rand_num += 10000000;
if(rand_num>99999999) rand_num = 10000000;
char buffer[12]; // 足够存储任何int值
int_to_digits_str(rand_num, buffer, sizeof(buffer));
for(index=0;index<8;index++){
sessionId[index]=buffer[index];
}
const char charset[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const int charset_size = sizeof(charset) - 1;
int i = 0;
for(i = 8; i < 32; i++) {
index= (hal_os_get_rand()+i) % charset_size;
sessionId[i] = charset[index];
}
sessionId[32] = '\0';
RLOGE("sessionId:%s\n", sessionId);
}
#endif
/**
* @brief 基于本地时间生成32字节sessionId
*
* 格式说明(32字节):
* [0-1] : 年份后两位 (00-99)
* [2-3] : 月份 (01-12)
* [4-5] : 日期 (01-31)
* [6-7] : 小时 (00-23)
* [8-9] : 分钟 (00-59)
* [10-11]: 秒 (00-59)
* [12-13]: 毫秒级计数器(同一秒内递增,保证唯一性)
* [14-31]: 随机字符(数字+大写字母)
*
* 示例:26060914305201A3B7C9D2E4F6G8H0J1K
*/
void generateSessionId(void)
{
static unsigned short seq_counter = 0; // 同一秒内的递增计数器
char time_buf[6];
int i = 0;
int index = 0;
// 获取当前本地时间
hal_os_get_localtime(time_buf);
// 同一秒内计数器递增,超过99则回绕
seq_counter++;
if (seq_counter > 99) {
seq_counter = 0;
}
// ========== 前14位:时间戳部分(基于本地时间)==========
// 将时间数据转为ASCII字符存入sessionId
// 年份 (2位) - buf[0] 是年份后两位,如26表示2026年
sessionId[0] = '0' + (time_buf[0] / 10); // 年十位
sessionId[1] = '0' + (time_buf[0] % 10); // 年个位
// 月份 (2位)
sessionId[2] = '0' + (time_buf[1] / 10); // 月十位
sessionId[3] = '0' + (time_buf[1] % 10); // 月个位
// 日期 (2位)
sessionId[4] = '0' + (time_buf[2] / 10); // 日十位
sessionId[5] = '0' + (time_buf[2] % 10); // 日个位
// 小时 (2位)
sessionId[6] = '0' + (time_buf[3] / 10); // 时十位
sessionId[7] = '0' + (time_buf[3] % 10); // 时个位
// 分钟 (2位)
sessionId[8] = '0' + (time_buf[4] / 10); // 分十位
sessionId[9] = '0' + (time_buf[4] % 10); // 分个位
// 秒 (2位)
sessionId[10] = '0' + (time_buf[5] / 10); // 秒十位
sessionId[11] = '0' + (time_buf[5] % 10); // 秒个位
// 同一秒内序列号 (2位,00-99)
sessionId[12] = '0' + (seq_counter / 10);
sessionId[13] = '0' + (seq_counter % 10);
// ========== 后18位:随机字符部分 ==========
const char charset[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const int charset_size = sizeof(charset) - 1;
for (i = 14; i < 32; i++) {
index = (hal_os_get_rand() + i) % charset_size;
sessionId[i] = charset[index];
}
sessionId[32] = '\0';
RLOGE("sessionId:%s\n", sessionId);
}
#if 1
void sendStartPacket(void) {
generateSessionId();
generateDeviceId();
gSequenceNumber = 0;
AudioPacket startPacket = {0};
startPacket.sequenceNumber = gSequenceNumber++;
startPacket.packetType = 1;
startPacket.dataSize = 0;
sendRecordingAudioPacket(&startPacket);
// rt_kprintf("sendStartPacket, seq=%u\r\n", startPacket.sequenceNumber);
}
void sendEndPacket(void) {
AudioPacket endPacket = {0};
endPacket.sequenceNumber = gSequenceNumber++;
endPacket.packetType = 2;
endPacket.dataSize = 0;
sendRecordingAudioPacket(&endPacket);
// rt_kprintf("sendEndPacket, seq=%u\r\n", endPacket.sequenceNumber);
}
void sendAudioPacket(const uint8_t* data, size_t dataSize) {
AudioPacket audioPacket = {0};
audioPacket.sequenceNumber = gSequenceNumber++;
audioPacket.packetType = audio_packet_type;
//audioPacket.packetType = 4;
audioPacket.dataSize = dataSize;
if(audioPacket.dataSize>0){
audioPacket.data = malloc(audioPacket.dataSize);
if(audioPacket.data==NULL){
RLOGE("audioPacket.data NULL");
return;
}
memcpy(audioPacket.data, data, dataSize);
}
sendRecordingAudioPacket(&audioPacket);
// rt_kprintf("sendAudioPacket, seq=%u\r\n", audioPacket.sequenceNumber);
if(audioPacket.data){
free(audioPacket.data);
}
}
void sendInterruptPacket(void) {
AudioPacket interruptPacket = {0};
interruptPacket.sequenceNumber = gSequenceNumber++;
interruptPacket.packetType = 3;
interruptPacket.dataSize = 0;
sendRecordingAudioPacket(&interruptPacket);
}
void sendAuthentication()
{
cJSON *sendAuth = cJSON_CreateObject();
generateDeviceId();
generateSerialNumber();
cJSON_AddItemToObject(sendAuth,"device_id",cJSON_CreateString(deviceId));
cJSON_AddItemToObject(sendAuth,"serial_number",cJSON_CreateString(serialNumber));
// cJSON_AddItemToObject(sendAuth,"deviceId",cJSON_CreateString(deviceId));
//cJSON_AddItemToObject(sendAuth,"serialNumber",cJSON_CreateString(serialNumber));
char *jstr = cJSON_PrintUnformatted(sendAuth);
if(jstr){
talkingq_ws_send_text(jstr);
free(jstr);
}
cJSON_Delete(sendAuth);
}
void sendRecordingAudioPacket(AudioPacket *packet) {
uint8_t *sendBuffer=NULL;
sendBuffer = malloc(DEVICE_ID_SIZE + SESSION_ID_SIZE + sizeof(uint32_t) * 2 + sizeof(uint32_t) + packet->dataSize);
if(sendBuffer==NULL){
RLOGE("sendBuffer NULL");
return;
}
size_t headerSize = DEVICE_ID_SIZE + SESSION_ID_SIZE + sizeof(packet->sequenceNumber)
+ sizeof(packet->packetType) + sizeof(packet->dataSize);
size_t totalSize = headerSize + packet->dataSize;
size_t offset = 0;
memcpy(sendBuffer + offset, deviceId, DEVICE_ID_SIZE);
offset += DEVICE_ID_SIZE;
memcpy(sendBuffer + offset, sessionId, SESSION_ID_SIZE);
offset += SESSION_ID_SIZE;
memcpy(sendBuffer + offset, &packet->sequenceNumber, sizeof(packet->sequenceNumber));
offset += sizeof(packet->sequenceNumber);
memcpy(sendBuffer + offset, &packet->packetType, sizeof(packet->packetType));
offset += sizeof(packet->packetType);
memcpy(sendBuffer + offset, &packet->dataSize, sizeof(packet->dataSize));
offset += sizeof(packet->dataSize);
memcpy(sendBuffer + offset, packet->data, packet->dataSize);
// recorder->save_len = totalSize;
// memcpy(recorder->save_buf, (void *)sendBuffer, recorder->save_len);
//record_msg_send(recorder, RECORD_MSG_DATA, recorder->save_len);
talkingq_ws_send_bin((uint8_t*)sendBuffer, totalSize);
free(sendBuffer);
// rt_kprintf("sendRecordingAudioPacket - Device ID: %s, Session ID: %s\n", deviceId, sessionId);
}
#endif
extern const char *mp3_file[];
int app_play_tips_file(int index)
{
int file_valid_check = 0;
printf("xiaoice_play_index:%d\n", index);
//正在录音过程中不允许播放
// if(xiaoice_get_tts_play_status() || get_start_mic_flag()==1) return -1;
if(power_down_status!=1){
if(vad_audio_is_recording() || tts_playing!=0 || fota_start==1 || power_down_status==2 || get_sleep_mode()==1) return -1;
}
char filename_buff[64]={0x00};
sprintf(filename_buff, "U:/%s", mp3_file[index]);
if(index==0) {
file_valid_check = 0;
}else{
if(ql_access(filename_buff, 0)==0){
file_valid_check = 1;
}else{
file_valid_check = 2;
}
}
if(file_valid_check!=2){
if(index==18){
add_file_to_play_player_queue(filename_buff, 2);
}else{
add_file_to_play_player_queue(filename_buff, 0);
}
}else{
return -2;
}
return 0;
}
void app_player_thread(void *arvg)
{
ice_work_item *player_item=NULL;
while(1){
ql_rtos_queue_wait(player_queue, (u8*)(&player_item), sizeof(ice_work_item *), 0xFFFFFFFF);
if(player_item!=NULL){
// if(player_item->type==0){//local mp3 file
if(player_item->data){
// printf("xiaoice player! %s\n",player_item->data);
app_set_app_operation(1);
aofei_mp3_player(player_item->data);
app_set_app_operation(1);
if(player_mq_couter>0) player_mq_couter--;
if(player_mq_couter==0){
printf("player_mq_couter==0\r\n");
if(tts_play_status==2){
tts_play_status = 0;
if(tts_playing!=2){
vad_tts_playback_finished();
tts_playing = 0;
}
printf("tts_play_status0==0\r\n");
}
if(tts_playing==2){
tts_play_status = 0;
printf("tts_play_status1==0\r\n");
}
if(player_item->type==1){
printf("vad_leave_message\r\n");
vad_leave_message();
}else if(player_item->type==2){
// vad_tts_playback_finished();
vad_start_func();
}
}
// }
}
if(player_item->data){
free(player_item->data);
player_item->data = NULL;
}
free(player_item);
player_item = NULL;
}
}
}
int tts_play_interrupt_func(void)
{
//如果在vad状态,则直接返回
if(vad_audio_is_recording()||g_mic_working) return -1;
g_mic_working = 1;//等待转为vad状态就清0 或者拾音超时 或者ws没有正常
xiaoice_close_all_audio();
talkingq_ws_connect();
if(get_xiaoice_connected_status()!=2) {
g_mic_working = 0;
return -2;
}
if(tts_playing==2){
printf("vad_start_func0\n");
vad_start_func();//如果是打断就不播报
}else{
app_play_tips_file(18);
}
}
void stop_player_func(void)
{
vad_audio_stop_record();
xiaoice_close_all_audio();
tts_playing = 0;
}
void vad_start_func(void)
{
set_state(STATE_IDLE);
//ql_rtos_task_sleep_ms(50);
vad_audio_trigger_record();
}
static void check_connect_thread_entry(void *parameter)
{
uint32 time_counter = 0;
uint32 cfun_recorder = 0;
int sim_status = 0;
int per_socket_status = 0;
int ret = 0;
QL_NW_REG_STATUS_INFO_T ql_nw_reg_status;
while(1)
{
#if 0
if(app_get_data_call_net_status() && per_net_status==0){
//app_play_tips_file(1);
per_net_status = 1;
hal_led_b_on();
}else if(!app_get_data_call_net_status() && per_net_status==1){
app_play_tips_file(2);
per_net_status = 0;
hal_led_b_off();
}
#endif
//判断网络状态
//a.如果连续3次连不上后台
//b.查询注网状态 正常则进行重新拨号
//c.注网状态异常,则cfun0/1一次,查询卡状态,如果卡状态异常,则后续不再做cfun0/1操作
//d.卡状态正常,注册网络异常,则每2个小时cfun0/1一次
if(wss_connect_counter>3 && fisrt_net_start==0){
wss_connect_counter = 0;
ql_sim_get_card_status(&sim_status);
if(sim_status == QL_SIM_STATUS_READY){
g_sim_ready = 1;
}else{
g_sim_ready = 0;
}
ql_nw_get_reg_status(&ql_nw_reg_status);
printf("net.state:%d, g_sim_ready:%d\n", ql_nw_reg_status.data_reg.state, g_sim_ready);
if(ql_nw_reg_status.data_reg.state==1 || ql_nw_reg_status.data_reg.state==5){
if(g_sim_ready){
//触发拨号
printf("app_data_recall\r\n");
app_data_recall();
}
}else{
//两个小时 CFUN01一次
if(cfun_recorder==0 || ql_rtos_get_systicks()-cfun_recorder>2*60*60000/5)
{
if(g_sim_ready){
printf("ql_dev_set_modem_fun\r\n");
ql_dev_set_modem_fun(QL_DEV_MODEM_MIN_FUN,0);
ql_rtos_task_sleep_ms(10000);
ql_dev_set_modem_fun(QL_DEV_MODEM_FULL_FUN,0);
ql_rtos_task_sleep_ms(10000);
}
cfun_recorder = ql_rtos_get_systicks();
}
}
}
#if 0
//if(app_get_app_sleep_flag()!=0){
//判断网络状态ok在进行
if(!get_rws_socket_thread_status()&&app_get_data_call_net_status()&&fota_start!=1&&sub_topic_status==1){
if(xiaoice_wss_connect_status!=1){//如果没有正在连接状态
hal_led_b_off();
wss_connect_counter++;
talkingq_ws_disconnect();
printf("reconnect wss socket:%d\r\n", wss_connect_counter);
ret = talkingq_ws_connect();
if(ret!=0){
/*播报网络异常*/
per_socket_status = 0;
app_play_tips_file(2);
hal_led_b_off();
}
}
}else {//如果网络正常
if(app_get_data_call_net_status()){
if(per_socket_status==0){
per_socket_status = 1;
app_play_tips_file(11);
hal_led_b_on();
}
}
}
//}else{
// ql_rtos_semaphore_wait(check_connect_sem, 0xFFFFFFFF);
//}
#endif
#if 1
//如果后台10s以上没有反馈 则进行重连
if(wss_connect_start==1){
wss_connect_start = 2;
time_counter = ql_rtos_get_systicks();
}else if(wss_connect_start==2 && (ql_rtos_get_systicks()-time_counter)>5000/5){
wss_connect_start = 0;
tts_playing = 0;
audio_packet_type = 0;
talkingq_ws_disconnect();
printf("tts_error_set_speech1\n");
vad_set_speech_timeout_flag(1);
app_play_tips_file(9);
//talkingq_ws_send_text("REQUEST_PROMPT_SOUND:tts_error");
//改为播放本地音频 听不清 请再说一遍
}
#endif
ql_rtos_task_sleep_ms(3000);
}
}
#if 1
void xiaoice_clear_player_mq(void)
{
ice_work_item *player_item=NULL;
while(ql_rtos_queue_wait(player_queue, (u8*)(&player_item), sizeof(ice_work_item *), 0)==0){
if(player_item!=NULL){
if(player_item->data){
free(player_item->data);
player_item->data = NULL;
}
free(player_item);
player_item = NULL;
}
}
player_mq_couter=0;
}
void xiaoice_close_all_audio(void)
{
int timeout=0;
app_codec_pa_close();//防止突然关掉的滋滋声
#if 1
if(tts_playing){
tts_playing = 2;
sendInterruptPacket();
}
if(player_mq_couter>0){
xiaoice_clear_player_mq();
timeout = hal_os_up_time_ms();
while(tts_play_status&&tts_playing!=2){
ql_rtos_task_sleep_ms(20);
if(hal_os_up_time_ms()-timeout>10000) break;
}
}
#endif
}
#endif
void app_sender_thread(void *arvg)
{
int index = 0, cnt = 0;
uint8 *data_out = NULL;
shine_t sh_shine;
shine_config_t sh_config;
int left = 0;
unsigned char value = 0;
while(1)
{
ql_rtos_semaphore_wait(sender_sem, 0xFFFFFFFF);
sender_loop:
if(!app_get_data_call_net_status()) continue;
if(shine_init==0){
shine_set_config_mpeg_defaults(&(sh_config.mpeg)); //--init sh_config struct
sh_config.wave.channels=PCM_MONO; //PCM_STEREO;//PCM_MONO;
sh_config.wave.samplerate=16000;//valid samplerates: 44100,48000,32000,22050,24000,16000,11025,12000,8000
sh_config.mpeg.copyright=1;
sh_config.mpeg.original=1;
sh_config.mpeg.mode=MONO;//STEREO;//MONO;
sh_config.mpeg.bitr=16; //64kbps for 16k //32kbps for 8k samplerate
sh_shine=shine_initialise(&sh_config);
shine_init = 1;
talkingq_ws_connect();
sendStartPacket();
}
left = rb_get_space_used(ring_sender);
if(left<PCM_FRAME_LENGTH){
if(!vad_audio_is_recording()){//录音结束
hal_os_rb_read(ring_sender,sender_buf, left, 5);
memset(sender_buf+left, 0, PCM_FRAME_LENGTH-left);
}else{
continue;
}
}else{
hal_os_rb_read(ring_sender,sender_buf, PCM_FRAME_LENGTH, 5);
}
cnt = 0;
for(index=0; index<576; index++)
{
mp3_buff[index] = sender_buf[cnt] + (sender_buf[cnt+1]<<8);
cnt=cnt+2;
}
data_out = shine_encode_buffer(sh_shine,&ppbuff,&cnt);
sendAudioPacket(data_out, cnt);
//quec_usbcdc_write(data_out, cnt);
//vad 结束 进来发送最后一个包
if(!vad_audio_is_recording()){
left = rb_get_space_used(ring_sender);
if(left>0) goto sender_loop;
printf("sendEndPacket\n");
data_out=shine_flush(sh_shine,&index);
sendAudioPacket(data_out, index);
sendEndPacket();
if(sh_shine!=NULL){
shine_close(sh_shine);
sh_shine = NULL;
}
shine_init = 0;
audio_packet_type = 0;
}
}
}
void app_nfc_sender(int8 *uuid)
{
int8 uuid_buff[128];
memset(uuid_buff, 0, sizeof(uuid_buff));
sprintf(uuid_buff, "REGISTER_TARGET_DEVICE:%s", uuid);
printf("uuid_buff:%s\r\n", uuid_buff);
talkingq_ws_connect();
talkingq_ws_send_text(uuid_buff);
audio_packet_type = 4;
return 0;
}
void app_vad_check_thread(void *arvg)
{
while(1)
{
if(app_get_app_sleep_flag()!=0){
vad_audio_process();
ql_rtos_task_sleep_ms(20);
}else{
ql_rtos_semaphore_wait(vad_check_sem, 0xFFFFFFFF);
}
}
}
void app_vad_check_sem_release(void)
{
ql_rtos_semaphore_release(vad_check_sem);
}
#if 0
void app_check_connect_sem_release(void)
{
ql_rtos_semaphore_release(check_connect_sem);
}
#endif
int app_ws_talkingq_init()
{
//one second = 640*50 = 32000
ring_sender = hal_os_rb_create(32000*5);
ql_rtos_semaphore_create(&sender_sem, 0);
ql_rtos_semaphore_create(&vad_check_sem, 0);
//ql_rtos_semaphore_create(&check_connect_sem, 0);
ql_rtos_queue_create(&player_queue, sizeof(ice_work_item *), 20);
ql_task_t playerTaskRef = NULL;
// 安全修复:增加栈大小从2KB到4KB,防止音频处理时栈溢出
ql_rtos_task_create(&playerTaskRef, 1024*4, 100, "app_player", app_player_thread, NULL);
ql_task_t checkTaskRef = NULL;
ql_rtos_task_create(&checkTaskRef, 1024*3, 102, "check_connect", check_connect_thread_entry, NULL);
ql_task_t senderTaskRef = NULL;
ql_rtos_task_create(&senderTaskRef, 1024*8, 101, "app_sender", app_sender_thread, NULL);
ql_task_t vadTaskRef = NULL;
// 安全修复:减小栈大小从20KB到8KB(VAD不需要那么大),节约内存
ql_rtos_task_create(&vadTaskRef, 1024*8, 101, "vad_check", app_vad_check_thread, NULL);
return 0;
}