diff --git a/src/flash_config/flash_vars.c b/src/flash_config/flash_vars.c new file mode 100644 index 000000000..5cc90326d --- /dev/null +++ b/src/flash_config/flash_vars.c @@ -0,0 +1,479 @@ +/* + This module saves variable data to a flash region in an erase effient way. + + Design: + variables to be small - we want as many writes between erases as possible. + sector will be all FF (erased), and data added into it. + reading consists of searching the first non FF byte from the end, then + reading the preceding bytes as the variables. + last byte of data is len (!== 0xFF!) + +*/ + + +#include "include.h" +#include "mem_pub.h" +#include "drv_model_pub.h" +#include "net_param_pub.h" +#include "flash_pub.h" + +#if WINDOWS +#elif PLATFORM_XR809 +#else + #include "BkDriverFlash.h" + #include "BkDriverUart.h" +#endif + +#include "../logging/logging.h" +#include "flash_vars.h" + + +//#define TEST_MODE +//#define debug_delay(x) rtos_delay_milliseconds(x) +#define debug_delay(x) + +#define FLASH_VARS_MAGIC 0xfefefefe +// NOTE: Changed below according to partitions in SDK!!!! +static unsigned int flash_vars_start = 0x1e3000; //0x1e1000 + 0x1000 + 0x1000; // after netconfig and mystery SSID +static unsigned int flash_vars_len = 0x2000; // two blocks in BK7231 +static unsigned int flash_vars_sector_len = 0x1000; // erase size in BK7231 + +FLASH_VARS_STRUCTURE flash_vars; +int flash_vars_offset = 0; // offset to first FF in our area +static int flash_vars_initialised = 0; // offset to first FF in our area + +static int flash_vars_valid(); +static int flash_vars_write_magic(); +static int _flash_vars_write(void *data, unsigned int off_set, unsigned int size); +static int flash_vars_erase(unsigned int off_set, unsigned int size); +int flash_vars_read(FLASH_VARS_STRUCTURE *data); + +#if WINDOWS + #define TEST_MODE +#elif PLATFORM_XR809 + #define TEST_MODE +#else + +#endif + +#ifdef TEST_MODE + +static char test_flash_area[0x2000]; + +#endif + + +static void alignOffset(int *offs){ + // do nothing - should work with any offsets + /* + if ((*offs) % 32){ + (*offs) += (32- ((*offs)%32)); + }*/ +} + + +// initialise and read variables from flash +int flash_vars_init(){ +#if WINDOWS +#elif PLATFORM_XR809 +#else + bk_logic_partition_t *pt; +#endif + //direct_serial_log = 1; + + if (!flash_vars_initialised){ + ADDLOG_DEBUG(LOG_FEATURE_CFG, "flash vars not initialised - reading"); + debug_delay(200); + + +#if WINDOWS +#elif PLATFORM_XR809 +#else + pt = bk_flash_get_info(BK_PARTITION_NET_PARAM); + // there is an EXTRA sctor used for some form of wifi? + // on T variety, this is 0x1e3000 + flash_vars_start = pt->partition_start_addr + pt->partition_length + 0x1000; + flash_vars_len = 0x2000; // two blocks in BK7231 + flash_vars_sector_len = 0x1000; // erase size in BK7231 +#endif + //ADDLOG_DEBUG(LOG_FEATURE_CFG, "got part info"); + debug_delay(200); + + os_memset(&flash_vars, 0, sizeof(flash_vars)); + flash_vars.len = sizeof(flash_vars); + + //ADDLOG_DEBUG(LOG_FEATURE_CFG, "cleared structure"); + debug_delay(200); + // read any existing + flash_vars_read(&flash_vars); + flash_vars_initialised = 1; + //ADDLOG_DEBUG(LOG_FEATURE_CFG, "read structure"); + debug_delay(200); + } else { + //ADDLOG_DEBUG(LOG_FEATURE_CFG, "flash vars already initialised"); + + } + return 0; +} + +static int flash_vars_valid(){ + //uint32_t i; + //uint32_t param; + UINT32 status; +#ifndef TEST_MODE + DD_HANDLE flash_hdl; +#endif + uint32_t start_addr; + unsigned int tmp = 0xffffffff; + GLOBAL_INT_DECLARATION(); + //ADDLOG_DEBUG(LOG_FEATURE_CFG, "flash_vars_valid()"); + debug_delay(200); + +#ifndef TEST_MODE + flash_hdl = ddev_open(FLASH_DEV_NAME, &status, 0); + ASSERT(DD_HANDLE_UNVALID != flash_hdl); + bk_flash_enable_security(FLASH_PROTECT_NONE); +#endif + start_addr = flash_vars_start; + + //ADDLOG_DEBUG(LOG_FEATURE_CFG, "flash_vars_valid() flash open"); + debug_delay(200); + +#ifdef TEST_MODE + os_memcpy(&tmp, &test_flash_area[start_addr - flash_vars_start], sizeof(tmp)); +#else + GLOBAL_INT_DISABLE(); + ddev_read(flash_hdl, (char *)&tmp, sizeof(tmp), start_addr); + GLOBAL_INT_RESTORE(); + ddev_close(flash_hdl); + bk_flash_enable_security(FLASH_PROTECT_ALL); +#endif + //ADDLOG_DEBUG(LOG_FEATURE_CFG, "flash_vars_valid() copied header 0x%x", tmp); + debug_delay(200); + + // if not our magic, then erase and write magic + if (tmp != FLASH_VARS_MAGIC){ + ADDLOG_ERROR(LOG_FEATURE_CFG, "flash_vars_valid - not our magic, erase"); + debug_delay(200); + if (flash_vars_write_magic() >= 0){ + ADDLOG_DEBUG(LOG_FEATURE_CFG, "flash initialised"); + } else { + ADDLOG_ERROR(LOG_FEATURE_CFG, "flash initialise failed"); + return -1; + } + return 0; + } + return 1; +} + +static int flash_vars_write_magic(){ + //uint32_t i; + //uint32_t param; + UINT32 status; +#ifndef TEST_MODE + DD_HANDLE flash_hdl; +#endif + uint32_t start_addr; + unsigned int tmp = FLASH_VARS_MAGIC; + GLOBAL_INT_DECLARATION(); + //ADDLOG_DEBUG(LOG_FEATURE_CFG, "flash_vars_magic write"); + debug_delay(200); + + start_addr = flash_vars_start; + //ADDLOG_DEBUG(LOG_FEATURE_CFG, "flash open"); + debug_delay(200); + + if (flash_vars_erase(0, flash_vars_len) >= 0){ + ADDLOG_DEBUG(LOG_FEATURE_CFG, "flash erased"); + debug_delay(200); + } else { + ADDLOG_ERROR(LOG_FEATURE_CFG, "flash erase failed"); + debug_delay(200); + return -1; + } +#ifdef TEST_MODE + os_memcpy(&test_flash_area[start_addr - flash_vars_start], &tmp, sizeof(tmp)); +#else + bk_flash_enable_security(FLASH_PROTECT_NONE); + + flash_hdl = ddev_open(FLASH_DEV_NAME, &status, 0); + ASSERT(DD_HANDLE_UNVALID != flash_hdl); + GLOBAL_INT_DISABLE(); + ddev_write(flash_hdl, (char *)&tmp, sizeof(tmp), start_addr); + GLOBAL_INT_RESTORE(); + ddev_close(flash_hdl); + bk_flash_enable_security(FLASH_PROTECT_ALL); +#endif + ADDLOG_DEBUG(LOG_FEATURE_CFG, "header written"); + // next write point + flash_vars_offset = sizeof(tmp); + alignOffset(&flash_vars_offset); + + ADDLOG_DEBUG(LOG_FEATURE_CFG, "header written %d", flash_vars_offset); + debug_delay(200); + + // write a new structure + flash_vars_write(); + + return 0; +} + + +// read data from flash vars area. +// design: +// search from end of flash until we find a non-zero byte. +// this is length of existing data. +// clear structure to 00 +// read existing data (excluding len) into structure. +// set len to current structure defn len. +// remember first FF byte offset +int flash_vars_read(FLASH_VARS_STRUCTURE *data){ + //uint32_t i; + //uint32_t param; + UINT32 status; +#ifndef TEST_MODE + DD_HANDLE flash_hdl; +#endif + uint32_t start_addr; + int loops = 0x2100/4; + unsigned int tmp = 0xffffffff; + GLOBAL_INT_DECLARATION(); + //ADDLOG_DEBUG(LOG_FEATURE_CFG, "flash_vars_read len %d", sizeof(*data)); + + // check for magic, and reset sector(s) if not. + if (flash_vars_valid() >= 0){ + //ADDLOG_DEBUG(LOG_FEATURE_CFG, "flash vars valid or initialised"); + } else { + ADDLOG_ERROR(LOG_FEATURE_CFG, "flash_vars_validity error"); + os_memset(data, 0, sizeof(*data)); + data->len = sizeof(*data); + debug_delay(200); + return -1; + } + debug_delay(200); + +#ifndef TEST_MODE + flash_hdl = ddev_open(FLASH_DEV_NAME, &status, 0); + ASSERT(DD_HANDLE_UNVALID != flash_hdl); + bk_flash_enable_security(FLASH_PROTECT_NONE); +#endif + start_addr = flash_vars_start + flash_vars_len; + + do { + start_addr -= sizeof(tmp); +#ifdef TEST_MODE + os_memcpy(&tmp, &test_flash_area[start_addr - flash_vars_start], sizeof(tmp)); +#else + GLOBAL_INT_DISABLE(); + ddev_read(flash_hdl, (char *)&tmp, sizeof(tmp), start_addr); + GLOBAL_INT_RESTORE(); +#endif + } while ((tmp == 0xFFFFFFFF) && (start_addr > flash_vars_start + 4) && (loops--)); + if (!loops){ + ADDLOG_ERROR(LOG_FEATURE_CFG, "loops over addr 0x%X", start_addr); + } + + //ADDLOG_DEBUG(LOG_FEATURE_CFG, "found at %u 0x%X", start_addr, tmp); + start_addr += sizeof(tmp); + debug_delay(200); + + if (tmp == 0xffffffff){ + // no data found, all erased + // clear result. + os_memset(data, 0, sizeof(*data)); + // set the len to the latest revision's len + data->len = sizeof(*data); +#ifndef TEST_MODE + ddev_close(flash_hdl); + bk_flash_enable_security(FLASH_PROTECT_ALL); +#endif + ADDLOG_INFO(LOG_FEATURE_CFG, "new flash vars"); + return 0; + } else { + int shifts = 0; + int len = 0; + while ((tmp & 0xFF000000) == 0xFF000000){ + tmp <<= 8; + shifts ++; + } + len = (tmp >> 24) & 0xff; + start_addr -= shifts; + start_addr -= len; + + if (len > sizeof(*data)){ + ADDLOG_ERROR(LOG_FEATURE_CFG, "len (%d) in flash_var greater than current structure len (%d)", len, sizeof(*data)); +#ifndef TEST_MODE + ddev_close(flash_hdl); + bk_flash_enable_security(FLASH_PROTECT_ALL); +#endif + // erase it all and write a new one + flash_vars_write_magic(); + + } else { + // clear result. + os_memset(data, 0, sizeof(*data)); + // read the DATA portion into the structure +#ifdef TEST_MODE + os_memcpy(data, &test_flash_area[start_addr - flash_vars_start], len-1); +#else + GLOBAL_INT_DISABLE(); + ddev_read(flash_hdl, (char *)data, len-1, start_addr); + GLOBAL_INT_RESTORE(); + ddev_close(flash_hdl); + bk_flash_enable_security(FLASH_PROTECT_ALL); +#endif + // set the len to the latest revision's len + data->len = sizeof(*data); + flash_vars_offset = (start_addr - flash_vars_start) + len; + alignOffset(&flash_vars_offset); + + ADDLOG_DEBUG(LOG_FEATURE_CFG, "new offset after read %d, boot_count %d, success count %d", + flash_vars_offset, + data->boot_count, + data->boot_success_count + ); + + } + return 1; + } +} + + +int flash_vars_write(){ + //ADDLOG_DEBUG(LOG_FEATURE_CFG, "flash vars write"); + flash_vars_init(); + alignOffset(&flash_vars_offset); + + FLASH_VARS_STRUCTURE *data = &flash_vars; + if (flash_vars_offset + data->len > flash_vars_len){ + if (flash_vars_write_magic() >= 0){ + ADDLOG_ERROR(LOG_FEATURE_CFG, "flash reinitialised"); + } else { + ADDLOG_ERROR(LOG_FEATURE_CFG, "flash reinitialise failed"); + return -1; + } + } + + //ADDLOG_DEBUG(LOG_FEATURE_CFG, "flash vars write at offset %d len %d", flash_vars_offset, data->len); + _flash_vars_write(data, flash_vars_offset, data->len); + flash_vars_offset += data->len; + alignOffset(&flash_vars_offset); + + ADDLOG_DEBUG(LOG_FEATURE_CFG, "new offset %d, boot_count %d, success count %d", + flash_vars_offset, + data->boot_count, + data->boot_success_count + ); + return 1; +} + + +// write updated data to flash vars area. +// off_set is zero based. size in bytes +// TODO - test we CAN write at a byte boundary? +// answer - the flash driver in theroy deals with than... writes are always in chunks of 32 bytes +// on 32 byte boundaries. +int _flash_vars_write(void *data, unsigned int off_set, unsigned int size){ + //uint32_t i; + //uint32_t param; + UINT32 status; +#ifndef TEST_MODE + DD_HANDLE flash_hdl; +#endif + uint32_t start_addr; + GLOBAL_INT_DECLARATION(); + //ADDLOG_DEBUG(LOG_FEATURE_CFG, "_flash vars write offset %d, size %d", off_set, size); + +#ifndef TEST_MODE + flash_hdl = ddev_open(FLASH_DEV_NAME, &status, 0); + ASSERT(DD_HANDLE_UNVALID != flash_hdl); + bk_flash_enable_security(FLASH_PROTECT_NONE); +#endif + start_addr = flash_vars_start + off_set; + + if (start_addr <= flash_vars_start){ + ADDLOG_ERROR(LOG_FEATURE_CFG, "_flash vars write invalid addr 0x%X", start_addr); + ddev_close(flash_hdl); + bk_flash_enable_security(FLASH_PROTECT_ALL); + return -1; + } + if (start_addr + size > flash_vars_start + flash_vars_len){ + ADDLOG_ERROR(LOG_FEATURE_CFG, "_flash vars write invalid addr 0x%X len 0x%X", start_addr, size); + ddev_close(flash_hdl); + bk_flash_enable_security(FLASH_PROTECT_ALL); + return -1; + } + +#ifdef TEST_MODE + os_memcpy(&test_flash_area[start_addr - flash_vars_start], data, size); +#else + GLOBAL_INT_DISABLE(); + ddev_write(flash_hdl, data, size, start_addr); + GLOBAL_INT_RESTORE(); + ddev_close(flash_hdl); + bk_flash_enable_security(FLASH_PROTECT_ALL); +#endif + + //ADDLOG_DEBUG(LOG_FEATURE_CFG, "_flash vars write wrote offset %d, size %d", off_set, size); + + return 0; +} + +// erase one or more of the sectors we are using. +// off_set is zero based. size in bytes +// in theory, can't erase outside of OUR area. +int flash_vars_erase(unsigned int off_set, unsigned int size){ + uint32_t i; + uint32_t param; + UINT32 status; +#ifndef TEST_MODE + DD_HANDLE flash_hdl; +#endif + uint32_t start_sector, end_sector; + GLOBAL_INT_DECLARATION(); + ADDLOG_DEBUG(LOG_FEATURE_CFG, "flash vars erase at offset %d len %d", off_set, size); + +#ifndef TEST_MODE + flash_hdl = ddev_open(FLASH_DEV_NAME, &status, 0); + ASSERT(DD_HANDLE_UNVALID != flash_hdl); + bk_flash_enable_security(FLASH_PROTECT_NONE); +#endif + start_sector = off_set >> 12; + end_sector = (off_set + size - 1) >> 12; + + for(i = start_sector; i <= end_sector; i ++) + { + param = flash_vars_start + (i << 12); + if (param < flash_vars_start){ + ADDLOG_ERROR(LOG_FEATURE_CFG, "flash vars erase invalid addr 0x%X < 0x%X", param, flash_vars_start); +#ifndef TEST_MODE + ddev_close(flash_hdl); + bk_flash_enable_security(FLASH_PROTECT_ALL); +#endif + return -1; + } + if (param + flash_vars_sector_len > flash_vars_start + flash_vars_len){ + ADDLOG_ERROR(LOG_FEATURE_CFG, "flash vars erase invalid addr 0x%X+0x%X > 0x%X", param, flash_vars_sector_len, flash_vars_start + flash_vars_len); +#ifndef TEST_MODE + ddev_close(flash_hdl); + bk_flash_enable_security(FLASH_PROTECT_ALL); +#endif + return -1; + } + ADDLOG_DEBUG(LOG_FEATURE_CFG, "flash vars erase block at addr 0x%X", param); +#ifdef TEST_MODE + os_memset(&test_flash_area[param - flash_vars_start], 0xff, 0x1000); +#else + GLOBAL_INT_DISABLE(); + ddev_control(flash_hdl, CMD_FLASH_ERASE_SECTOR, (void *)¶m); + GLOBAL_INT_RESTORE(); +#endif + } +#ifndef TEST_MODE + ddev_close(flash_hdl); + bk_flash_enable_security(FLASH_PROTECT_ALL); +#endif + //ADDLOG_DEBUG(LOG_FEATURE_CFG, "flash vars erase end"); + + return 0; +} \ No newline at end of file diff --git a/src/flash_config/flash_vars.h b/src/flash_config/flash_vars.h new file mode 100644 index 000000000..7924c7853 --- /dev/null +++ b/src/flash_config/flash_vars.h @@ -0,0 +1,21 @@ + + + + +typedef struct flash_vars_structure +{ + unsigned short boot_count; // number of times the device has booted + unsigned short boot_success_count; // if a device boots completely (>30s), will equal boot_success_count + unsigned char _align[3]; + unsigned char len; // length of the whole structure (i.e. 2+2+1 = 5) MUST NOT BE 255 +} FLASH_VARS_STRUCTURE; + +extern FLASH_VARS_STRUCTURE flash_vars; + + +int flash_vars_init(); +int flash_vars_write(); + +// for testing only... only done once at startup... +extern int flash_vars_offset; +int flash_vars_read(FLASH_VARS_STRUCTURE *data); diff --git a/src/flash_config/flash_vars_vars.c b/src/flash_config/flash_vars_vars.c new file mode 100644 index 000000000..83ca7ab34 --- /dev/null +++ b/src/flash_config/flash_vars_vars.c @@ -0,0 +1,54 @@ + +#include "flash_vars.h" +#include "../logging/logging.h" + + +//#define DISABLE_FLASH_VARS_VARS + + +// call at startup +void increment_boot_count(){ +#ifndef DISABLE_FLASH_VARS_VARS + FLASH_VARS_STRUCTURE data; + + flash_vars_init(); + flash_vars.boot_count ++; + ADDLOG_INFO(LOG_FEATURE_CFG, "####### Boot Count %d #######", flash_vars.boot_count); + flash_vars_write(); + + flash_vars_read(&data); + ADDLOG_DEBUG(LOG_FEATURE_CFG, "re-read - offset %d, boot count %d, boot success %d, bootfailures %d", + flash_vars_offset, + data.boot_count, + data.boot_success_count, + data.boot_count - data.boot_success_count ); +#endif +} + +// call once started (>30s?) +void boot_complete(){ +#ifndef DISABLE_FLASH_VARS_VARS + FLASH_VARS_STRUCTURE data; + // mark that we have completed a boot. + ADDLOG_INFO(LOG_FEATURE_CFG, "####### Set Boot Complete #######"); + + flash_vars.boot_success_count = flash_vars.boot_count; + flash_vars_write(); + + flash_vars_read(&data); + ADDLOG_DEBUG(LOG_FEATURE_CFG, "re-read - offset %d, boot count %d, boot success %d, bootfailures %d", + flash_vars_offset, + data.boot_count, + data.boot_success_count, + data.boot_count - data.boot_success_count ); +#endif +} + +// call to return the number of boots since a boot_complete +int boot_failures(){ + int diff = 0; +#ifndef DISABLE_FLASH_VARS_VARS + diff = flash_vars.boot_count - flash_vars.boot_success_count; +#endif + return diff; +} \ No newline at end of file diff --git a/src/flash_config/flash_vars_vars.h b/src/flash_config/flash_vars_vars.h new file mode 100644 index 000000000..6f50f4413 --- /dev/null +++ b/src/flash_config/flash_vars_vars.h @@ -0,0 +1,12 @@ + + +//#define DISABLE_FLASH_VARS_VARS + +#define BOOT_COMPLETE_SECONDS 30 + +// call at startup +void increment_boot_count(); +// call once started (>30s?) +void boot_complete(); +// call to return the number of boots since a boot_complete +int boot_failures(); \ No newline at end of file diff --git a/src/httpserver/rest_interface.c b/src/httpserver/rest_interface.c index 3aeb6f6ee..f179c0991 100644 --- a/src/httpserver/rest_interface.c +++ b/src/httpserver/rest_interface.c @@ -14,6 +14,8 @@ #include "lwip/sockets.h" #include "../flash_config/flash_config.h" #include "../new_cfg.h" +#include "../flash_config/flash_vars_vars.h" +#include "../flash_config/flash_vars.h" extern UINT32 flash_read(char *user_buf, UINT32 count, UINT32 address); @@ -51,6 +53,10 @@ static int http_rest_get_testconfig(http_request_t *request); static int http_rest_post_channels(http_request_t *request); static int http_rest_get_channels(http_request_t *request); +static int http_rest_get_flash_vars_test(http_request_t *request); + + + void init_rest(){ HTTP_RegisterCallback( "/api/", HTTP_GET, http_rest_get); HTTP_RegisterCallback( "/api/", HTTP_POST, http_rest_post); @@ -137,6 +143,11 @@ static int http_rest_get(http_request_t *request){ if (!strcmp(request->url, "api/testconfig")){ return http_rest_get_testconfig(request); } + + if (!strncmp(request->url, "api/testflashvars", 17)){ + return http_rest_get_flash_vars_test(request); + } + @@ -874,6 +885,45 @@ static int http_rest_get_testconfig(http_request_t *request){ return 0; } +static int http_rest_get_flash_vars_test(http_request_t *request){ +#ifndef DISABLE_FLASH_VARS_VARS + char *params = request->url + 17; + int increment = 0; + int len = 0; + int sres; + int i; + char tmp[128]; + FLASH_VARS_STRUCTURE data, *p; + + p = &flash_vars; + + sres = sscanf(params, "%x-%x", &increment, &len); + + ADDLOG_DEBUG(LOG_FEATURE_API, "http_rest_get_flash_vars_test %d %d returned %d", increment, len, sres); + + if (increment == 10){ + flash_vars_read(&data); + p = &data; + } else { + for (i = 0; i < increment; i++){ + increment_boot_count(); + } + for (i = 0; i < len; i++){ + boot_complete(); + } + } + + sprintf(tmp, "offset %d, boot count %d, boot success %d, bootfailures %d", + flash_vars_offset, + p->boot_count, + p->boot_success_count, + p->boot_count - p->boot_success_count ); + + return http_rest_error(request, 200, tmp); +#else + return http_rest_error(request, 400, "flash vars unsupported"); +#endif +} static int http_rest_get_channels(http_request_t *request){ diff --git a/src/user_main.c b/src/user_main.c index bfc2a5e83..65faf6e62 100644 --- a/src/user_main.c +++ b/src/user_main.c @@ -50,6 +50,7 @@ #include "littlefs/our_lfs.h" #include "flash_config/flash_config.h" +#include "flash_config/flash_vars_vars.h" #undef Malloc #undef Free @@ -164,6 +165,10 @@ static void app_led_timer_handler(void *data) print_network_info(); } + // when we hit 30s, mark as boot complete. + if (g_secondsElapsed == BOOT_COMPLETE_SECONDS){ + boot_complete(); + } if (g_openAP){ g_openAP--; @@ -320,9 +325,11 @@ void user_main(void) { OSStatus err; int bForceOpenAP = 0; + int bootFailures = 0; const char *wifi_ssid, *wifi_pass; - //OPERATE_RET op_ret = OPRT_OK; + // read or initialise the boot count flash area + increment_boot_count(); CFG_InitAndLoad(); @@ -341,6 +348,14 @@ void user_main(void) // you can use this if you bricked your module by setting wrong access point data bForceOpenAP = 1; #endif + + + bootFailures = boot_failures(); + if (bootFailures > 3){ + bForceOpenAP = 1; + ADDLOG_INFO(LOG_FEATURE_MAIN, "###### would force AP mode - boot failures %d", bootFailures); + } + if(*wifi_ssid == 0 || *wifi_pass == 0 || bForceOpenAP) { // start AP mode in 5 seconds g_openAP = 5;