// 音频缓冲区(用于真实语音测试)
#define VAD_AUDIO_BUFFER_SIZE 3200 // 200ms @ 16kHz
-static int16_t g_audio_buffer[VAD_AUDIO_BUFFER_SIZE];
+static int16_t *g_audio_buffer = NULL; /* heap allocated */
static int g_audio_buffer_pos = 0;
/* 录音封装(由 C++ 提供,C 接口声明) */
/* ---------------- 实时录音并同步喂给 VAD(基于文件尾随) ---------------- */
static volatile bool g_vad_live_running = false;
static MediaThreadIdHandle g_vad_tail_thread = NULL;
-static char g_vad_live_path[256] = "/user/vad_live.pcm";
+#define LN_VAD_PATH_CAP 256
+static char *g_vad_live_path = NULL; /* heap allocated, cap LN_VAD_PATH_CAP */
static int g_vad_prev_status = -1;
+static void EnsureVadLivePathInitialized(void)
+{
+ if (g_vad_live_path == NULL)
+ {
+ g_vad_live_path = (char *)malloc(LN_VAD_PATH_CAP);
+ if (g_vad_live_path)
+ {
+ g_vad_live_path[0] = '\0';
+ strncpy(g_vad_live_path, "/user/vad_live.pcm", LN_VAD_PATH_CAP - 1);
+ g_vad_live_path[LN_VAD_PATH_CAP - 1] = '\0';
+ }
+ }
+}
+
// 尾随线程,持续从文件中读取数据并喂给 VAD
static void *VadTailThread(void *arg)
{
/* 等待文件可读 */
while (g_vad_live_running && fp == NULL)
{
- fp = fopen(g_vad_live_path, "rb");
+ EnsureVadLivePathInitialized();
+ fp = fopen(g_vad_live_path ? g_vad_live_path : "/user/vad_live.pcm", "rb");
if (fp == NULL)
{
usleep(50 * 1000);
{
printf("VAD StartRecordThread\n");
(void)arg;
- const char *startArgs[6] = {"01", "pcm", "16000", "1", "16", g_vad_live_path};
+ EnsureVadLivePathInitialized();
+ const char *startArgs[6] = {"01", "pcm", "16000", "1", "16", g_vad_live_path ? g_vad_live_path : "/user/vad_live.pcm"};
(void)AudioCaptureSample(6, startArgs);
return NULL;
}
}
/* 可选自定义输出路径 */
- const char *out_path = (const char *)g_vad_live_path;
+ EnsureVadLivePathInitialized();
+ const char *out_path = (const char *)(g_vad_live_path ? g_vad_live_path : "/user/vad_live.pcm");
uint8_t argvbuf[VAD_MAX_ARGC][VAD_MAX_ARGV_LEN] = {0};
uint32_t argc = 0;
if (para != NULL && paraLen > 0 && ConvertCmdToParam(para, paraLen, argvbuf, &argc) == VAD_SUCCESS && argc >= 1)
{
- strncpy(g_vad_live_path, (const char *)argvbuf[0], sizeof(g_vad_live_path) - 1);
- g_vad_live_path[sizeof(g_vad_live_path) - 1] = '\0';
- out_path = g_vad_live_path;
+ if (g_vad_live_path)
+ {
+ strncpy(g_vad_live_path, (const char *)argvbuf[0], LN_VAD_PATH_CAP - 1);
+ g_vad_live_path[LN_VAD_PATH_CAP - 1] = '\0';
+ out_path = g_vad_live_path;
+ }
}
printf("=== VAD 实时录音开始 ===\n");
/* 1) 确保目录存在并异步启动系统录音保存为 PCM(避免在 AT 线程上堆栈溢出) */
EnsureParentDir(out_path);
- strncpy(g_vad_live_path, out_path, sizeof(g_vad_live_path) - 1);
- g_vad_live_path[sizeof(g_vad_live_path) - 1] = '\0';
+ if (g_vad_live_path)
+ {
+ strncpy(g_vad_live_path, out_path, LN_VAD_PATH_CAP - 1);
+ g_vad_live_path[LN_VAD_PATH_CAP - 1] = '\0';
+ }
StartRecordThread(NULL);
/* 2) 启动尾随线程实时喂给 VAD */
{
/* 强制升级:直接触发下载 */
printf("[OTA] force upgrade: trigger download(强制升级:直接触发下载)\n");
- push_ota_status(OTA_UI_STATUS_START);
- do_ln_http_server_send_data(LN_HTTP_SERVICE_CMD_OTA_DOWNLOAD, NULL, 0);
+ push_ota_status(OTA_UI_STATUS_FORCE_START);
}
else if (ut == 0)
{
printf("[OTA] optional upgrade: waiting for user confirmation(可选升级:等待用户确认)\n");
- do_ln_http_server_send_data(LN_HTTP_SERVICE_CMD_OTA_DOWNLOAD, NULL, 0);
- push_ota_status(OTA_UI_STATUS_START);
+ push_ota_status(OTA_UI_STATUS_OPTIONAL_START);
}
else
{
return ERRCODE_FAIL;
}
- char line[2048];
+ char *line = (char *)malloc(2048);
+ if (!line)
+ {
+ fclose(fp);
+ return ERRCODE_FAIL;
+ }
int line_count = 0;
int success_count = 0;
int error_count = 0;
uapi_ln_audio_download_is_cancel(0);
}
- while (fgets(line, sizeof(line), fp))
+ while (fgets(line, 2048, fp))
{
if (g_ln_audio_download_cancel) {
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[SSE] download canceled by user\n");
}
fclose(fp);
+ free(line);
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[FILE_PARSE] processing completed: total lines=%d, success=%d, errors=%d\n",
line_count, success_count, error_count);
if (recv_data.data) free(recv_data.data);
}
}
-/* WebSocket自动启动任务(网络就绪后启动WebSocket)*/
-static app_task_definition_t ln_ws_autostart_taskcfg = {0};
-static void ln_ws_autostart_task(void const *arg)
-{
- printf("[LN][WS] autostart task running\n");
- while (1)
- {
- /* 每3秒检查一次网络 */
- if (uapi_dev_runtime_get_cat1_networkstat())
- {
- printf("[LN][WS] network online, try start websocket\n");
- int ret = ln_ws_start(ln_ws_process_message);
- if (ret == 0)
- {
- printf("[LN][WS] websocket started\n");
- break; /* 启动成功后退出任务 */
- }
- else
- {
- printf("[LN][WS] start websocket failed: %d, retry later\n", ret);
- }
- }
- usleep(3000 * 1000); // 休眠3秒
- }
- printf("[LN][WS] autostart task exit\n");
-}
// 雷鸟http服务初始化
errcode_t uapi_ln_http_server_init(void)
{
/* 不中断HTTP服务,按需可返回失败 */
}
- // /* 创建WebSocket自动启动任务:每3秒检测一次网络并启动WS */
- // ln_ws_autostart_taskcfg.create_queue_handle = NULL;
- // ln_ws_autostart_taskcfg.attr.name = "ln_ws_autostart_task";
- // ln_ws_autostart_taskcfg.attr.priority = osPriorityNormal;
- // ln_ws_autostart_taskcfg.attr.stack_size = 0x1000;
- // ln_ws_autostart_taskcfg.func = ln_ws_autostart_task;
- // ln_ws_autostart_taskcfg.use_mem = USE_EXTEND_MEM;
- // (void)uapi_create_osThread_custom(&ln_ws_autostart_taskcfg);
return ERRCODE_SUCC;
}
/* =================== 设备角色信息结构体定义 =================== */
typedef struct
{
- char character_id[64]; // 角色ID
- char character_name[64]; // 角色名称
- char avatar_hair_color[64]; // 发色
- char avatar_eyes_color[64]; // 瞳色
- char avatar_hair_style[64]; // 发型
- char avatar_eyes_shape[64]; // 眼型
- char avatar_screen_image[256]; // 屏保图片
+ char *character_id; // 角色ID
+ char *character_name; // 角色名称
+ char *avatar_hair_color; // 发色
+ char *avatar_eyes_color; // 瞳色
+ char *avatar_hair_style; // 发型
+ char *avatar_eyes_shape; // 眼型
+ char *avatar_screen_image; // 屏保图片
int avatar_screen_image_as_emoji; // 屏保图片是否作为对话表情
} LnCharacterConfig;
typedef struct
{
int scheduleId;
- char deviceId[64];
- char scheduleName[128];
- char reminderContent[512];
- char reminderAudioUrl[512];
+ char *deviceId;
+ char *scheduleName;
+ char *reminderContent;
+ char *reminderAudioUrl;
int enableVoice;
int enableVibration;
} ScheduleReminder;
/* 日程 日程提醒醒配置配置信息结构体 */
typedef struct
{
- char scheduleName[128]; // 日程名称
+ char *scheduleName; // 日程名称
} ScheduleReminderConfig;
/* 初始化 LN HTTP 服务 */
#include <string.h>
#include <cJSON.h>
-ScheduleReminder g_schedule_reminder = {0};
-/* å®\89å\85¨å\86\99å\85¥å¸®å\8a©å\87½æ\95° */
-static inline void ln_safe_strcpy(char *dst, size_t dst_size, const char *src)
+LnCharacterConfig g_ln_character_config = {0};
+/* å\8a¨æ\80\81å\97符串工å\85· */
+static inline char *ln_strdup_s(const char *src)
{
- if (!dst || dst_size == 0)
- return;
- if (src == NULL)
- {
- dst[0] = '\0';
- return;
- }
- strncpy(dst, src, dst_size - 1);
- dst[dst_size - 1] = '\0';
+ if (src == NULL) src = "";
+ size_t len = strlen(src);
+ char *p = (char *)malloc(len + 1);
+ if (!p) return NULL;
+ memcpy(p, src, len + 1);
+ return p;
+}
+
+static inline void ln_assign_dup(char **dst, const char *src)
+{
+ if (!dst) return;
+ char *newp = ln_strdup_s(src);
+ if (!newp) return;
+ if (*dst) free(*dst);
+ *dst = newp;
}
/* 解析设备角色配置 JSON 并保存到全局结构体 */
/* 基础字段 */
cJSON *cid = cJSON_GetObjectItem(data, "characterId");
cJSON *cname = cJSON_GetObjectItem(data, "characterName");
- ln_safe_strcpy(g_ln_character_config.character_id,
- sizeof(g_ln_character_config.character_id),
- (cid && cJSON_IsString(cid)) ? cid->valuestring : "");
+ ln_assign_dup(&g_ln_character_config.character_id,
+ (cid && cJSON_IsString(cid)) ? cid->valuestring : "");
- ln_safe_strcpy(g_ln_character_config.character_name,
- sizeof(g_ln_character_config.character_name),
- (cname && cJSON_IsString(cname)) ? cname->valuestring : "");
+ ln_assign_dup(&g_ln_character_config.character_name,
+ (cname && cJSON_IsString(cname)) ? cname->valuestring : "");
/* 头像配置 */
cJSON *avatar = cJSON_GetObjectItem(data, "characterAvatar");
cJSON *screenImage = cJSON_GetObjectItem(avatar, "screenImage");
cJSON *screenImageAsEmoji = cJSON_GetObjectItem(avatar, "screenImageAsEmoji");
- ln_safe_strcpy(g_ln_character_config.avatar_hair_color,
- sizeof(g_ln_character_config.avatar_hair_color),
- (hairColor && cJSON_IsString(hairColor)) ? hairColor->valuestring : "");
+ ln_assign_dup(&g_ln_character_config.avatar_hair_color,
+ (hairColor && cJSON_IsString(hairColor)) ? hairColor->valuestring : "");
- ln_safe_strcpy(g_ln_character_config.avatar_eyes_color,
- sizeof(g_ln_character_config.avatar_eyes_color),
- (eyesColor && cJSON_IsString(eyesColor)) ? eyesColor->valuestring : "");
+ ln_assign_dup(&g_ln_character_config.avatar_eyes_color,
+ (eyesColor && cJSON_IsString(eyesColor)) ? eyesColor->valuestring : "");
- ln_safe_strcpy(g_ln_character_config.avatar_hair_style,
- sizeof(g_ln_character_config.avatar_hair_style),
- (hairStyle && cJSON_IsString(hairStyle)) ? hairStyle->valuestring : "");
+ ln_assign_dup(&g_ln_character_config.avatar_hair_style,
+ (hairStyle && cJSON_IsString(hairStyle)) ? hairStyle->valuestring : "");
- ln_safe_strcpy(g_ln_character_config.avatar_eyes_shape,
- sizeof(g_ln_character_config.avatar_eyes_shape),
- (eyesShape && cJSON_IsString(eyesShape)) ? eyesShape->valuestring : "");
+ ln_assign_dup(&g_ln_character_config.avatar_eyes_shape,
+ (eyesShape && cJSON_IsString(eyesShape)) ? eyesShape->valuestring : "");
- ln_safe_strcpy(g_ln_character_config.avatar_screen_image,
- sizeof(g_ln_character_config.avatar_screen_image),
- (screenImage && cJSON_IsString(screenImage)) ? screenImage->valuestring : "");
+ ln_assign_dup(&g_ln_character_config.avatar_screen_image,
+ (screenImage && cJSON_IsString(screenImage)) ? screenImage->valuestring : "");
g_ln_character_config.avatar_screen_image_as_emoji =
(screenImageAsEmoji && cJSON_IsBool(screenImageAsEmoji)) ? (cJSON_IsTrue(screenImageAsEmoji) ? 1 : 0) : 0;
else
{
/* 缺省时清空结构体字段 */
- g_ln_character_config.avatar_hair_color[0] = '\0';
- g_ln_character_config.avatar_eyes_color[0] = '\0';
- g_ln_character_config.avatar_hair_style[0] = '\0';
- g_ln_character_config.avatar_eyes_shape[0] = '\0';
- g_ln_character_config.avatar_screen_image[0] = '\0';
+ ln_assign_dup(&g_ln_character_config.avatar_hair_color, "");
+ ln_assign_dup(&g_ln_character_config.avatar_eyes_color, "");
+ ln_assign_dup(&g_ln_character_config.avatar_hair_style, "");
+ ln_assign_dup(&g_ln_character_config.avatar_eyes_shape, "");
+ ln_assign_dup(&g_ln_character_config.avatar_screen_image, "");
g_ln_character_config.avatar_screen_image_as_emoji = 0;
}
/* 打印解析结果 */
printf("[DEVICE_CHARACTER] parsed:\n");
- printf(" character_id: %s\n", g_ln_character_config.character_id);
- printf(" character_name: %s\n", g_ln_character_config.character_name);
- printf(" avatar_hair_color: %s\n", g_ln_character_config.avatar_hair_color);
- printf(" avatar_eyes_color: %s\n", g_ln_character_config.avatar_eyes_color);
- printf(" avatar_hair_style: %s\n", g_ln_character_config.avatar_hair_style);
- printf(" avatar_eyes_shape: %s\n", g_ln_character_config.avatar_eyes_shape);
- printf(" avatar_screen_image: %s\n", g_ln_character_config.avatar_screen_image);
+ printf(" character_id: %s\n", g_ln_character_config.character_id ? g_ln_character_config.character_id : "");
+ printf(" character_name: %s\n", g_ln_character_config.character_name ? g_ln_character_config.character_name : "");
+ printf(" avatar_hair_color: %s\n", g_ln_character_config.avatar_hair_color ? g_ln_character_config.avatar_hair_color : "");
+ printf(" avatar_eyes_color: %s\n", g_ln_character_config.avatar_eyes_color ? g_ln_character_config.avatar_eyes_color : "");
+ printf(" avatar_hair_style: %s\n", g_ln_character_config.avatar_hair_style ? g_ln_character_config.avatar_hair_style : "");
+ printf(" avatar_eyes_shape: %s\n", g_ln_character_config.avatar_eyes_shape ? g_ln_character_config.avatar_eyes_shape : "");
+ printf(" avatar_screen_image: %s\n", g_ln_character_config.avatar_screen_image ? g_ln_character_config.avatar_screen_image : "");
printf(" avatar_screen_image_as_emoji: %d\n", g_ln_character_config.avatar_screen_image_as_emoji);
cJSON_Delete(root);
#include "cJSON.h"
#include "ln_api.h"
-LnCharacterConfig g_ln_character_config = {0};
+ScheduleReminder g_schedule_reminder = {0};
ScheduleReminderConfig g_schedule_reminder_config = {0};
-// å®\89å\85¨å\97符串å¤\8då\88¶å\87½æ\95°
-static void ln_safe_strcpy(char *dest, size_t dest_size, const char *src)
+// å\8a¨æ\80\81å\97符串工å\85·
+static char *ln_strdup_s(const char *src)
{
- if (!dest || dest_size == 0 || !src)
- return;
- strncpy(dest, src, dest_size - 1);
- dest[dest_size - 1] = '\0';
+ if (src == NULL) src = "";
+ size_t len = strlen(src);
+ char *p = (char *)malloc(len + 1);
+ if (!p) return NULL;
+ memcpy(p, src, len + 1);
+ return p;
+}
+
+static void ln_assign_dup(char **dst, const char *src)
+{
+ if (!dst) return;
+ char *newp = ln_strdup_s(src);
+ if (!newp) return;
+ if (*dst) free(*dst);
+ *dst = newp;
}
/* 解析日程提醒信息 */
// 填充全局结构体
g_schedule_reminder.scheduleId = (scheduleId && cJSON_IsNumber(scheduleId)) ? scheduleId->valueint : 0;
- ln_safe_strcpy(g_schedule_reminder.deviceId,
- sizeof(g_schedule_reminder.deviceId),
- (deviceId && cJSON_IsString(deviceId)) ? deviceId->valuestring : "");
+ ln_assign_dup(&g_schedule_reminder.deviceId,
+ (deviceId && cJSON_IsString(deviceId)) ? deviceId->valuestring : "");
- ln_safe_strcpy(g_schedule_reminder.scheduleName,
- sizeof(g_schedule_reminder.scheduleName),
- (scheduleName && cJSON_IsString(scheduleName)) ? scheduleName->valuestring : "");
+ ln_assign_dup(&g_schedule_reminder.scheduleName,
+ (scheduleName && cJSON_IsString(scheduleName)) ? scheduleName->valuestring : "");
- ln_safe_strcpy(g_schedule_reminder.reminderContent,
- sizeof(g_schedule_reminder.reminderContent),
- (reminderContent && cJSON_IsString(reminderContent)) ? reminderContent->valuestring : "");
+ ln_assign_dup(&g_schedule_reminder.reminderContent,
+ (reminderContent && cJSON_IsString(reminderContent)) ? reminderContent->valuestring : "");
- ln_safe_strcpy(g_schedule_reminder.reminderAudioUrl,
- sizeof(g_schedule_reminder.reminderAudioUrl),
- (reminderAudioUrl && cJSON_IsString(reminderAudioUrl)) ? reminderAudioUrl->valuestring : "");
+ ln_assign_dup(&g_schedule_reminder.reminderAudioUrl,
+ (reminderAudioUrl && cJSON_IsString(reminderAudioUrl)) ? reminderAudioUrl->valuestring : "");
g_schedule_reminder.enableVoice = (enableVoice && cJSON_IsNumber(enableVoice)) ? enableVoice->valueint : 0;
g_schedule_reminder.enableVibration = (enableVibration && cJSON_IsNumber(enableVibration)) ? enableVibration->valueint : 0;
// 打印解析结果
printf("[SCHEDULE_REMINDER] parsed successfully:\n");
printf(" scheduleId: %d\n", g_schedule_reminder.scheduleId);
- printf(" deviceId: %s\n", g_schedule_reminder.deviceId);
- printf(" scheduleName: %s\n", g_schedule_reminder.scheduleName);
- printf(" reminderContent: %s\n", g_schedule_reminder.reminderContent);
- printf(" reminderAudioUrl: %s\n", g_schedule_reminder.reminderAudioUrl);
+ printf(" deviceId: %s\n", g_schedule_reminder.deviceId ? g_schedule_reminder.deviceId : "");
+ printf(" scheduleName: %s\n", g_schedule_reminder.scheduleName ? g_schedule_reminder.scheduleName : "");
+ printf(" reminderContent: %s\n", g_schedule_reminder.reminderContent ? g_schedule_reminder.reminderContent : "");
+ printf(" reminderAudioUrl: %s\n", g_schedule_reminder.reminderAudioUrl ? g_schedule_reminder.reminderAudioUrl : "");
printf(" enableVoice: %d\n", g_schedule_reminder.enableVoice);
printf(" enableVibration: %d\n", g_schedule_reminder.enableVibration);
// 解析日程名称字段
cJSON *scheduleName = cJSON_GetObjectItem(data, "scheduleName");
- ln_safe_strcpy(g_schedule_reminder_config.scheduleName,
- sizeof(g_schedule_reminder_config.scheduleName),
- (scheduleName && cJSON_IsString(scheduleName)) ? scheduleName->valuestring : "");
+ ln_assign_dup(&g_schedule_reminder_config.scheduleName,
+ (scheduleName && cJSON_IsString(scheduleName)) ? scheduleName->valuestring : "");
// 打印解析结果
printf("[SCHEDULE_REMINDER_CONFIG] parsed successfully:\n");
- printf(" scheduleName: %s\n", g_schedule_reminder_config.scheduleName);
+ printf(" scheduleName: %s\n", g_schedule_reminder_config.scheduleName ? g_schedule_reminder_config.scheduleName : "");
// 清理资源
cJSON_Delete(root);
*/
typedef enum {
OTA_UI_STATUS_NOT_START = 0, /* 未开始 */
- OTA_UI_STATUS_START, /* 开始 */
+ OTA_UI_STATUS_FORCE_START, /* ota强制开始升级状态 */
+ OTA_UI_STATUS_OPTIONAL_START, /* ota可选开始升级状态 */
OTA_UI_STATUS_FAILED, /* 失败 */
OTA_UI_STATUS_SUCCESS, /* 成功 */
} ota_ui_status_t;