Files
banban/ql-application/threadx/interface/factory_test/factory_test.c
T

416 lines
13 KiB
C

#include <stdio.h>
#include <string.h>
#include "ql_adc.h"
#include "ql_application.h"
#include "ql_data_call.h"
#include "ql_dev.h"
#include "ql_gpio.h"
#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"
#include "reg.h"
#define FT_NFC_VERSION_EXPECTED 0x82
int g_testmode = 1;
int g_nfc_bind_status = 0;
extern int8 output_uuid[64];
extern s8 Read_NTAG213(u8 *uid_buf, u8 *data_buf, u16 *data_len);
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;
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) {
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 (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(mode == FT_LED_LOOPBACK ? 250 : 100);
}
}
}
static void factory_led_mode(int mode)
{
g_led_mode = mode;
}
static int iccid_valid(const char *iccid)
{
unsigned int len;
if (!iccid) return 0;
len = strlen(iccid);
return len >= 18 && len <= 22;
}
static ft_error_t run_boot_audio(char *data, unsigned int data_size)
{
int ret = factory_audio_init();
if (ret != 0) return FT_ERR_AUDIO_CODEC_INIT;
ret = factory_audio_play_prompt(FT_AUDIO_BOOT_PATH);
if (ret == -2) return FT_ERR_AUDIO_FILE_MISSING;
if (ret != 0) return FT_ERR_AUDIO_PLAY;
snprintf(data, data_size, "{\"path\":\"%s\",\"volume_percent\":100}",
FT_AUDIO_BOOT_PATH);
return FT_OK;
}
static ft_error_t run_power_adc(char *data, unsigned int data_size)
{
unsigned short samples[FT_ADC_SAMPLE_COUNT];
unsigned short min_value = 0;
unsigned short max_value = 0;
unsigned int sum = 0;
unsigned int i;
unsigned int average;
if (ql_adc_init() != 0) return FT_ERR_ADC_READ;
for (i = 0; i < FT_ADC_SAMPLE_COUNT; ++i) {
ql_adc_onff(1);
ql_rtos_task_sleep_ms(5);
ql_adc_onff(0);
samples[i] = ql_get_battery_vol();
if (samples[i] == 0) return FT_ERR_ADC_READ;
if (i == 0 || samples[i] < min_value) min_value = samples[i];
if (i == 0 || samples[i] > max_value) max_value = samples[i];
sum += samples[i];
ql_rtos_task_sleep_ms(45);
}
average = (sum - min_value - max_value) / (FT_ADC_SAMPLE_COUNT - 2);
snprintf(data, data_size,
"{\"voltage_mv\":%u,\"sample_min_mv\":%u,"
"\"sample_max_mv\":%u,\"limit_min_mv\":%d,"
"\"limit_max_mv\":%d}",
average, min_value, max_value, FT_ADC_MIN_MV, FT_ADC_MAX_MV);
if (average < FT_ADC_MIN_MV) return FT_ERR_ADC_LOW;
if (average > FT_ADC_MAX_MV) return FT_ERR_ADC_HIGH;
return FT_OK;
}
static ft_error_t run_audio_loopback(char *data, unsigned int data_size)
{
u32 recorded_ms = 0;
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(FT_LED_LOOPBACK);
ret = factory_audio_wait_and_loopback(FT_DIALOG_KEY_TIMEOUT_MS, &recorded_ms);
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);
if (ret == -4) return FT_ERR_DIALOG_KEY_TIMEOUT;
if (ret == -1) return FT_ERR_RECORD_OPEN;
if (ret == -2) return FT_ERR_RECORD_EMPTY;
if (ret != 0) return FT_ERR_LOOPBACK_PLAY;
return FT_OK;
}
static ft_error_t run_esim(char *data, unsigned int data_size)
{
int status = QL_SIM_STATUS_UNKNOWN;
int ret = -1;
char iccid[32] = {0};
u32 start = ft_now_ms();
do {
ret = ql_sim_get_card_status(&status);
if (ret == QL_SIM_SUCCESS && status == QL_SIM_STATUS_READY) break;
ql_rtos_task_sleep_ms(500);
} while (ft_now_ms() - start < FT_SIM_TIMEOUT_MS);
if (ret != QL_SIM_SUCCESS || status != QL_SIM_STATUS_READY) {
snprintf(data, data_size, "{\"sim_status\":%d}", status);
return FT_ERR_SIM_NOT_READY;
}
if (ql_sim_get_iccid(iccid, sizeof(iccid)) != QL_SIM_SUCCESS) {
return FT_ERR_ICCID_READ;
}
if (!iccid_valid(iccid)) {
snprintf(data, data_size, "{\"sim_status\":%d,\"iccid\":\"%s\"}",
status, iccid);
return FT_ERR_ICCID_FORMAT;
}
ft_protocol_set_iccid(iccid);
snprintf(data, data_size, "{\"sim_status\":\"READY\",\"iccid\":\"%s\"}",
iccid);
return FT_OK;
}
static ft_error_t run_data_call(char *data, unsigned int data_size)
{
struct ql_data_call_info info;
int ret;
int attempt;
if (ql_network_register_wait(120) != 0) return FT_ERR_NETWORK_REGISTER;
ql_set_data_call_asyn_mode(0, NULL);
ret = ql_start_data_call(1, 0, NULL, NULL, NULL, 0);
if (ret != 0) return FT_ERR_PDP_ACTIVATE;
memset(&info, 0, sizeof(info));
memset(g_ip_address, 0, sizeof(g_ip_address));
for (attempt = 0; attempt < 10; ++attempt) {
if (ql_get_data_call_info(1, 0, &info) == 0 && info.v4.state) {
inet_ntop(AF_INET, &info.v4.addr.ip, g_ip_address, sizeof(g_ip_address));
if (g_ip_address[0] && strcmp(g_ip_address, "0.0.0.0") != 0) break;
}
ql_rtos_task_sleep_ms(1000);
}
snprintf(data, data_size, "{\"ip\":\"%s\",\"ip_type\":\"IPv4\"}",
g_ip_address);
if (!g_ip_address[0] || strcmp(g_ip_address, "0.0.0.0") == 0) {
return FT_ERR_IP_INVALID;
}
return FT_OK;
}
static ft_error_t run_nfc(char *data, unsigned int data_size)
{
u8 uid[7] = {0};
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(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(FT_LED_ON);
return FT_ERR_NFC_CHIP;
}
do {
memset(output_uuid, 0, 64);
if (Read_NTAG213(uid, ntag_data, &ntag_len) == MI_OK && output_uuid[0]) {
ft_protocol_set_nfc_uid((char *)output_uuid);
snprintf(data, data_size,
"{\"chip_version\":\"0x%02X\",\"uid\":\"%s\"}",
version, (char *)output_uuid);
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(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';
switch (node) {
case FT_NODE_BOOT_AUDIO: return run_boot_audio(data, data_size);
case FT_NODE_POWER_ADC: return run_power_adc(data, data_size);
case FT_NODE_AUDIO_LOOPBACK: return run_audio_loopback(data, 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;
}
}
static void factory_test_task(void *arg)
{
char imei[32] = {0};
char data[384];
ft_node_t node;
ft_error_t result = FT_OK;
u32 start;
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(FT_LED_ON);
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);
start = ft_now_ms();
result = run_node(node, data, sizeof(data));
ft_protocol_node_result(node, result == FT_OK, result,
ft_now_ms() - start, data);
if (result != FT_OK) {
ft_protocol_session_result(0, result);
while (1) ql_rtos_task_sleep_s(60);
}
}
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);
}
ft_protocol_session_result(1, FT_OK);
ft_protocol_power_off();
ql_rtos_task_sleep_ms(500);
ql_power_down(1);
ft_protocol_session_result(0, FT_ERR_POWER_DOWN);
while (1) ql_rtos_task_sleep_s(60);
}
application_init(factory_test_task, "factory_test", 10, 0);