/****************************************************************************** * Copyright 2013-2014 Espressif Systems (Wuxi) * * FileName: user_main.c * * Description: entry file of user application * * Modification history: * 2014/1/1, v1.0 create this file. *******************************************************************************/ #include "lua.h" #include "platform.h" #include "c_string.h" #include "c_stdlib.h" #include "c_stdio.h" #include "vfs.h" #include "flash_api.h" #include "user_interface.h" #include "user_modules.h" #include "ets_sys.h" #include "driver/uart.h" #include "task/task.h" #include "mem.h" #include "espconn.h" #include "sections.h" #ifdef LUA_USE_MODULES_RTCTIME #include "rtc/rtctime.h" #endif static task_handle_t input_sig; static uint8 input_sig_flag = 0; /* Contents of esp_init_data_default.bin */ extern const uint32_t init_data[]; extern const uint32_t init_data_end[]; __asm__( ".align 4\n" "init_data: .incbin \"" ESP_INIT_DATA_DEFAULT "\"\n" "init_data_end:\n" ); extern const char _irom0_text_start[], _irom0_text_end[],_flash_used_end[]; #define IROM0_SIZE (_irom0_text_end - _irom0_text_start) #define INIT_DATA_SIZE (init_data_end - init_data) #define PRE_INIT_TEXT_ATTR __attribute__((section(".p3.pre_init"))) #define IROM_PTABLE_ATTR __attribute__((section(".irom0.ptable"))) #define PARTITION(n) (SYSTEM_PARTITION_CUSTOMER_BEGIN + n) #define SIZE_256K 0x00040000 #define SIZE_1024K 0x00100000 #define FLASH_BASE_ADDR ((char *) 0x40200000) //TODO: map the TLS server and client certs into NODEMCU_TLSCERT_PARTITION const partition_item_t partition_init_table[] IROM_PTABLE_ATTR = { { PARTITION(NODEMCU_EAGLEROM_PARTITION), 0x00000, 0x0B000}, { SYSTEM_PARTITION_RF_CAL, 0x0B000, 0x1000}, { SYSTEM_PARTITION_PHY_DATA, 0x0C000, 0x1000}, { SYSTEM_PARTITION_SYSTEM_PARAMETER, 0x0D000, 0x3000}, { PARTITION(NODEMCU_IROM0TEXT_PARTITION), 0x10000, 0x0000}, { PARTITION(NODEMCU_LFS0_PARTITION), 0x0, LUA_FLASH_STORE}, { PARTITION(NODEMCU_SPIFFS0_PARTITION), 0x0, SPIFFS_MAX_FILESYSTEM_SIZE}, {0,(uint32_t) &_irom0_text_end,0} }; // The following enum must maintain the partition table order enum partition {iram0=0, rf_call, phy_data, sys_parm, irom0, lfs, spiffs}; #define PTABLE_SIZE ((sizeof(partition_init_table)/sizeof(partition_item_t))-1) #define PT_CHUNK 0x8000 #define PT_ALIGN(n) ((n + (PT_CHUNK-1)) & (~((PT_CHUNK-1)))) /* * The non-OS SDK prolog has been fundamentally revised in V3. See SDK EN document * Partition Table.md for further discussion. This version of user_main.c is a * complete rework aligned to V3, with the redundant pre-V3 features removed. * * SDK V3 significantly reduces the RAM footprint required by the SDK and introduces * the use of a partition table (PT) to control flash allocation. The NodeMCU uses * this PT for overall allocation of its flash resources. A constant copy PT is * maintained at the start of IROM0 (flash offset 0x10000) -- see partition_init_table * declaration above -- to facilitate its modification either in the firmware binary * or in the flash itself. This is Flash PT used during startup to create the live PT * in RAM that is used by the SDK. * * Note that user_pre_init() runs with Icache enabled -- that is the IROM0 partition * is already mapped the address space at 0x40210000 and so that most SDK services * are available, such as system_get_flash_size_map() which returns the valid flash * size (including the 8Mb and 16Mb variants). * * We will be separately releasing a host PC-base python tool to configure the PT, * etc., but the following code will initialise the PT to sensible defaults even if * this tool isn't used. */ static int setup_partition_table(partition_item_t *pt, uint32_t *n) { // Flash size lookup is SIZE_256K*2^N where N is as follows (see SDK/user_interface.h) static char flash_size_scaler[] = /* 0 1 2 3 4 5 6 7 8 9 */ /* ½M ¼M 1M 2M 4M 2M 4M 4M 8M 16M */ "\001\000\002\003\004\003\004\004\005\006"; enum flash_size_map fs_size_code = system_get_flash_size_map(); uint32_t flash_size = SIZE_256K << flash_size_scaler[fs_size_code]; uint32_t first_free_flash_addr = partition_init_table[PTABLE_SIZE].addr - (uint32_t) FLASH_BASE_ADDR; int i,j; os_memcpy(pt, partition_init_table, PTABLE_SIZE * sizeof(*pt)); if (flash_size < SIZE_1024K) { os_printf("Flash size (%u) too small to support NodeMCU\n", flash_size); return -1; } else { os_printf("system SPI FI size:%u, Flash size: %u\n", fs_size_code, flash_size ); } // Calculate the runtime sized partitions // The iram0, rf_call, phy_data, sys_parm partitions are as-is. if (pt[irom0].size == 0) { pt[irom0].size = first_free_flash_addr - pt[irom0].addr; } if (pt[lfs].addr == 0) { pt[lfs].addr = PT_ALIGN(pt[irom0].addr + pt[irom0].size); os_printf("LFS base: %08X\n", pt[lfs].addr); } if (pt[lfs].size == 0) { pt[lfs].size = 0x10000; os_printf("LFS size: %08X\n", pt[lfs].size); } if (pt[spiffs].addr == 0) { pt[spiffs].addr = PT_ALIGN(pt[lfs].addr + pt[lfs].size); os_printf("SPIFFS base: %08X\n", pt[spiffs].addr); } if (pt[spiffs].size == SPIFFS_MAX_FILESYSTEM_SIZE) { pt[spiffs].size = flash_size - pt[spiffs].addr; os_printf("SPIFFS size: %08X\n", pt[spiffs].size); } // Check that the phys data partition has been initialised and if not then do this // now to prevent the SDK halting on a "rf_cal[0] !=0x05,is 0xFF" error. uint32_t init_data_hdr = 0xffffffff, data_addr = pt[phy_data].addr; int status = spi_flash_read(data_addr, &init_data_hdr, sizeof (uint32_t)); if (status == SPI_FLASH_RESULT_OK && *(char *)&init_data_hdr != 0x05) { uint32_t idata[INIT_DATA_SIZE]; os_printf("Writing Init Data to 0x%08x\n",data_addr); spi_flash_erase_sector(data_addr/SPI_FLASH_SEC_SIZE); os_memcpy(idata, init_data, sizeof(idata)); spi_flash_write(data_addr, idata, sizeof(idata)); os_delay_us(1000); } // Check partitions are page aligned and remove and any zero-length partitions. // This must be done last as this might break the enum partition ordering. for (i = 0, j = 0; i < PTABLE_SIZE; i++) { const partition_item_t *p = pt + i; if ((p->addr| p->size) & (SPI_FLASH_SEC_SIZE-1)) { os_printf("Partitions must be flash page aligned\n"); return -1; } if (p->size == 0) continue; if (j < i) { pt[j] = *p; p = pt + j; } os_printf("%2u: %08x %08x %08x\n", j, p->type, p->addr, p->size); j++; } *n = j; return fs_size_code; } void user_pre_init(void) { #ifdef LUA_USE_MODULES_RTCTIME // Note: Keep this as close to call_user_start() as possible, since it // is where the cpu clock actually gets bumped to 80MHz. rtctime_early_startup (); #endif static partition_item_t pt[PTABLE_SIZE]; uint32_t pt_size; uint32_t fs_size_code = setup_partition_table(pt, &pt_size); if( fs_size_code > 0 && system_partition_table_regist(pt, pt_size, fs_size_code)) { return; } os_printf("system_partition_table_regist fail (%u)\n", fs_size_code); while(1); } uint32 ICACHE_RAM_ATTR user_iram_memory_is_enabled(void) { return FALSE; // NodeMCU runs like a dog if iRAM is enabled } // +================== New task interface ==================+ static void start_lua(task_param_t param, uint8 priority) { char* lua_argv[] = { (char *)"lua", (char *)"-i", NULL }; NODE_DBG("Task task_lua started.\n"); lua_main( 2, lua_argv ); // Only enable UART interrupts once we've successfully started up, // otherwise the task queue might fill up with input events and prevent // the start_lua task from being posted. ETS_UART_INTR_ENABLE(); } static void handle_input(task_param_t flag, uint8 priority) { (void)priority; if (flag & 0x8000) { input_sig_flag = flag & 0x4000 ? 1 : 0; } lua_handle_input (flag & 0x01); } bool user_process_input(bool force) { return task_post_low(input_sig, force); } void nodemcu_init(void) { NODE_ERR("\n"); // Initialize platform first for lua modules. if( platform_init() != PLATFORM_OK ) { // This should never happen NODE_DBG("Can not init platform for modules.\n"); return; } #ifdef BUILD_SPIFFS if (!vfs_mount("/FLASH", 0)) { // Failed to mount -- try reformat dbg_printf("Formatting file system. Please wait...\n"); if (!vfs_format()) { NODE_ERR( "\n*** ERROR ***: unable to format. FS might be compromised.\n" ); NODE_ERR( "It is advised to re-flash the NodeMCU image.\n" ); } } #endif if (!task_post_low(task_get_id(start_lua),'s')) NODE_ERR("Failed to post the start_lua task!\n"); } #ifdef LUA_USE_MODULES_WIFI #include "../modules/wifi_common.h" void user_rf_pre_init(void) { //set WiFi hostname before RF initialization (adds ~479 us to boot time) wifi_change_default_host_name(); } #endif /****************************************************************************** * FunctionName : user_init * Description : entry of user application, init user function here * Parameters : none * Returns : none *******************************************************************************/ void user_init(void) { #ifdef LUA_USE_MODULES_RTCTIME rtctime_late_startup (); #endif UartBautRate br = BIT_RATE_DEFAULT; input_sig = task_get_id(handle_input); uart_init (br, br, input_sig, &input_sig_flag); #ifndef NODE_DEBUG system_set_os_print(0); #endif system_init_done_cb(nodemcu_init); } /* * The SDK now establishes exception handlers for EXCCAUSE errors: ILLEGAL, * INSTR_ERROR, LOAD_STORE_ERROR, PRIVILEGED, UNALIGNED, LOAD_PROHIBITED, * STORE_PROHIBITED. These handlers are established in SDK/app_main.c. * LOAD_STORE_ERROR is handled by SDK/user_exceptions.o:load_non_32_wide_handler() * which is a fork of our version. The remaining are handled by a static function * at SDK:app+main.c:offset 0x0348. * void __real__xtos_set_exception_handler (uint32_t cause, exception_handler_fn fn); void __wrap__xtos_set_exception_handler (uint32_t cause, exception_handler_fn fn) { os_printf("Exception handler %x %x\n", cause, fn); __real__xtos_set_exception_handler (cause, fn); } */