/******************************************************************************* * @file cmd_parse.c * @brief Command Parser Wrapper (Legacy Compatibility) * @author Charles KWON * @date 2025-02-04 * @copyright (c) 2025 Medithings Inc. All rights reserved. * * @details This file provides: * - Legacy compatibility wrapper (received_command_process) * - Utility functions (error handling, validation) * - EEPROM read functions * * Actual parsing is handled by mt_parser/parser.c * Command handlers are in cmd/cmd.c ******************************************************************************/ #include "cmd_parse.h" #include "debug_print.h" #include "parser.h" #include "i2c_manager.h" #include "app_timer.h" #include "config/device_config.h" #include "ble/ble_data_tx.h" #include "ble_nus.h" #include "crc16.h" #include "cat_interface.h" #include "storage/dr_mem.h" #include #include /*============================================================================== * @section DEFINES Constants *============================================================================*/ #define err_code1 65535 /* length error */ #define err_code2 65534 /* activate error */ #define err_code3 65533 /* param error */ #define err_code4 65532 /* ? missing */ #define err_code5 65531 /* CMD wrong */ #define err_code6 65530 /* CRC wrong */ /*============================================================================== * @section EXTERN External Declarations *============================================================================*/ extern uint8_t m_reset_status; /* Note: SERIAL_NO, bond_data_delete, m_static_passkey, m_pd_adc_cnt, m_pd_delay_us, m_life_cycle are declared in device_config.h */ /* Defined in measurements.h */ extern uint16_t led_pd_dac_v[]; /* Defined in main.c */ extern char ble_tx_buffer[]; extern volatile bool processing; extern bool ble_got_new_data; /* BLE buffer (defined in ble_data_tx.c) */ extern uint8_t ble_bin_buffer[]; /* Parser globals (defined in parser.c) */ extern dr_platform_if_t g_plat; extern bool g_log_enable; /*============================================================================== * @section GLOBALS Global Variables (used by other modules) *============================================================================*/ uint8_t resetCount = 0; /**< Reset counter */ bool info4 = false; /**< Additional info flag for PD Full mode */ /*============================================================================== * @section STATIC Static Variables *============================================================================*/ static uint32_t processing_start_tick = 0; /*============================================================================== * @section UTIL Utility Functions *============================================================================*/ /** * @brief Internal log function for parser */ static void log_printf(const char *fmt, ...) { va_list args; va_start(args, fmt); vprintf(fmt, args); va_end(args); } /*============================================================================== * @section CRC CRC Functions *============================================================================*/ bool crc16_check(uint8_t const *p_data, uint32_t data_len, uint16_t expected_crc) { uint16_t computed_crc = crc16_compute(p_data, data_len, NULL); return (computed_crc == expected_crc); } bool crc16_check_packet(uint8_t const *packet, uint32_t packet_len) { if (packet_len < 2) return false; uint32_t data_len = packet_len - 2; uint16_t expected_crc = (packet[packet_len - 1] << 8) | packet[packet_len - 2]; return crc16_check(packet, data_len, expected_crc); } /*============================================================================== * @section ERROR Error Handling Functions *============================================================================*/ bool length_error(const char *cmd, uint8_t target_length, uint8_t length) { if (target_length == length) { return true; } char resp_error[4]; resp_error[0] = 'r'; resp_error[1] = cmd[1]; resp_error[2] = cmd[2]; resp_error[3] = '!'; single_format_data(ble_bin_buffer, resp_error, err_code1); binary_tx_handler(ble_bin_buffer, 3); return false; } bool activate_error(const char *cmd, bool device_status) { if (device_status == true) { return true; } char resp_error[4]; resp_error[0] = 'r'; resp_error[1] = cmd[1]; resp_error[2] = cmd[2]; resp_error[3] = '!'; single_format_data(ble_bin_buffer, resp_error, err_code2); binary_tx_handler(ble_bin_buffer, 3); return false; } void param_error(const char *cmd) { char resp_error[4]; resp_error[0] = 'r'; resp_error[1] = cmd[1]; resp_error[2] = cmd[2]; resp_error[3] = '!'; single_format_data(ble_bin_buffer, resp_error, err_code3); binary_tx_handler(ble_bin_buffer, 3); } void quest_error(const char *cmd) { char resp_error[4]; const char pass_init[6] = "123456"; if ((cmd[0] == '*') && (cmd[1] == '*') && (cmd[2] == '*') && (cmd[3] == '*')) { if (dr_memWrite("passkey", (uint8_t *)pass_init, 6) != NRF_SUCCESS) { DBG_PRINTF("ERR!!! passkey write fail\r\n\r\n"); } resp_error[0] = '*'; resp_error[1] = cmd[1]; resp_error[2] = cmd[2]; resp_error[3] = '*'; single_format_data(ble_bin_buffer, resp_error, err_code4); binary_tx_handler(ble_bin_buffer, 3); } else { resp_error[0] = 'r'; resp_error[1] = cmd[1]; resp_error[2] = cmd[2]; resp_error[3] = '!'; single_format_data(ble_bin_buffer, resp_error, err_code4); binary_tx_handler(ble_bin_buffer, 3); } } /*============================================================================== * @section INIT_READ Initial Values Read (via dr_mem API) *============================================================================*/ ret_code_t eeprom_init_values_read(void) { ret_code_t err_code; /* FDS entries: Serial Number (12B, AES) */ err_code = dr_memRead("serial_no", (uint8_t *)SERIAL_NO, 12); if (err_code != NRF_SUCCESS) return err_code; DBG_PRINTF("\r\n SN:%s \r\n", SERIAL_NO); /* FDS entries: Passkey (6B, AES) */ err_code = dr_memRead("passkey", (uint8_t *)m_static_passkey, 6); if (err_code != NRF_SUCCESS) return err_code; DBG_PRINTF("\r\n passkey-0 :%s \n", m_static_passkey); /* FDS entries: Bond data delete (1B) */ { uint8_t raw = 0; err_code = dr_memRead("bond_delete", &raw, 1); if (err_code != NRF_SUCCESS) return err_code; bond_data_delete = (raw != 0); } DBG_PRINTF("\r\n bond_data_delete :%d \n", bond_data_delete); /* FDS entries: Reset status (1B) */ err_code = dr_memRead("reset_status", &m_reset_status, 1); if (err_code != NRF_SUCCESS) return err_code; /* FDS entries: PD ADC count (1B) */ err_code = dr_memRead("pd_adc_cnt", &m_pd_adc_cnt, 1); if (err_code != NRF_SUCCESS) return err_code; if (m_pd_adc_cnt == 0 || m_pd_adc_cnt >= 255) { m_pd_adc_cnt = 8; } /* FDS entries: PD delay (2B) */ err_code = dr_memRead("pd_delay", &m_pd_delay_us, 2); if (err_code != NRF_SUCCESS) return err_code; if (m_pd_delay_us < 5000 || m_pd_delay_us > 30000) { m_pd_delay_us = 8000; } /* W25Q32 entries: AGC Gain array (96B) */ err_code = dr_memRead("agc_gain", led_pd_dac_v, 96); if (err_code != NRF_SUCCESS) return err_code; /* W25Q32 entries: Life cycle (4B) */ err_code = dr_memRead("life_cycle", &m_life_cycle, 4); if (err_code != NRF_SUCCESS) return err_code; DBG_PRINTF("\r\n m_life_cycle:%u, m_pd_delay_us:%u, m_pd_adc_cnt:%u \r\n", m_life_cycle, m_pd_delay_us, m_pd_adc_cnt); return NRF_SUCCESS; } /*============================================================================== * @section API Main Entry Point *============================================================================*/ /** * @brief Process received command from UART or BLE * @details Initializes parser on first call, then delegates to dr_cmd_parser */ void received_command_process(uint8_t const *data_array, which_cmd_t cmd_t, uint8_t length) { uint8_t r_data[BLE_NUS_MAX_DATA_LEN] = {0}; int parser_result; (void)cmd_t; /* Currently not used by new parser */ ble_got_new_data = true; memset(ble_tx_buffer, 0, BLE_NUS_MAX_DATA_LEN); /* Copy data to local buffer */ for (uint16_t i = 0; i < length; i++) { r_data[i] = data_array[i]; } DBG_PRINTF("data : %s\r\n", r_data); DBG_PRINTF("Length : %d\r\n", length); /* Initialize parser (once) */ static bool parser_initialized = false; if (!parser_initialized) { g_plat.log = log_printf; g_plat.tx_bin = binary_tx_handler; g_plat.crc_check = true; /* CRC enabled */ g_log_enable = true; parser_initialized = true; DBG_PRINTF(">>> Parser initialized (mt_parser)\r\n"); } /* Check if already processing */ if (processing == true) { uint32_t now = app_timer_cnt_get(); if (processing_start_tick == 0) { processing_start_tick = now; } /* Timeout check (5 seconds) */ if (app_timer_cnt_diff_compute(now, processing_start_tick) > APP_TIMER_TICKS(5000)) { processing = false; processing_start_tick = 0; DBG_PRINTF("processing timeout -> force reset to false\r\n"); } else { DBG_PRINTF("Busy - command ignored\r\n"); return; } } /* Call mt_parser */ parser_result = dr_cmd_parser(r_data, length); if (parser_result > 0) { DBG_PRINTF(">>> Handled by mt_parser (result=%d)\r\n", parser_result); } else if (parser_result == 9) { DBG_PRINTF(">>> CRC/Parse error\r\n"); } else { DBG_PRINTF(">>> Unknown command\r\n"); } }