/******************************************************************************* * @file device_config.c * @brief Device Configuration Management * @author Charles KWON * @date 2025-01-30 * @copyright (c) 2025 Medithings Inc. All rights reserved. * * @details EEPROM configuration loading and default values. ******************************************************************************/ #include "device_config.h" #include "nrf_delay.h" #include "debug_print.h" #include "cat_interface.h" #include #include #include "storage/dr_mem.h" #include "fstorage.h" /*============================================================================== * GLOBAL VARIABLES *============================================================================*/ char m_static_passkey[7] = "123456"; char SERIAL_NO[16] = "2025VIVAM00001"; char HW_NO[12] = "HW00000001"; bool bond_data_delete = true; uint8_t m_pd_adc_cnt = 8; uint16_t m_pd_delay_us = 8000; uint32_t m_life_cycle = 0; uint16_t led_pd_dac_v[48]; /*============================================================================== * VALIDATION FUNCTIONS *============================================================================*/ bool is_valid_serial_no(const char *serial) { if (serial == NULL) return false; /* Check for reasonable serial number format */ for (int i = 0; i < 12; i++) { char c = serial[i]; if (c == 0) break; if (c == 0xFF) return false; /* Uninitialized EEPROM */ if (c < 0x20 || c > 0x7E) return false; /* Non-printable */ } return true; } bool is_valid_passkey(const char *passkey) { if (passkey == NULL) return false; /* Check for 6 digit passkey */ for (int i = 0; i < 6; i++) { char c = passkey[i]; if (c == 0xFF) return false; /* Uninitialized EEPROM */ if (c < '0' || c > '9') return false; /* Not a digit */ } return true; } /*============================================================================== * CONFIGURATION LOADING *============================================================================*/ void load_default_values(void) { /* Default Serial Number */ memset(SERIAL_NO, 0, sizeof(SERIAL_NO)); memcpy(SERIAL_NO, "2025AAAAT001", 12); /* Default Passkey */ memset(m_static_passkey, 0, sizeof(m_static_passkey)); memcpy(m_static_passkey, "123456", 6); /* Default measurement parameters */ m_pd_delay_us = 8000; m_pd_adc_cnt = 8; /* Default state */ bond_data_delete = 1; /* Default AGC Gain values */ for (int i = 0; i < 48; i++) { led_pd_dac_v[i] = 2048; } DBG_PRINTF("[CFG] Using default values\r\n"); } static bool is_valid_hw_no(const char *hw) { if (hw == NULL) return false; /* All 0x00 or 0xFF = uninitialized */ bool all_zero = true, all_ff = true; for (int i = 0; i < 12; i++) { if (hw[i] != 0x00) all_zero = false; if ((uint8_t)hw[i] != 0xFF) all_ff = false; } if (all_zero || all_ff) return false; /* Check printable ASCII */ for (int i = 0; i < 12; i++) { char c = hw[i]; if (c == 0) break; if (c < 0x20 || c > 0x7E) return false; } return true; } void load_device_configuration(void) { extern uint8_t m_reset_status; ret_code_t err_code; /* Start with RAM defaults, then override with FDS-saved values below. * dr_memSetDefaults() removed: it was clobbering FDS-loaded m_config, * wiping user-saved data (serial_no, passkey, etc.) on every boot * when hw_no was still all-zeros. */ load_default_values(); /* Show m_config state right after config_load (before dr_memRead) */ { char tmp[13] = {0}; memcpy(tmp, m_config.serial_no, 12); DBG_PRINTF("[CFG] FDS serial_no='%s'\r\n", tmp); memcpy(tmp, m_config.hw_no, 12); DBG_PRINTF("[CFG] FDS hw_no='%s'\r\n", tmp); } /* Read FDS entries (already loaded by config_load() into m_config) */ err_code = dr_memRead("serial_no", (uint8_t *)SERIAL_NO, 12); DBG_PRINTF("[CFG] dr_memRead S/N rc=%u valid=%d\r\n", err_code, is_valid_serial_no(SERIAL_NO)); DBG_PRINTF("[CFG] S/N hex: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\r\n", (uint8_t)SERIAL_NO[0], (uint8_t)SERIAL_NO[1], (uint8_t)SERIAL_NO[2], (uint8_t)SERIAL_NO[3], (uint8_t)SERIAL_NO[4], (uint8_t)SERIAL_NO[5], (uint8_t)SERIAL_NO[6], (uint8_t)SERIAL_NO[7], (uint8_t)SERIAL_NO[8], (uint8_t)SERIAL_NO[9], (uint8_t)SERIAL_NO[10], (uint8_t)SERIAL_NO[11]); if (err_code != NRF_SUCCESS || !is_valid_serial_no(SERIAL_NO)) { DBG_PRINTF("[CFG] Invalid S/N - using default\r\n"); memset(SERIAL_NO, 0, sizeof(SERIAL_NO)); memcpy(SERIAL_NO, "2025AAAAT001", 12); } err_code = dr_memRead("passkey", (uint8_t *)m_static_passkey, 6); if (err_code != NRF_SUCCESS || !is_valid_passkey(m_static_passkey)) { DBG_PRINTF("[CFG] Invalid passkey - using default\r\n"); memset(m_static_passkey, 0, sizeof(m_static_passkey)); memcpy(m_static_passkey, "123456", 6); } { uint8_t raw = 0; dr_memRead("bond_delete", &raw, 1); bond_data_delete = (raw != 0); } dr_memRead("reset_status", &m_reset_status, 1); if (m_reset_status > 2) { DBG_PRINTF("[CFG] Invalid reset_status (%u) - using 1\r\n", m_reset_status); m_reset_status = 1; } dr_memRead("pd_adc_cnt", &m_pd_adc_cnt, 1); if (m_pd_adc_cnt == 0 || m_pd_adc_cnt >= 255) { DBG_PRINTF("[CFG] Invalid adc_cnt (%u) - using 8\r\n", m_pd_adc_cnt); m_pd_adc_cnt = 8; } dr_memRead("pd_delay", &m_pd_delay_us, 2); if (m_pd_delay_us < 5000 || m_pd_delay_us > 30000) { DBG_PRINTF("[CFG] Invalid pd_delay (%u) - using 8000\r\n", m_pd_delay_us); m_pd_delay_us = 8000; } /* W25Q32 entries - NOT read during boot * W25Q32 is not initialized at boot (SPIM + BSP event conflict). * agc_gain and life_cycle use RAM defaults from load_default_values(). * Actual W25Q32 values are loaded later when W25Q32 is powered on * (via BLE command or measurement start). */ DBG_PRINTF("[CFG] Loaded: S/N=%s, delay=%u, cnt=%u, life=%u\r\n", SERIAL_NO, m_pd_delay_us, m_pd_adc_cnt, m_life_cycle); /* 3. config_data_t dump (FDS raw) */ { char tmp[13] = {0}; #define CFG_DLY() nrf_delay_ms(10) DBG_PRINTF("[CFG] === config_data_t (FDS) ===\r\n"); CFG_DLY(); DBG_PRINTF("[CFG] magic : 0x%08X\r\n", m_config.magic_number); CFG_DLY(); memcpy(tmp, m_config.hw_no, 12); tmp[12] = 0; DBG_PRINTF("[CFG] hw_no : %s\r\n", tmp); CFG_DLY(); memcpy(tmp, m_config.serial_no, 12); tmp[12] = 0; DBG_PRINTF("[CFG] serial : %s\r\n", tmp); CFG_DLY(); DBG_PRINTF("[CFG] passkey : %c%c%c%c%c%c\r\n", m_config.static_passkey[0], m_config.static_passkey[1], m_config.static_passkey[2], m_config.static_passkey[3], m_config.static_passkey[4], m_config.static_passkey[5]); CFG_DLY(); DBG_PRINTF("[CFG] bond_del: %u\r\n", m_config.bond_data_delete); CFG_DLY(); DBG_PRINTF("[CFG] reset_st: %d\r\n", m_config.reset_status); CFG_DLY(); DBG_PRINTF("[CFG] adc_cnt : %u\r\n", m_config.pd_adc_cnt); CFG_DLY(); DBG_PRINTF("[CFG] pd_delay: %u\r\n", m_config.pd_delay_us); CFG_DLY(); /* W25Q32 entries */ DBG_PRINTF("[CFG] agc_gain[48]:\r\n"); CFG_DLY(); for (int i = 0; i < 48; i += 8) { DBG_PRINTF(" [%2d] %u %u %u %u %u %u %u %u\r\n", i, led_pd_dac_v[i], led_pd_dac_v[i+1], led_pd_dac_v[i+2], led_pd_dac_v[i+3], led_pd_dac_v[i+4], led_pd_dac_v[i+5], led_pd_dac_v[i+6], led_pd_dac_v[i+7]); CFG_DLY(); } DBG_PRINTF("[CFG] life : %u\r\n", m_life_cycle); CFG_DLY(); DBG_PRINTF("[CFG] ========================\r\n"); #undef CFG_DLY } }