feat(factory): add GNSS test and LED status modes
This commit is contained in:
@@ -11,6 +11,7 @@ vpath %.c ../custom_app ../custom_app/lib/libnfc
|
||||
SRC_FILES+= \
|
||||
factory_test.c \
|
||||
factory_protocol.c \
|
||||
factory_gnss.c \
|
||||
factory_audio.c \
|
||||
hal_i2c.c \
|
||||
DP1312EA.c \
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ql_gnss.h"
|
||||
#include "ql_rtos.h"
|
||||
|
||||
#include "factory_gnss.h"
|
||||
|
||||
#define FT_GNSS_NMEA_GGA_GSV ((1 << 0) | (1 << 2))
|
||||
|
||||
typedef struct {
|
||||
volatile int open_ret;
|
||||
volatile u32 nmea_count;
|
||||
volatile int sat_count;
|
||||
volatile int fix_status;
|
||||
volatile int started;
|
||||
u32 start_ms;
|
||||
} factory_gnss_context_t;
|
||||
|
||||
static factory_gnss_context_t g_factory_gnss;
|
||||
|
||||
static u32 factory_gnss_now_ms(void)
|
||||
{
|
||||
return ql_rtos_get_systicks() * 5U;
|
||||
}
|
||||
|
||||
static int nmea_field_int(const char *line, int line_len, int wanted_field,
|
||||
int *value)
|
||||
{
|
||||
int field = 0;
|
||||
int i;
|
||||
int parsed = 0;
|
||||
int number = 0;
|
||||
|
||||
for (i = 0; i < line_len; ++i) {
|
||||
char ch = line[i];
|
||||
if (ch == ',') {
|
||||
if (field == wanted_field) break;
|
||||
++field;
|
||||
continue;
|
||||
}
|
||||
if (field == wanted_field) {
|
||||
if (ch < '0' || ch > '9') break;
|
||||
number = number * 10 + (ch - '0');
|
||||
parsed = 1;
|
||||
}
|
||||
}
|
||||
if (!parsed) return 0;
|
||||
*value = number;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void factory_gnss_callback(char *data, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!data || len <= 0) return;
|
||||
for (i = 0; i + 5 < len; ++i) {
|
||||
int line_end;
|
||||
int value;
|
||||
|
||||
if (data[i] != '$') continue;
|
||||
++g_factory_gnss.nmea_count;
|
||||
for (line_end = i; line_end < len; ++line_end) {
|
||||
if (data[line_end] == '\r' || data[line_end] == '\n') break;
|
||||
}
|
||||
if (data[i + 3] == 'G' && data[i + 4] == 'S' && data[i + 5] == 'V' &&
|
||||
nmea_field_int(data + i, line_end - i, 3, &value) &&
|
||||
value > g_factory_gnss.sat_count) {
|
||||
g_factory_gnss.sat_count = value;
|
||||
}
|
||||
if (data[i + 3] == 'G' && data[i + 4] == 'G' && data[i + 5] == 'A' &&
|
||||
nmea_field_int(data + i, line_end - i, 6, &value) &&
|
||||
value > g_factory_gnss.fix_status) {
|
||||
g_factory_gnss.fix_status = value;
|
||||
}
|
||||
i = line_end;
|
||||
}
|
||||
}
|
||||
|
||||
void factory_gnss_start(void)
|
||||
{
|
||||
int cfgsys_ret;
|
||||
int cfgmsg_ret;
|
||||
int mode_ret;
|
||||
|
||||
memset(&g_factory_gnss, 0, sizeof(g_factory_gnss));
|
||||
g_factory_gnss.open_ret = -1;
|
||||
g_factory_gnss.start_ms = factory_gnss_now_ms();
|
||||
cfgsys_ret = ql_gnss_cfgsys_set(1);
|
||||
cfgmsg_ret = ql_gnss_cfgmsg_set(FT_GNSS_NMEA_GGA_GSV);
|
||||
mode_ret = ql_set_gnss_nmea_mode(PACKET_MODE);
|
||||
g_factory_gnss.open_ret = ql_gnss_open(factory_gnss_callback);
|
||||
g_factory_gnss.started = (g_factory_gnss.open_ret == 0);
|
||||
printf("[FT_GNSS] start open_ret=%d cfgsys_ret=%d cfgmsg_ret=%d mode_ret=%d\r\n",
|
||||
g_factory_gnss.open_ret, cfgsys_ret, cfgmsg_ret, mode_ret);
|
||||
}
|
||||
|
||||
void factory_gnss_snapshot(factory_gnss_result_t *result)
|
||||
{
|
||||
if (!result) return;
|
||||
result->open_ret = g_factory_gnss.open_ret;
|
||||
result->nmea_count = g_factory_gnss.nmea_count;
|
||||
result->sat_count = g_factory_gnss.sat_count;
|
||||
result->fix_status = g_factory_gnss.fix_status;
|
||||
result->search_ms = factory_gnss_now_ms() - g_factory_gnss.start_ms;
|
||||
}
|
||||
|
||||
void factory_gnss_stop(void)
|
||||
{
|
||||
int ret;
|
||||
if (!g_factory_gnss.started) return;
|
||||
ret = ql_gnss_close();
|
||||
g_factory_gnss.started = 0;
|
||||
printf("[FT_GNSS] stop ret=%d nmea=%lu sat=%d fix=%d\r\n", ret,
|
||||
(unsigned long)g_factory_gnss.nmea_count,
|
||||
g_factory_gnss.sat_count, g_factory_gnss.fix_status);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifndef BANBAN_FACTORY_GNSS_H
|
||||
#define BANBAN_FACTORY_GNSS_H
|
||||
|
||||
#include "ql_type.h"
|
||||
|
||||
typedef struct {
|
||||
int open_ret;
|
||||
u32 nmea_count;
|
||||
int sat_count;
|
||||
int fix_status;
|
||||
u32 search_ms;
|
||||
} factory_gnss_result_t;
|
||||
|
||||
void factory_gnss_start(void);
|
||||
void factory_gnss_snapshot(factory_gnss_result_t *result);
|
||||
void factory_gnss_stop(void);
|
||||
|
||||
#endif
|
||||
@@ -42,7 +42,7 @@ const char *ft_node_name(ft_node_t node)
|
||||
{
|
||||
static const char *names[FT_NODE_COUNT] = {
|
||||
"BOOT_AUDIO", "POWER_ADC", "AUDIO_LOOPBACK",
|
||||
"ESIM", "DATA_CALL", "NFC"
|
||||
"ESIM", "DATA_CALL", "NFC", "GNSS"
|
||||
};
|
||||
|
||||
if (node < 0 || node >= FT_NODE_COUNT) {
|
||||
@@ -249,7 +249,7 @@ void ft_protocol_session_result(int passed, ft_error_t code)
|
||||
"\"duration_ms\":%lu,\"nodes\":{"
|
||||
"\"BOOT_AUDIO\":\"%s\",\"POWER_ADC\":\"%s\","
|
||||
"\"AUDIO_LOOPBACK\":\"%s\",\"ESIM\":\"%s\","
|
||||
"\"DATA_CALL\":\"%s\",\"NFC\":\"%s\"}}",
|
||||
"\"DATA_CALL\":\"%s\",\"NFC\":\"%s\",\"GNSS\":\"%s\"}}",
|
||||
FT_PROTOCOL_VERSION, (unsigned long)++g_protocol.seq,
|
||||
g_protocol.session, (unsigned long)ft_now_ms(), passed ? "PASS" : "FAIL",
|
||||
code, g_protocol.imei, g_protocol.iccid, g_protocol.nfc_uid,
|
||||
@@ -258,7 +258,8 @@ void ft_protocol_session_result(int passed, ft_error_t code)
|
||||
(unsigned long)(ft_now_ms() - g_protocol.start_ms),
|
||||
ft_state_name(g_protocol.nodes[0]), ft_state_name(g_protocol.nodes[1]),
|
||||
ft_state_name(g_protocol.nodes[2]), ft_state_name(g_protocol.nodes[3]),
|
||||
ft_state_name(g_protocol.nodes[4]), ft_state_name(g_protocol.nodes[5]));
|
||||
ft_state_name(g_protocol.nodes[4]), ft_state_name(g_protocol.nodes[5]),
|
||||
ft_state_name(g_protocol.nodes[6]));
|
||||
}
|
||||
|
||||
void ft_protocol_power_off(void)
|
||||
|
||||
@@ -9,12 +9,14 @@
|
||||
#include "ql_iic.h"
|
||||
#include "ql_nw.h"
|
||||
#include "ql_power.h"
|
||||
#include "ql_pwm.h"
|
||||
#include "ql_rtos.h"
|
||||
#include "ql_sim.h"
|
||||
#include "sockets.h"
|
||||
|
||||
#include "DP1312EA.h"
|
||||
#include "factory_audio.h"
|
||||
#include "factory_gnss.h"
|
||||
#include "factory_protocol.h"
|
||||
#include "factory_test.h"
|
||||
#include "nfc.h"
|
||||
@@ -34,24 +36,96 @@ static char g_ip_address[64];
|
||||
static volatile int g_led_mode;
|
||||
static ql_task_t g_led_task;
|
||||
|
||||
enum {
|
||||
FT_LED_OFF = 0,
|
||||
FT_LED_ON,
|
||||
FT_LED_LOOPBACK,
|
||||
FT_LED_NFC,
|
||||
FT_LED_GNSS
|
||||
};
|
||||
|
||||
static int factory_led_pwm_set(unsigned short duty, int *enabled)
|
||||
{
|
||||
QL_PWM_CONFIG_EX config;
|
||||
|
||||
config.pwm_psc = 1;
|
||||
config.high_one_cycle_duration = duty;
|
||||
config.total_one_cycle_duration = 320;
|
||||
config.clk_src = QL_PWM_CLK_SRC_32K;
|
||||
if (ql_pwm_config_plus_ex(FT_STATUS_LED_PIN, &config) != 0) return -1;
|
||||
if (!*enabled) {
|
||||
if (ql_pwm_enable(FT_STATUS_LED_PIN) != 0) {
|
||||
ql_pwm_disable(FT_STATUS_LED_PIN);
|
||||
return -1;
|
||||
}
|
||||
*enabled = 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void factory_led_pwm_stop(int *enabled)
|
||||
{
|
||||
if (*enabled) ql_pwm_disable(FT_STATUS_LED_PIN);
|
||||
*enabled = 0;
|
||||
}
|
||||
|
||||
static void factory_led_task(void *arg)
|
||||
{
|
||||
int phase = 0;
|
||||
int last_mode = -1;
|
||||
int pwm_enabled = 0;
|
||||
int pwm_failed = 0;
|
||||
int breath_duty = 16;
|
||||
int breath_step = 16;
|
||||
(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) {
|
||||
int mode = g_led_mode;
|
||||
if (mode != last_mode) {
|
||||
phase = 0;
|
||||
breath_duty = 16;
|
||||
breath_step = 16;
|
||||
pwm_failed = 0;
|
||||
last_mode = mode;
|
||||
}
|
||||
if (mode == FT_LED_GNSS && !pwm_failed) {
|
||||
if (factory_led_pwm_set((unsigned short)breath_duty,
|
||||
&pwm_enabled) == 0) {
|
||||
breath_duty += breath_step;
|
||||
if (breath_duty >= 304 || breath_duty <= 16) {
|
||||
breath_step = -breath_step;
|
||||
}
|
||||
ql_rtos_task_sleep_ms(40);
|
||||
continue;
|
||||
}
|
||||
pwm_failed = 1;
|
||||
factory_led_pwm_stop(&pwm_enabled);
|
||||
printf("[FT_LED] GPIO70 PWM unavailable, use GNSS double-flash\r\n");
|
||||
}
|
||||
if (pwm_enabled) {
|
||||
factory_led_pwm_stop(&pwm_enabled);
|
||||
ql_gpio_init(FT_STATUS_LED_PIN, PIN_DIRECTION_OUT,
|
||||
PIN_PULL_DISABLE, PIN_LEVEL_HIGH);
|
||||
}
|
||||
if (mode == FT_LED_OFF) {
|
||||
ql_gpio_set_level(FT_STATUS_LED_PIN, PIN_LEVEL_LOW);
|
||||
ql_rtos_task_sleep_ms(100);
|
||||
} else if (g_led_mode == 1) {
|
||||
} else if (mode == FT_LED_ON) {
|
||||
ql_gpio_set_level(FT_STATUS_LED_PIN, PIN_LEVEL_HIGH);
|
||||
ql_rtos_task_sleep_ms(100);
|
||||
} else if (mode == FT_LED_GNSS) {
|
||||
static const int levels[] = {1, 0, 1, 0};
|
||||
static const int delays[] = {80, 80, 80, 520};
|
||||
ql_gpio_set_level(FT_STATUS_LED_PIN,
|
||||
levels[phase] ? PIN_LEVEL_HIGH : PIN_LEVEL_LOW);
|
||||
ql_rtos_task_sleep_ms(delays[phase]);
|
||||
phase = (phase + 1) % 4;
|
||||
} 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);
|
||||
ql_rtos_task_sleep_ms(mode == FT_LED_LOOPBACK ? 250 : 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -123,9 +197,9 @@ static ft_error_t run_audio_loopback(char *data, unsigned int data_size)
|
||||
if (ret != 0) {
|
||||
return FT_ERR_AUDIO_PLAY;
|
||||
}
|
||||
factory_led_mode(2);
|
||||
factory_led_mode(FT_LED_LOOPBACK);
|
||||
ret = factory_audio_wait_and_loopback(FT_DIALOG_KEY_TIMEOUT_MS, &recorded_ms);
|
||||
factory_led_mode(1);
|
||||
factory_led_mode(FT_LED_ON);
|
||||
snprintf(data, data_size,
|
||||
"{\"recorded_ms\":%lu,\"max_ms\":%d,\"volume_percent\":100}",
|
||||
(unsigned long)recorded_ms, FT_AUDIO_MAX_MS);
|
||||
@@ -211,12 +285,12 @@ static ft_error_t run_nfc(char *data, unsigned int data_size)
|
||||
if (audio_ret != 0) {
|
||||
return FT_ERR_AUDIO_PLAY;
|
||||
}
|
||||
factory_led_mode(3);
|
||||
factory_led_mode(FT_LED_NFC);
|
||||
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);
|
||||
factory_led_mode(FT_LED_ON);
|
||||
return FT_ERR_NFC_CHIP;
|
||||
}
|
||||
|
||||
@@ -227,16 +301,54 @@ 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);
|
||||
factory_led_mode(FT_LED_ON);
|
||||
return FT_OK;
|
||||
}
|
||||
ql_rtos_task_sleep_ms(200);
|
||||
} while (ft_now_ms() - start < FT_NFC_TIMEOUT_MS);
|
||||
|
||||
factory_led_mode(1);
|
||||
factory_led_mode(FT_LED_ON);
|
||||
return FT_ERR_NFC_TIMEOUT;
|
||||
}
|
||||
|
||||
static ft_error_t run_gnss(char *data, unsigned int data_size)
|
||||
{
|
||||
factory_gnss_result_t result;
|
||||
int audio_ret;
|
||||
|
||||
factory_led_mode(FT_LED_GNSS);
|
||||
audio_ret = factory_audio_play_prompt(FT_AUDIO_GNSS_PATH);
|
||||
if (audio_ret != 0) {
|
||||
factory_gnss_stop();
|
||||
factory_led_mode(FT_LED_ON);
|
||||
return audio_ret == -2 ? FT_ERR_AUDIO_FILE_MISSING : FT_ERR_AUDIO_PLAY;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
factory_gnss_snapshot(&result);
|
||||
if (result.open_ret == 0 && result.nmea_count >= 1 &&
|
||||
(result.sat_count >= 1 || result.fix_status > 0)) {
|
||||
snprintf(data, data_size,
|
||||
"{\"open_ret\":%d,\"nmea_count\":%lu,\"sat_count\":%d,"
|
||||
"\"fix_status\":%d,\"search_ms\":%lu,"
|
||||
"\"required_nmea_count\":1,\"required_sat_count\":1}",
|
||||
result.open_ret, (unsigned long)result.nmea_count,
|
||||
result.sat_count, result.fix_status,
|
||||
(unsigned long)result.search_ms);
|
||||
factory_gnss_stop();
|
||||
factory_led_mode(FT_LED_ON);
|
||||
return FT_OK;
|
||||
}
|
||||
|
||||
if (result.open_ret != 0) {
|
||||
ql_rtos_task_sleep_s(2);
|
||||
factory_gnss_start();
|
||||
} else {
|
||||
ql_rtos_task_sleep_ms(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static ft_error_t run_node(ft_node_t node, char *data, unsigned int data_size)
|
||||
{
|
||||
data[0] = '\0';
|
||||
@@ -247,6 +359,7 @@ static ft_error_t run_node(ft_node_t node, char *data, unsigned int data_size)
|
||||
case FT_NODE_ESIM: return run_esim(data, data_size);
|
||||
case FT_NODE_DATA_CALL: return run_data_call(data, data_size);
|
||||
case FT_NODE_NFC: return run_nfc(data, data_size);
|
||||
case FT_NODE_GNSS: return run_gnss(data, data_size);
|
||||
default: return FT_ERR_NFC_LOCKED;
|
||||
}
|
||||
}
|
||||
@@ -261,10 +374,11 @@ static void factory_test_task(void *arg)
|
||||
int audio_ret;
|
||||
(void)arg;
|
||||
|
||||
factory_gnss_start();
|
||||
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);
|
||||
factory_led_mode(FT_LED_ON);
|
||||
if (ql_dev_get_imei(imei, sizeof(imei)) == 0) {
|
||||
ft_protocol_set_imei(imei);
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
#include "ql_type.h"
|
||||
#include "ql_gpio.h"
|
||||
|
||||
#define FT_PROTOCOL_VERSION 1
|
||||
#define FT_FIRMWARE_VERSION "FACTORY_TEST_1.0.0"
|
||||
#define FT_PROTOCOL_VERSION 2
|
||||
#define FT_FIRMWARE_VERSION "FACTORY_TEST_1.1.0"
|
||||
|
||||
#define FT_ADC_MIN_MV 3500
|
||||
#define FT_ADC_MAX_MV 4200
|
||||
@@ -37,6 +37,7 @@ typedef enum {
|
||||
FT_NODE_ESIM,
|
||||
FT_NODE_DATA_CALL,
|
||||
FT_NODE_NFC,
|
||||
FT_NODE_GNSS,
|
||||
FT_NODE_COUNT
|
||||
} ft_node_t;
|
||||
|
||||
@@ -58,6 +59,9 @@ typedef enum {
|
||||
FT_ERR_NETWORK_REGISTER = 1501,
|
||||
FT_ERR_PDP_ACTIVATE = 1502,
|
||||
FT_ERR_IP_INVALID = 1503,
|
||||
FT_ERR_GNSS_OPEN = 1601,
|
||||
FT_ERR_GNSS_NO_NMEA = 1602,
|
||||
FT_ERR_GNSS_NO_SATELLITE = 1603,
|
||||
FT_ERR_NFC_LOCKED = 1701,
|
||||
FT_ERR_NFC_CHIP = 1702,
|
||||
FT_ERR_NFC_TIMEOUT = 1703,
|
||||
|
||||
Reference in New Issue
Block a user