feat: automate FT1 factory test status reporting

This commit is contained in:
chenjintao
2026-09-24 11:21:40 +08:00
parent 8b499948dd
commit 06b0eaaf70
11 changed files with 461 additions and 38 deletions
@@ -9,6 +9,7 @@
#include "factory_protocol.h"
#define FT_PROTOCOL_LINE_MAX 1024
#define FT_PROTOCOL_UART_PORT QL_DEBUG_UART_PORT
typedef struct {
char session[32];
@@ -20,9 +21,17 @@ typedef struct {
u32 seq;
ft_state_t nodes[FT_NODE_COUNT];
int cdc_open;
ql_mutex_t tx_mutex;
ql_task_t status_task;
volatile int status_broadcast;
int active_node;
} ft_protocol_context_t;
static ft_protocol_context_t g_protocol;
static void ft_emit(const char *fmt, ...);
u32 ft_now_ms(void);
static void ft_status_task(void *arg);
u32 ft_now_ms(void)
{
@@ -64,10 +73,16 @@ static void ft_emit(const char *fmt, ...)
if (len < 0) return;
if (len >= (int)sizeof(line)) len = sizeof(line) - 1;
printf("%s", line);
if (g_protocol.cdc_open) {
ql_uart_write(QL_USB_CDC_PORT, (unsigned char *)line, len);
if (g_protocol.tx_mutex) ql_rtos_mutex_lock(g_protocol.tx_mutex, QL_WAIT_FOREVER);
if (FT_PROTOCOL_UART_PORT == QL_DEBUG_UART_PORT) {
/* DBG UART is the printf console on this board. Use the same
transport as the visible boot/application logs so FT1 frames are
actually received by the host terminal. */
printf("%s", line);
} else if (g_protocol.cdc_open) {
ql_uart_write(FT_PROTOCOL_UART_PORT, (unsigned char *)line, len);
}
if (g_protocol.tx_mutex) ql_rtos_mutex_unlock(g_protocol.tx_mutex);
}
static void ft_make_session(void)
@@ -94,10 +109,50 @@ static void ft_make_session(void)
void ft_protocol_init(void)
{
int uart_ret;
memset(&g_protocol, 0, sizeof(g_protocol));
g_protocol.cdc_open =
(ql_uart_open(QL_USB_CDC_PORT, QL_UART_BAUD_115200, QL_FC_NONE) == 0);
g_protocol.active_node = -1;
ql_rtos_mutex_create(&g_protocol.tx_mutex);
uart_ret = ql_uart_open(FT_PROTOCOL_UART_PORT, QL_UART_BAUD_115200, QL_FC_NONE);
g_protocol.cdc_open = (uart_ret == 0 || uart_ret == QL_UART_ALREAD_OPEN);
ft_make_session();
printf("[FT1] control UART port=%d open_ret=%d ready=%d\r\n",
FT_PROTOCOL_UART_PORT, uart_ret, g_protocol.cdc_open);
}
static void ft_status_task(void *arg)
{
(void)arg;
while (g_protocol.status_broadcast) {
int i;
char payload[FT_PROTOCOL_LINE_MAX - 16];
int offset = snprintf(payload, sizeof(payload),
"{\"v\":%d,\"type\":\"status_snapshot\",\"seq\":%lu,\"session\":\"%s\",\"uptime_ms\":%lu,\"active_node\":%d,\"nodes\":{",
FT_PROTOCOL_VERSION, (unsigned long)++g_protocol.seq,
g_protocol.session, (unsigned long)ft_now_ms(),
g_protocol.active_node);
for (i = 0; i < FT_NODE_COUNT && offset > 0 && offset < (int)sizeof(payload); ++i) {
offset += snprintf(payload + offset, sizeof(payload) - offset,
"%s\"%s\":\"%s\"",
i ? "," : "", ft_node_name((ft_node_t)i),
ft_state_name(g_protocol.nodes[i]));
}
if (offset > 0 && offset < (int)sizeof(payload)) {
snprintf(payload + offset, sizeof(payload) - offset, "}}");
ft_emit("%s", payload);
}
ql_rtos_task_sleep_ms(500);
}
}
void ft_protocol_start_status_broadcast(void)
{
if (g_protocol.status_broadcast) return;
g_protocol.status_broadcast = 1;
/* The snapshot formatter keeps a near-1 KiB JSON buffer on the task
stack; leave headroom for libc formatting and UART calls. */
ql_rtos_task_create(&g_protocol.status_task, 8192, 15,
"ft_status", ft_status_task, NULL);
}
static void ft_copy(char *dst, unsigned int dst_size, const char *src)
@@ -141,6 +196,7 @@ void ft_protocol_session_start(void)
void ft_protocol_node_start(ft_node_t node)
{
if (node >= 0 && node < FT_NODE_COUNT) g_protocol.active_node = node;
ft_emit("{\"v\":%d,\"type\":\"node_start\",\"seq\":%lu,"
"\"session\":\"%s\",\"uptime_ms\":%lu,\"imei\":\"%s\","
"\"iccid\":\"%s\",\"nfc_uid\":\"%s\",\"node\":%d,"
@@ -169,6 +225,7 @@ void ft_protocol_node_result(ft_node_t node, int passed, ft_error_t code,
const char *result = passed ? "PASS" : "FAIL";
if (node >= 0 && node < FT_NODE_COUNT) {
g_protocol.nodes[node] = passed ? FT_STATE_PASS : FT_STATE_FAIL;
g_protocol.active_node = -1;
}
if (!data_json || !data_json[0]) data_json = "{}";
@@ -10,6 +10,7 @@ typedef enum {
} ft_state_t;
void ft_protocol_init(void);
void ft_protocol_start_status_broadcast(void);
void ft_protocol_set_imei(const char *imei);
void ft_protocol_set_iccid(const char *iccid);
void ft_protocol_set_nfc_uid(const char *uid);
@@ -31,6 +31,35 @@ void handle_nfc_bind(const char *uid) { (void)uid; }
void handle_nfc_trigger(const char *uid) { (void)uid; }
static char g_ip_address[64];
static volatile int g_led_mode;
static ql_task_t g_led_task;
static void factory_led_task(void *arg)
{
int phase = 0;
(void)arg;
ql_gpio_init(FT_STATUS_LED_PIN, PIN_DIRECTION_OUT, PIN_PULL_DISABLE,
PIN_LEVEL_HIGH);
while (1) {
if (g_led_mode == 0) {
ql_gpio_set_level(FT_STATUS_LED_PIN, PIN_LEVEL_LOW);
ql_rtos_task_sleep_ms(100);
} else if (g_led_mode == 1) {
ql_gpio_set_level(FT_STATUS_LED_PIN, PIN_LEVEL_HIGH);
ql_rtos_task_sleep_ms(100);
} else {
phase = !phase;
ql_gpio_set_level(FT_STATUS_LED_PIN,
phase ? PIN_LEVEL_HIGH : PIN_LEVEL_LOW);
ql_rtos_task_sleep_ms(g_led_mode == 2 ? 250 : 100);
}
}
}
static void factory_led_mode(int mode)
{
g_led_mode = mode;
}
static int iccid_valid(const char *iccid)
{
@@ -87,7 +116,16 @@ static ft_error_t run_power_adc(char *data, unsigned int data_size)
static ft_error_t run_audio_loopback(char *data, unsigned int data_size)
{
u32 recorded_ms = 0;
int ret = factory_audio_wait_and_loopback(FT_DIALOG_KEY_TIMEOUT_MS, &recorded_ms);
int ret = factory_audio_play_prompt(FT_AUDIO_LOOPBACK_PATH);
if (ret == -2) {
return FT_ERR_AUDIO_FILE_MISSING;
}
if (ret != 0) {
return FT_ERR_AUDIO_PLAY;
}
factory_led_mode(2);
ret = factory_audio_wait_and_loopback(FT_DIALOG_KEY_TIMEOUT_MS, &recorded_ms);
factory_led_mode(1);
snprintf(data, data_size,
"{\"recorded_ms\":%lu,\"max_ms\":%d,\"volume_percent\":100}",
(unsigned long)recorded_ms, FT_AUDIO_MAX_MS);
@@ -163,12 +201,22 @@ static ft_error_t run_nfc(char *data, unsigned int data_size)
u8 ntag_data[180] = {0};
u16 ntag_len = 0;
u8 version;
int audio_ret;
u32 start = ft_now_ms();
audio_ret = factory_audio_play_prompt(FT_AUDIO_NFC_PATH);
if (audio_ret == -2) {
return FT_ERR_AUDIO_FILE_MISSING;
}
if (audio_ret != 0) {
return FT_ERR_AUDIO_PLAY;
}
factory_led_mode(3);
DP1312EA_Reset();
version = DP1312EA_ReadRawRC(VersionReg);
if (version != FT_NFC_VERSION_EXPECTED) {
snprintf(data, data_size, "{\"chip_version\":\"0x%02X\"}", version);
factory_led_mode(1);
return FT_ERR_NFC_CHIP;
}
@@ -179,11 +227,13 @@ static ft_error_t run_nfc(char *data, unsigned int data_size)
snprintf(data, data_size,
"{\"chip_version\":\"0x%02X\",\"uid\":\"%s\"}",
version, (char *)output_uuid);
factory_led_mode(1);
return FT_OK;
}
ql_rtos_task_sleep_ms(200);
} while (ft_now_ms() - start < FT_NFC_TIMEOUT_MS);
factory_led_mode(1);
return FT_ERR_NFC_TIMEOUT;
}
@@ -213,10 +263,13 @@ static void factory_test_task(void *arg)
ql_rtos_task_sleep_s(2);
ft_protocol_init();
ql_rtos_task_create(&g_led_task, 2048, 15, "ft_led", factory_led_task, NULL);
factory_led_mode(1);
if (ql_dev_get_imei(imei, sizeof(imei)) == 0) {
ft_protocol_set_imei(imei);
}
ft_protocol_session_start();
ft_protocol_start_status_broadcast();
for (node = FT_NODE_BOOT_AUDIO; node < FT_NODE_COUNT; ++node) {
ft_protocol_node_start(node);
@@ -230,11 +283,7 @@ static void factory_test_task(void *arg)
}
}
audio_ret = factory_audio_play_prompt(FT_AUDIO_PASS_PATH);
if (audio_ret == 0) {
ql_rtos_task_sleep_ms(300);
audio_ret = factory_audio_play_prompt(FT_AUDIO_SHUTDOWN_PATH);
}
audio_ret = factory_audio_play_prompt(FT_AUDIO_SHUTDOWN_PATH);
if (audio_ret != 0) {
ft_protocol_session_result(0, FT_ERR_FINAL_AUDIO);
while (1) ql_rtos_task_sleep_s(60);
@@ -21,10 +21,13 @@
#define FT_NFC_TIMEOUT_MS 60000
#define FT_LOOPBACK_KEY_PIN GPIO_PIN_NO_84
#define FT_STATUS_LED_PIN GPIO_PIN_NO_70
#define FT_PA_PIN GPIO_PIN_NO_68
#define FT_AUDIO_BOOT_PATH "U:/factory_boot_zh.mp3"
#define FT_AUDIO_PASS_PATH "U:/factory_pass_zh.mp3"
#define FT_AUDIO_LOOPBACK_PATH "U:/factory_audio_loopback_zh.mp3"
#define FT_AUDIO_NFC_PATH "U:/factory_nfc_test_zh.mp3"
#define FT_AUDIO_GNSS_PATH "U:/factory_gnss_test_zh.mp3"
#define FT_AUDIO_SHUTDOWN_PATH "U:/factory_shutdown_zh.mp3"
typedef enum {