2020-04-10 17:24:08 +01:00
/*
support_esp32 . ino - ESP32 specific code for Tasmota
Copyright ( C ) 2020 Theo Arends / Jörg Schüler - Maroldt
This program is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
2020-04-19 15:58:13 +01:00
/*********************************************************************************************\
* ESP8266 Support
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# ifdef ESP8266
extern " C " {
extern struct rst_info resetInfo ;
}
2020-04-22 15:07:52 +01:00
uint32_t ESP_ResetInfoReason ( void ) {
2020-04-19 15:58:13 +01:00
return resetInfo . reason ;
}
2020-04-22 15:07:52 +01:00
String ESP_getResetReason ( void ) {
2020-04-19 15:58:13 +01:00
return ESP . getResetReason ( ) ;
}
2020-04-22 15:07:52 +01:00
uint32_t ESP_getChipId ( void ) {
2020-04-19 15:58:13 +01:00
return ESP . getChipId ( ) ;
}
2020-04-22 15:07:52 +01:00
uint32_t ESP_getSketchSize ( void ) {
2020-04-19 15:58:13 +01:00
return ESP . getSketchSize ( ) ;
}
2020-04-22 15:07:52 +01:00
uint32_t ESP_getFreeHeap ( void ) {
return ESP . getFreeHeap ( ) ;
}
2020-08-12 10:46:06 +01:00
uint32_t ESP_getMaxAllocHeap ( void ) {
2020-08-12 11:11:47 +01:00
/*
From libraries . rst
ESP . getMaxFreeBlockSize ( ) returns the largest contiguous free RAM block in
the heap , useful for checking heap fragmentation . * * NOTE : * * Maximum
` ` malloc ( ) ` ` able block will be smaller due to memory manager overheads .
From HeapMetric . ino
ESP . getMaxFreeBlockSize ( ) does not indicate the amount of memory that is
available for use in a single malloc call . It indicates the size of a
contiguous block of ( raw ) memory before the umm_malloc overhead is removed .
It should also be pointed out that , if you allow for the needed overhead in
your malloc call , it could still fail in the general case . An IRQ handler
could have allocated memory between the time you call
ESP . getMaxFreeBlockSize ( ) and your malloc call , reducing the available
memory .
*/
uint32_t free_block_size = ESP . getMaxFreeBlockSize ( ) ;
if ( free_block_size > 100 ) { free_block_size - = 100 ; }
return free_block_size ;
2020-08-12 10:46:06 +01:00
}
2020-04-22 15:07:52 +01:00
void ESP_Restart ( void ) {
2020-04-19 15:58:13 +01:00
// ESP.restart(); // This results in exception 3 on restarts on core 2.3.0
ESP . reset ( ) ;
}
2020-11-22 16:35:04 +00:00
uint32_t FlashWriteStartSector ( void ) {
return ( ESP . getSketchSize ( ) / SPI_FLASH_SEC_SIZE ) + 2 ; // Stay on the safe side
}
uint32_t FlashWriteMaxSector ( void ) {
return ( ( ( uint32_t ) & _FS_end - 0x40200000 ) / SPI_FLASH_SEC_SIZE ) - 2 ;
}
uint8_t * FlashDirectAccess ( void ) {
return ( uint8_t * ) ( 0x40200000 + ( FlashWriteStartSector ( ) * SPI_FLASH_SEC_SIZE ) ) ;
}
2020-04-19 15:58:13 +01:00
# endif
/*********************************************************************************************\
* ESP32 Support
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2020-04-10 17:24:08 +01:00
# ifdef ESP32
2020-07-05 13:51:55 +01:00
// Handle 20k of NVM
2020-04-10 17:24:08 +01:00
# include <nvs.h>
# include <rom/rtc.h>
2020-11-12 16:50:34 +00:00
# include <esp_phy_init.h>
2020-04-10 17:24:08 +01:00
2020-04-22 15:07:52 +01:00
void NvmLoad ( const char * sNvsName , const char * sName , void * pSettings , unsigned nSettingsLen ) {
2020-04-10 17:24:08 +01:00
nvs_handle handle ;
2020-04-19 14:36:04 +01:00
noInterrupts ( ) ;
nvs_open ( sNvsName , NVS_READONLY , & handle ) ;
size_t size = nSettingsLen ;
nvs_get_blob ( handle , sName , pSettings , & size ) ;
2020-04-10 17:24:08 +01:00
nvs_close ( handle ) ;
interrupts ( ) ;
}
2020-04-22 15:07:52 +01:00
void NvmSave ( const char * sNvsName , const char * sName , const void * pSettings , unsigned nSettingsLen ) {
2020-04-10 17:24:08 +01:00
nvs_handle handle ;
2020-04-19 14:36:04 +01:00
noInterrupts ( ) ;
nvs_open ( sNvsName , NVS_READWRITE , & handle ) ;
nvs_set_blob ( handle , sName , pSettings , nSettingsLen ) ;
nvs_commit ( handle ) ;
2020-04-10 17:24:08 +01:00
nvs_close ( handle ) ;
interrupts ( ) ;
}
2020-11-12 17:37:01 +00:00
int32_t NvmErase ( const char * sNvsName ) {
2020-04-10 17:24:08 +01:00
nvs_handle handle ;
noInterrupts ( ) ;
2020-11-12 17:37:01 +00:00
int32_t result = nvs_open ( sNvsName , NVS_READWRITE , & handle ) ;
2020-11-12 16:38:15 +00:00
if ( ESP_OK = = result ) { result = nvs_erase_all ( handle ) ; }
if ( ESP_OK = = result ) { result = nvs_commit ( handle ) ; }
2020-04-10 17:24:08 +01:00
nvs_close ( handle ) ;
interrupts ( ) ;
2020-11-12 16:38:15 +00:00
return result ;
2020-04-10 17:24:08 +01:00
}
2020-04-22 15:07:52 +01:00
void SettingsErase ( uint8_t type ) {
2020-11-12 16:38:15 +00:00
// All SDK and Tasmota data is held in default NVS partition
2020-11-12 16:50:34 +00:00
// cal_data - SDK PHY calibration data as documented in esp_phy_init.h
// qpc - Tasmota Quick Power Cycle state
// main - Tasmota Settings data
2020-11-12 17:37:01 +00:00
int32_t r1 , r2 , r3 ;
2020-11-12 16:38:15 +00:00
switch ( type ) {
case 0 : // Reset 2, 5, 6 = Erase all flash from program end to end of physical flash
// nvs_flash_erase(); // Erase RTC, PHY, sta.mac, ap.sndchan, ap.mac, Tasmota etc.
r1 = NvmErase ( " qpc " ) ;
r2 = NvmErase ( " main " ) ;
AddLog_P ( LOG_LEVEL_DEBUG , PSTR ( D_LOG_APPLICATION D_ERASE " Tasmota data (%d,%d) " ) , r1 , r2 ) ;
break ;
case 1 : case 4 : // Reset 3 or WIFI_FORCE_RF_CAL_ERASE = SDK parameter area
2020-11-12 16:50:34 +00:00
r1 = esp_phy_erase_cal_data_in_nvs ( ) ;
// r1 = NvmErase("cal_data");
2020-11-12 16:38:15 +00:00
AddLog_P ( LOG_LEVEL_DEBUG , PSTR ( D_LOG_APPLICATION D_ERASE " PHY data (%d) " ) , r1 ) ;
break ;
case 2 : // Not used = QPC and Tasmota parameter area (0x0F3xxx - 0x0FBFFF)
r1 = NvmErase ( " qpc " ) ;
r2 = NvmErase ( " main " ) ;
AddLog_P ( LOG_LEVEL_DEBUG , PSTR ( D_LOG_APPLICATION D_ERASE " Tasmota data (%d,%d) " ) , r1 , r2 ) ;
break ;
case 3 : // QPC Reached = QPC, Tasmota and SDK parameter area (0x0F3xxx - 0x0FFFFF)
// nvs_flash_erase(); // Erase RTC, PHY, sta.mac, ap.sndchan, ap.mac, Tasmota etc.
r1 = NvmErase ( " qpc " ) ;
r2 = NvmErase ( " main " ) ;
// r3 = esp_phy_erase_cal_data_in_nvs();
// r3 = NvmErase("cal_data");
// AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_ERASE " Tasmota (%d,%d) and PHY data (%d)"), r1, r2, r3);
AddLog_P ( LOG_LEVEL_DEBUG , PSTR ( D_LOG_APPLICATION D_ERASE " Tasmota data (%d,%d) " ) , r1 , r2 ) ;
break ;
2020-04-19 14:36:04 +01:00
}
2020-04-10 17:24:08 +01:00
}
2020-04-22 15:07:52 +01:00
void SettingsRead ( void * data , size_t size ) {
2020-04-19 14:36:04 +01:00
NvmLoad ( " main " , " Settings " , data , size ) ;
2020-04-10 17:24:08 +01:00
}
2020-04-22 15:07:52 +01:00
void SettingsWrite ( const void * pSettings , unsigned nSettingsLen ) {
2020-04-19 14:36:04 +01:00
NvmSave ( " main " , " Settings " , pSettings , nSettingsLen ) ;
2020-04-10 17:24:08 +01:00
}
2020-04-22 15:07:52 +01:00
void QPCRead ( void * pSettings , unsigned nSettingsLen ) {
2020-04-19 14:36:04 +01:00
NvmLoad ( " qpc " , " pcreg " , pSettings , nSettingsLen ) ;
2020-04-10 17:24:08 +01:00
}
2020-04-22 15:07:52 +01:00
void QPCWrite ( const void * pSettings , unsigned nSettingsLen ) {
2020-04-19 14:36:04 +01:00
NvmSave ( " qpc " , " pcreg " , pSettings , nSettingsLen ) ;
2020-04-10 17:24:08 +01:00
}
2020-07-05 14:13:57 +01:00
void ZigbeeErase ( void ) {
NvmErase ( " zb " ) ;
}
2020-07-05 13:51:55 +01:00
void ZigbeeRead ( void * pSettings , unsigned nSettingsLen ) {
NvmLoad ( " zb " , " zigbee " , pSettings , nSettingsLen ) ;
}
void ZigbeeWrite ( const void * pSettings , unsigned nSettingsLen ) {
NvmSave ( " zb " , " zigbee " , pSettings , nSettingsLen ) ;
}
2020-11-12 16:38:15 +00:00
void NvsInfo ( void ) {
nvs_stats_t nvs_stats ;
nvs_get_stats ( NULL , & nvs_stats ) ;
AddLog_P ( LOG_LEVEL_INFO , PSTR ( " INF: NVS Used %d, Free %d, Total %d, Namspaces %d " ) ,
nvs_stats . used_entries , nvs_stats . free_entries , nvs_stats . total_entries , nvs_stats . namespace_count ) ;
}
2020-11-22 16:35:04 +00:00
//
// Flash memory mapping
//
# include "Esp.h"
# include "rom/spi_flash.h"
# include "esp_spi_flash.h"
# include <memory>
# include <soc/soc.h>
# include <soc/efuse_reg.h>
# include <esp_partition.h>
extern " C " {
# include "esp_ota_ops.h"
# include "esp_image_format.h"
}
uint32_t EspFlashBaseAddress ( void ) {
const esp_partition_t * partition = esp_ota_get_next_update_partition ( nullptr ) ;
if ( ! partition ) { return 0 ; }
return partition - > address ; // For tasmota 0x00010000 or 0x00200000
}
uint32_t EspFlashBaseEndAddress ( void ) {
const esp_partition_t * partition = esp_ota_get_next_update_partition ( nullptr ) ;
if ( ! partition ) { return 0 ; }
return partition - > address + partition - > size ; // For tasmota 0x00200000 or 0x003F0000
}
uint8_t * EspFlashMmap ( uint32_t address ) {
static spi_flash_mmap_handle_t handle = 0 ;
if ( handle ) {
spi_flash_munmap ( handle ) ;
handle = 0 ;
}
const uint8_t * data ;
int32_t err = spi_flash_mmap ( address , 5 * SPI_FLASH_MMU_PAGE_SIZE , SPI_FLASH_MMAP_DATA , ( const void * * ) & data , & handle ) ;
/*
AddLog_P ( LOG_LEVEL_DEBUG , PSTR ( " DBG: Spi_flash_map %d " ) , err ) ;
spi_flash_mmap_dump ( ) ;
*/
return ( uint8_t * ) data ;
}
/*
int32_t EspPartitionMmap ( uint32_t action ) {
static spi_flash_mmap_handle_t handle ;
int32_t err = 0 ;
if ( 1 = = action ) {
const esp_partition_t * partition = esp_ota_get_running_partition ( ) ;
// const esp_partition_t* partition = esp_ota_get_next_update_partition(nullptr);
if ( ! partition ) { return 0 ; }
err = esp_partition_mmap ( partition , 0 , 4 * SPI_FLASH_MMU_PAGE_SIZE , SPI_FLASH_MMAP_DATA , ( const void * * ) & TasmotaGlobal_mmap_data , & handle ) ;
AddLog_P ( LOG_LEVEL_DEBUG , PSTR ( " DBG: Partition start 0x%08X, Partition end 0x%08X, Mmap data 0x%08X " ) , partition - > address , partition - > size , TasmotaGlobal_mmap_data ) ;
} else {
spi_flash_munmap ( handle ) ;
handle = 0 ;
}
return err ;
}
*/
2020-04-10 17:24:08 +01:00
//
// Crash stuff
//
2020-04-22 15:07:52 +01:00
void CrashDump ( void ) {
2020-04-10 17:24:08 +01:00
}
2020-04-22 15:07:52 +01:00
bool CrashFlag ( void ) {
2020-04-10 17:24:08 +01:00
return false ;
}
2020-04-22 15:07:52 +01:00
void CrashDumpClear ( void ) {
2020-04-10 17:24:08 +01:00
}
2020-04-22 15:07:52 +01:00
void CmndCrash ( void ) {
2020-04-10 17:24:08 +01:00
/*
volatile uint32_t dummy ;
dummy = * ( ( uint32_t * ) 0x00000000 ) ;
*/
}
// Do an infinite loop to trigger WDT watchdog
2020-04-22 15:07:52 +01:00
void CmndWDT ( void ) {
2020-04-10 17:24:08 +01:00
/*
volatile uint32_t dummy = 0 ;
while ( 1 ) {
dummy + + ;
}
*/
}
// This will trigger the os watch after OSWATCH_RESET_TIME (=120) seconds
2020-04-22 15:07:52 +01:00
void CmndBlockedLoop ( void ) {
2020-04-10 17:24:08 +01:00
/*
while ( 1 ) {
delay ( 1000 ) ;
}
*/
}
2020-04-19 14:36:04 +01:00
//
// ESP32 specific
//
# include "soc/soc.h"
# include "soc/rtc_cntl_reg.h"
2020-04-22 15:07:52 +01:00
void DisableBrownout ( void ) {
2020-04-19 14:36:04 +01:00
// https://github.com/espressif/arduino-esp32/issues/863#issuecomment-347179737
WRITE_PERI_REG ( RTC_CNTL_BROWN_OUT_REG , 0 ) ; // Disable brownout detector
}
2020-04-19 15:58:13 +01:00
//
// ESP32 Alternatives
//
2020-04-22 15:07:52 +01:00
String ESP32GetResetReason ( uint32_t cpu_no ) {
2020-04-19 15:58:13 +01:00
// tools\sdk\include\esp32\rom\rtc.h
2020-11-15 08:57:23 +00:00
switch ( rtc_get_reset_reason ( cpu_no ) ) {
case POWERON_RESET : return F ( " Vbat power on reset " ) ; // 1
case SW_RESET : return F ( " Software reset digital core " ) ; // 3
case OWDT_RESET : return F ( " Legacy watch dog reset digital core " ) ; // 4
case DEEPSLEEP_RESET : return F ( " Deep Sleep reset digital core " ) ; // 5
case SDIO_RESET : return F ( " Reset by SLC module, reset digital core " ) ; // 6
case TG0WDT_SYS_RESET : return F ( " Timer Group0 Watch dog reset digital core " ) ; // 7
case TG1WDT_SYS_RESET : return F ( " Timer Group1 Watch dog reset digital core " ) ; // 8
case RTCWDT_SYS_RESET : return F ( " RTC Watch dog Reset digital core " ) ; // 9
case INTRUSION_RESET : return F ( " Instrusion tested to reset CPU " ) ; // 10
case TGWDT_CPU_RESET : return F ( " Time Group reset CPU " ) ; // 11
case SW_CPU_RESET : return F ( " Software reset CPU " ) ; // 12
case RTCWDT_CPU_RESET : return F ( " RTC Watch dog Reset CPU " ) ; // 13
case EXT_CPU_RESET : return F ( " or APP CPU, reseted by PRO CPU " ) ; // 14
case RTCWDT_BROWN_OUT_RESET : return F ( " Reset when the vdd voltage is not stable " ) ; // 15
2020-11-22 16:35:04 +00:00
case RTCWDT_RTC_RESET : return F ( " RTC Watch dog reset digital core and rtc module " ) ; // 16
2020-04-19 15:58:13 +01:00
}
2020-11-15 08:57:23 +00:00
return F ( " No meaning " ) ; // 0 and undefined
2020-04-19 15:58:13 +01:00
}
2020-04-22 15:07:52 +01:00
String ESP_getResetReason ( void ) {
2020-04-19 15:58:13 +01:00
return ESP32GetResetReason ( 0 ) ; // CPU 0
}
2020-04-22 15:07:52 +01:00
uint32_t ESP_ResetInfoReason ( void ) {
2020-04-19 15:58:13 +01:00
RESET_REASON reason = rtc_get_reset_reason ( 0 ) ;
if ( POWERON_RESET = = reason ) { return REASON_DEFAULT_RST ; }
if ( SW_CPU_RESET = = reason ) { return REASON_SOFT_RESTART ; }
if ( DEEPSLEEP_RESET = = reason ) { return REASON_DEEP_SLEEP_AWAKE ; }
if ( SW_RESET = = reason ) { return REASON_EXT_SYS_RST ; }
2020-11-15 08:57:23 +00:00
return - 1 ; //no "official error code", but should work with the current code base
2020-04-19 15:58:13 +01:00
}
2020-04-22 15:07:52 +01:00
uint32_t ESP_getChipId ( void ) {
2020-04-19 15:58:13 +01:00
uint32_t id = 0 ;
for ( uint32_t i = 0 ; i < 17 ; i = i + 8 ) {
id | = ( ( ESP . getEfuseMac ( ) > > ( 40 - i ) ) & 0xff ) < < i ;
}
return id ;
}
2020-04-22 15:07:52 +01:00
uint32_t ESP_getSketchSize ( void ) {
2020-04-19 15:58:13 +01:00
static uint32_t sketchsize = 0 ;
if ( ! sketchsize ) {
sketchsize = ESP . getSketchSize ( ) ; // This takes almost 2 seconds on an ESP32
}
return sketchsize ;
}
2020-04-22 15:07:52 +01:00
uint32_t ESP_getFreeHeap ( void ) {
// return ESP.getFreeHeap();
return ESP . getMaxAllocHeap ( ) ;
}
2020-08-12 10:46:06 +01:00
uint32_t ESP_getMaxAllocHeap ( void ) {
2020-08-12 11:11:47 +01:00
// largest block of heap that can be allocated at once
uint32_t free_block_size = ESP . getMaxAllocHeap ( ) ;
if ( free_block_size > 100 ) { free_block_size - = 100 ; }
return free_block_size ;
2020-08-12 10:46:06 +01:00
}
2020-04-22 15:07:52 +01:00
void ESP_Restart ( void ) {
2020-04-19 15:58:13 +01:00
ESP . restart ( ) ;
}
2020-11-22 16:35:04 +00:00
uint32_t FlashWriteStartSector ( void ) {
// Needs to be on SPI_FLASH_MMU_PAGE_SIZE (= 0x10000) alignment for mmap usage
uint32_t aligned_address = ( ( EspFlashBaseAddress ( ) + ( 2 * SPI_FLASH_MMU_PAGE_SIZE ) ) / SPI_FLASH_MMU_PAGE_SIZE ) * SPI_FLASH_MMU_PAGE_SIZE ;
return aligned_address / SPI_FLASH_SEC_SIZE ;
}
uint32_t FlashWriteMaxSector ( void ) {
// Needs to be on SPI_FLASH_MMU_PAGE_SIZE (= 0x10000) alignment for mmap usage
uint32_t aligned_end_address = ( EspFlashBaseEndAddress ( ) / SPI_FLASH_MMU_PAGE_SIZE ) * SPI_FLASH_MMU_PAGE_SIZE ;
return aligned_end_address / SPI_FLASH_SEC_SIZE ;
}
uint8_t * FlashDirectAccess ( void ) {
uint32_t address = FlashWriteStartSector ( ) * SPI_FLASH_SEC_SIZE ;
uint8_t * data = EspFlashMmap ( address ) ;
/*
AddLog_P ( LOG_LEVEL_DEBUG , PSTR ( " DBG: Flash start address 0x%08X, Mmap address 0x%08X " ) , address , data ) ;
uint8_t buf [ 32 ] ;
memcpy ( buf , data , sizeof ( buf ) ) ;
AddLogBuffer ( LOG_LEVEL_DEBUG , ( uint8_t * ) & buf , 32 ) ;
*/
return data ;
}
2020-04-10 17:24:08 +01:00
# endif // ESP32