From b26ae50d6334d3bc676e1170a457ab3582695341 Mon Sep 17 00:00:00 2001 From: loboris Date: Sat, 8 Jul 2017 13:49:17 +0200 Subject: [PATCH 1/5] Wear levelling, first commit --- esp32/Makefile | 11 ++++++++++ esp32/modesp.c | 41 ++++++++++++++++++++++++++++++++------ esp32/modules/flashbdev.py | 3 ++- 3 files changed, 48 insertions(+), 7 deletions(-) diff --git a/esp32/Makefile b/esp32/Makefile index 9ab7af876..dce962045 100644 --- a/esp32/Makefile +++ b/esp32/Makefile @@ -72,6 +72,8 @@ INC += -I$(ESPCOMP)/lwip/include/lwip/posix INC += -I$(ESPCOMP)/mbedtls/include INC += -I$(ESPCOMP)/mbedtls/port/include INC += -I$(ESPCOMP)/spi_flash/include +INC += -I$(ESPCOMP)/wear_levelling/include +INC += -I$(ESPCOMP)/wear_levelling/private_include INC += -I$(ESPCOMP)/vfs/include INC += -I$(ESPCOMP)/newlib/platform_include INC += -I$(ESPCOMP)/xtensa-debug-module/include @@ -374,6 +376,14 @@ ESPIDF_SPI_FLASH_O = $(addprefix $(ESPCOMP)/spi_flash/,\ flash_ops.o \ ) +ESPIDF_WEAR_LEVELLING_O = $(addprefix $(ESPCOMP)/wear_levelling/,\ + wear_levelling.o \ + Partition.o \ + WL_Flash.o \ + crc32.o \ + SPI_Flash.o \ + ) + $(BUILD)/$(ESPCOMP)/lwip/%.o: CFLAGS += -Wno-address -Wno-unused-variable -Wno-unused-but-set-variable ESPIDF_LWIP_O = $(addprefix $(ESPCOMP)/lwip/,\ api/pppapi.o \ @@ -556,6 +566,7 @@ OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_NGHTTP_O)) OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_NVS_FLASH_O)) OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_OPENSSL_O)) OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_SPI_FLASH_O)) +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_WEAR_LEVELLING_O)) OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_WPA_SUPPLICANT_O)) ################################################################################ # Main targets diff --git a/esp32/modesp.c b/esp32/modesp.c index 805002655..afec3ea53 100644 --- a/esp32/modesp.c +++ b/esp32/modesp.c @@ -30,6 +30,7 @@ #include #include "esp_spi_flash.h" +#include "wear_levelling.h" #include "py/runtime.h" #include "py/mperrno.h" @@ -37,11 +38,24 @@ #include "drivers/dht/dht.h" #include "modesp.h" +STATIC wl_handle_t fs_handle = WL_INVALID_HANDLE; +STATIC size_t wl_sect_size = 4096; + +STATIC const esp_partition_t fs_part = { + ESP_PARTITION_TYPE_DATA, //type + ESP_PARTITION_SUBTYPE_DATA_FAT, //subtype + 0x200000, // address + 0x1FF000, // size (2MB) + "uPYpart", // label + 0 // encrypted +}; + STATIC mp_obj_t esp_flash_read(mp_obj_t offset_in, mp_obj_t buf_in) { mp_int_t offset = mp_obj_get_int(offset_in); mp_buffer_info_t bufinfo; mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_WRITE); - esp_err_t res = spi_flash_read(offset, bufinfo.buf, bufinfo.len); + + esp_err_t res = wl_read(fs_handle, offset, bufinfo.buf, bufinfo.len); if (res != ESP_OK) { mp_raise_OSError(MP_EIO); } @@ -53,7 +67,8 @@ STATIC mp_obj_t esp_flash_write(mp_obj_t offset_in, mp_obj_t buf_in) { mp_int_t offset = mp_obj_get_int(offset_in); mp_buffer_info_t bufinfo; mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_READ); - esp_err_t res = spi_flash_write(offset, bufinfo.buf, bufinfo.len); + + esp_err_t res = wl_write(fs_handle, offset, bufinfo.buf, bufinfo.len); if (res != ESP_OK) { mp_raise_OSError(MP_EIO); } @@ -63,7 +78,8 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(esp_flash_write_obj, esp_flash_write); STATIC mp_obj_t esp_flash_erase(mp_obj_t sector_in) { mp_int_t sector = mp_obj_get_int(sector_in); - esp_err_t res = spi_flash_erase_sector(sector); + + esp_err_t res = wl_erase_range(fs_handle, sector * wl_sect_size, wl_sect_size); if (res != ESP_OK) { mp_raise_OSError(MP_EIO); } @@ -72,15 +88,28 @@ STATIC mp_obj_t esp_flash_erase(mp_obj_t sector_in) { STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp_flash_erase_obj, esp_flash_erase); STATIC mp_obj_t esp_flash_size(void) { - return mp_obj_new_int_from_uint(spi_flash_get_chip_size()); + if (fs_handle == WL_INVALID_HANDLE) { + esp_err_t res = wl_mount(&fs_part, &fs_handle); + if (res != ESP_OK) { + return mp_obj_new_int_from_uint(0x010000); + } + wl_sect_size = wl_sector_size(fs_handle); + } + return mp_obj_new_int_from_uint(0x1FF000); } STATIC MP_DEFINE_CONST_FUN_OBJ_0(esp_flash_size_obj, esp_flash_size); -STATIC mp_obj_t esp_flash_user_start(void) { - return MP_OBJ_NEW_SMALL_INT(0x200000); +STATIC mp_obj_t esp_flash_sec_size() { + return mp_obj_new_int_from_uint(wl_sect_size); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(esp_flash_sec_size_obj, esp_flash_sec_size); + +STATIC IRAM_ATTR mp_obj_t esp_flash_user_start(void) { + return MP_OBJ_NEW_SMALL_INT(0); } STATIC MP_DEFINE_CONST_FUN_OBJ_0(esp_flash_user_start_obj, esp_flash_user_start); + STATIC mp_obj_t esp_neopixel_write_(mp_obj_t pin, mp_obj_t buf, mp_obj_t timing) { mp_buffer_info_t bufinfo; mp_get_buffer_raise(buf, &bufinfo, MP_BUFFER_READ); diff --git a/esp32/modules/flashbdev.py b/esp32/modules/flashbdev.py index 935f5342f..17c9bd6b3 100644 --- a/esp32/modules/flashbdev.py +++ b/esp32/modules/flashbdev.py @@ -2,6 +2,7 @@ class FlashBdev: + FS_SIZE = esp.flash_size() SEC_SIZE = 4096 START_SEC = esp.flash_user_start() // SEC_SIZE @@ -31,4 +32,4 @@ def ioctl(self, op, arg): bdev = None else: # for now we use a fixed size for the filesystem - bdev = FlashBdev(2048 * 1024 // FlashBdev.SEC_SIZE) + bdev = FlashBdev(size // FlashBdev.SEC_SIZE) From dd6911bd7eccc1a0e574ab294ff670ef55d83641 Mon Sep 17 00:00:00 2001 From: loboris Date: Mon, 10 Jul 2017 09:42:38 +0200 Subject: [PATCH 2/5] small bug fix in flashfbdev.py/modesp.c --- esp32/modesp.c | 1 + esp32/modules/flashbdev.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/esp32/modesp.c b/esp32/modesp.c index afec3ea53..3e4101d23 100644 --- a/esp32/modesp.c +++ b/esp32/modesp.c @@ -127,6 +127,7 @@ STATIC const mp_rom_map_elem_t esp_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_flash_erase), MP_ROM_PTR(&esp_flash_erase_obj) }, { MP_ROM_QSTR(MP_QSTR_flash_size), MP_ROM_PTR(&esp_flash_size_obj) }, { MP_ROM_QSTR(MP_QSTR_flash_user_start), MP_ROM_PTR(&esp_flash_user_start_obj) }, + { MP_ROM_QSTR(MP_QSTR_flash_sec_size), MP_ROM_PTR(&esp_flash_sec_size_obj) }, { MP_ROM_QSTR(MP_QSTR_neopixel_write), MP_ROM_PTR(&esp_neopixel_write_obj) }, { MP_ROM_QSTR(MP_QSTR_dht_readinto), MP_ROM_PTR(&dht_readinto_obj) }, diff --git a/esp32/modules/flashbdev.py b/esp32/modules/flashbdev.py index 17c9bd6b3..ec41445d6 100644 --- a/esp32/modules/flashbdev.py +++ b/esp32/modules/flashbdev.py @@ -3,7 +3,7 @@ class FlashBdev: FS_SIZE = esp.flash_size() - SEC_SIZE = 4096 + SEC_SIZE = esp.flash_sec_size() START_SEC = esp.flash_user_start() // SEC_SIZE def __init__(self, blocks): From db92267135bcdc8b5052b90c21e2667cf946374a Mon Sep 17 00:00:00 2001 From: loboris Date: Wed, 12 Jul 2017 15:49:13 +0200 Subject: [PATCH 3/5] Configuration options for wear leveling driver are added to mpconfigport.h Added RTC Class to machine module: Added new methods to machine module: Changed 'fatfsport.c' so that the correct timestamps are set on file change/create Added sdcard driver which uses esp-idf sdmmc driver. Added Makefile option to start esp-idf monitor (terminal emulator) Added options to stay in bootloader after 'erase' and 'deploy' Added simple bash script to make MicroPython build proces easier --- esp32/BUILD.sh | 49 +++++ esp32/Makefile | 25 ++- esp32/fatfs_port.c | 11 +- esp32/machrtc.c | 387 +++++++++++++++++++++++++++++++++++++ esp32/machrtc.h | 29 +++ esp32/main.c | 5 + esp32/modesp.c | 247 ++++++++++++++++++++++- esp32/modmachine.c | 74 +++++++ esp32/modules/flashbdev.py | 9 +- esp32/modules/sdcard.py | 46 +++++ esp32/mpconfigport.h | 9 + esp32/mpsleep.c | 126 ++++++++++++ esp32/mpsleep.h | 47 +++++ esp32/sdkconfig.h | 9 +- esp32/timeutils_epoch.c | 212 ++++++++++++++++++++ esp32/timeutils_epoch.h | 54 ++++++ 16 files changed, 1315 insertions(+), 24 deletions(-) create mode 100755 esp32/BUILD.sh create mode 100644 esp32/machrtc.c create mode 100644 esp32/machrtc.h create mode 100644 esp32/modules/sdcard.py create mode 100644 esp32/mpsleep.c create mode 100644 esp32/mpsleep.h create mode 100644 esp32/timeutils_epoch.c create mode 100644 esp32/timeutils_epoch.h diff --git a/esp32/BUILD.sh b/esp32/BUILD.sh new file mode 100755 index 000000000..dc3ee8e0f --- /dev/null +++ b/esp32/BUILD.sh @@ -0,0 +1,49 @@ +#!/bin/bash + +# ############################################### +# This is the clone of the MicroPython repository +# (https://github.com/micropython/micropython) +# with added ESP32 build +# ############################################### + +# Usage: +# ./BUILD - run the build, create MicroPython firmware +# ./BUILD clean - clean the build +# ./BUILD deploy - flash MicroPython firmware to ESP32 +# ./BUILD erase - erase the whole ESP32 Flash +# ./BUILD monitor - run esp-idf terminal program + +# ########################################################################### +# build MicroPython cross compiler which compiles .py scripts into .mpy files +# ########################################################################### +make -C ../mpy-cross + +export HOST_PLATFORM=linux + +# ######################################## +# Add Xtensa toolchain path to system path +# ######################################## +export PATH=/xtensa-esp32-elf/bin:$PATH + +# ################### +# Export esp-idf path +# ################### +export IDF_PATH=/esp-idf + +# ################################## +# Export Micropython build variables +# ################################## +export CROSS_COMPILE=xtensa-esp32-elf- +export ESPIDF=$IDF_PATH +export PORT=/dev/ttyUSB1 +export FLASH_MODE=dio +export FLASH_SIZE=4MB +export BAUD=921600 +export DEPLOY_ERASE_RESET=no_reset + +# ########################## +# build MicroPython firmware +# ########################## +cd esp32 + +make "$@" diff --git a/esp32/Makefile b/esp32/Makefile index dce962045..db9a2d67d 100644 --- a/esp32/Makefile +++ b/esp32/Makefile @@ -16,10 +16,12 @@ include ../py/py.mk PORT ?= /dev/ttyUSB0 BAUD ?= 460800 +MONITOR_BAUD ?= 115200 FLASH_MODE ?= dio FLASH_FREQ ?= 40m FLASH_SIZE ?= 4MB CROSS_COMPILE ?= xtensa-esp32-elf- +DEPLOY_ERASE_RESET ?= reset # paths to ESP IDF and its components ifeq ($(ESPIDF),) @@ -27,6 +29,9 @@ $(error Please configure the ESPIDF variable) endif ESPCOMP = $(ESPIDF)/components ESPTOOL ?= $(ESPCOMP)/esptool_py/esptool/esptool.py +ESPMONITOR ?= $(ESPIDF)/tools/idf_monitor.py +MONITOR_OPTS := --baud $(MONITOR_BAUD) --port $(PORT) --toolchain-prefix $(CROSS_COMPILE) + # verify the ESP IDF version ESPIDF_SUPHASH := 9b955f4c9f1b32652ea165d3e4cdaad01bba170e @@ -81,6 +86,7 @@ INC += -I$(ESPCOMP)/wpa_supplicant/include INC += -I$(ESPCOMP)/wpa_supplicant/port/include INC += -I$(ESPCOMP)/ethernet/include INC += -I$(ESPCOMP)/app_trace/include +INC += -I$(ESPCOMP)/sdmmc/include CFLAGS = -std=gnu99 -Os -ffunction-sections -fdata-sections -fstrict-volatile-bitfields -mlongcalls -nostdlib -Wall -Werror -Wno-error=unused-function -Wno-error=unused-but-set-variable -Wno-error=unused-variable -Wno-error=deprecated-declarations -DMBEDTLS_CONFIG_FILE='"mbedtls/esp_config.h"' -DHAVE_CONFIG_H -DESP_PLATFORM $(INC) CFLAGS += -DIDF_VER=\"$(IDF_VER)\" @@ -136,6 +142,9 @@ SRC_C = \ espneopixel.c \ machine_hw_spi.c \ mpthreadport.c \ + mpsleep.c \ + machrtc.c \ + timeutils_epoch.c \ $(SRC_MOD) ESP8266_SRC_C = $(addprefix esp8266/,\ @@ -207,6 +216,8 @@ ESPIDF_DRIVER_O = $(addprefix $(ESPCOMP)/driver/,\ spi_master.o \ spi_common.o \ rtc_module.o \ + sdmmc_host.o \ + sdmmc_transaction.o \ ) $(BUILD)/$(ESPCOMP)/esp32/dport_access.o: CFLAGS += -Wno-array-bounds @@ -384,6 +395,10 @@ ESPIDF_WEAR_LEVELLING_O = $(addprefix $(ESPCOMP)/wear_levelling/,\ SPI_Flash.o \ ) +ESPIDF_SDMMC_O = $(addprefix $(ESPCOMP)/sdmmc/,\ + sdmmc_cmd.o \ + ) + $(BUILD)/$(ESPCOMP)/lwip/%.o: CFLAGS += -Wno-address -Wno-unused-variable -Wno-unused-but-set-variable ESPIDF_LWIP_O = $(addprefix $(ESPCOMP)/lwip/,\ api/pppapi.o \ @@ -567,13 +582,14 @@ OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_NVS_FLASH_O)) OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_OPENSSL_O)) OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_SPI_FLASH_O)) OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_WEAR_LEVELLING_O)) +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_SDMMC_O)) OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_WPA_SUPPLICANT_O)) ################################################################################ # Main targets all: $(BUILD)/firmware.bin -.PHONY: idf-version deploy erase +.PHONY: idf-version deploy erase monitor idf-version: $(ECHO) "ESP IDF supported hash: $(ESPIDF_SUPHASH)" @@ -584,11 +600,14 @@ $(BUILD)/firmware.bin: $(BUILD)/bootloader.bin $(BUILD)/partitions.bin $(BUILD)/ deploy: $(BUILD)/firmware.bin $(ECHO) "Writing $^ to the board" - $(Q)$(ESPTOOL) --chip esp32 --port $(PORT) --baud $(BAUD) write_flash -z --flash_mode $(FLASH_MODE) --flash_freq $(FLASH_FREQ) --flash_size $(FLASH_SIZE) 0 $^ + $(Q)$(ESPTOOL) --chip esp32 --port $(PORT) --baud $(BAUD) --after $(DEPLOY_ERASE_RESET) write_flash -z --flash_mode $(FLASH_MODE) --flash_freq $(FLASH_FREQ) --flash_size $(FLASH_SIZE) 0 $^ erase: $(ECHO) "Erasing flash" - $(Q)$(ESPTOOL) --chip esp32 --port $(PORT) --baud $(BAUD) erase_flash + $(Q)$(ESPTOOL) --chip esp32 --port $(PORT) --baud $(BAUD) --after $(DEPLOY_ERASE_RESET) erase_flash + +monitor: + $(ESPMONITOR) $(MONITOR_OPTS) $(BUILD)/application.elf ################################################################################ # Declarations to build the application diff --git a/esp32/fatfs_port.c b/esp32/fatfs_port.c index b3690a01f..f487bb330 100644 --- a/esp32/fatfs_port.c +++ b/esp32/fatfs_port.c @@ -28,18 +28,15 @@ #include "py/obj.h" #include "lib/oofatfs/ff.h" -#include "timeutils.h" -//#include "modmachine.h" +#include "timeutils_epoch.h" +#include "machrtc.h" DWORD get_fattime(void) { - // TODO: Optimize division (there's no HW division support on ESP8266, - // so it's expensive). - //uint32_t secs = (uint32_t)(pyb_rtc_get_us_since_2000() / 1000000); - uint32_t secs = 0; + uint32_t secs = (uint32_t)(mach_rtc_get_us_since_epoch() / 1000000); timeutils_struct_time_t tm; - timeutils_seconds_since_2000_to_struct_time(secs, &tm); + timeutils_seconds_since_epoch_to_struct_time(secs, &tm); return (((DWORD)(tm.tm_year - 1980) << 25) | ((DWORD)tm.tm_mon << 21) | ((DWORD)tm.tm_mday << 16) | ((DWORD)tm.tm_hour << 11) | ((DWORD)tm.tm_min << 5) | ((DWORD)tm.tm_sec >> 1)); diff --git a/esp32/machrtc.c b/esp32/machrtc.c new file mode 100644 index 000000000..73848f48d --- /dev/null +++ b/esp32/machrtc.c @@ -0,0 +1,387 @@ +/* + * Copyright (c) 2016, Pycom Limited. + * + * Modified by LoBo (https://github.com/loboris) 07/2017 + * + * This software is licensed under the GNU GPL version 3 or any + * later version, with permitted additional terms. For more information + * see the Pycom Licence v1.0 document supplied with this file, or + * available at https://www.pycom.io/opensource/licensing + */ + +#include +#include + +#include "apps/sntp/sntp.h" + +#include "py/nlr.h" +#include "py/obj.h" +#include "py/objstr.h" +#include "py/runtime.h" +#include "timeutils_epoch.h" +#include "esp_system.h" +#include "machrtc.h" +#include "soc/rtc.h" +#include "esp_clk.h" + +#include "sdkconfig.h" +#include "esp_attr.h" +#include "esp_log.h" +#include "rom/ets_sys.h" +#include "rom/uart.h" +#include "soc/soc.h" +#include "soc/rtc.h" +#include "soc/rtc_cntl_reg.h" +#include "mphalport.h" + +uint32_t sntp_update_period = 3600000; // in ms + +#define MACHINE_RTC_VALID_EXT_PINS \ +( \ + (1ll << 0) | \ + (1ll << 2) | \ + (1ll << 4) | \ + (1ll << 12) | \ + (1ll << 13) | \ + (1ll << 14) | \ + (1ll << 15) | \ + (1ll << 25) | \ + (1ll << 26) | \ + (1ll << 27) | \ + (1ll << 32) | \ + (1ll << 33) | \ + (1ll << 34) | \ + (1ll << 35) | \ + (1ll << 36) | \ + (1ll << 37) | \ + (1ll << 38) | \ + (1ll << 39) \ +) + +#define MACHINE_RTC_LAST_EXT_PIN 39 + +machine_rtc_config_t machine_rtc_config = { 0 }; + + +#define DEFAULT_SNTP_SERVER "pool.ntp.org" + +//------------------------------ +typedef struct _mach_rtc_obj_t { + mp_obj_base_t base; + mp_obj_t sntp_server_name; + bool synced; +} mach_rtc_obj_t; + +static RTC_DATA_ATTR uint64_t delta_from_epoch_til_boot; + +STATIC mach_rtc_obj_t mach_rtc_obj; +const mp_obj_type_t mach_rtc_type; + +//-------------------- +void rtc_init0(void) { + mach_rtc_set_us_since_epoch(0); +} + +//------------------------------------------------ +void mach_rtc_set_us_since_epoch(uint64_t nowus) { + struct timeval tv; + + // store the packet timestamp + gettimeofday(&tv, NULL); + delta_from_epoch_til_boot = nowus - (uint64_t)((tv.tv_sec * 1000000ull) + tv.tv_usec); +} + +//--------------------------- +void mach_rtc_synced (void) { + mach_rtc_obj.synced = true; +} + +//------------------------------------------ +uint64_t mach_rtc_get_us_since_epoch(void) { + struct timeval tv; + gettimeofday(&tv, NULL); + return (uint64_t)((tv.tv_sec * 1000000ull) + tv.tv_usec) + delta_from_epoch_til_boot; +}; + +//------------------------------------------------------------- +STATIC uint64_t mach_rtc_datetime_us(const mp_obj_t datetime) { + timeutils_struct_time_t tm; + uint64_t useconds; + + // set date and time + mp_obj_t *items; + uint len; + mp_obj_get_array(datetime, &len, &items); + + // verify the tuple + if (len < 3 || len > 8) { + mp_raise_ValueError("Invalid arguments"); + } + + tm.tm_year = mp_obj_get_int(items[0]); + tm.tm_mon = mp_obj_get_int(items[1]); + tm.tm_mday = mp_obj_get_int(items[2]); + if (len < 7) { + useconds = 0; + } else { + useconds = mp_obj_get_int(items[6]); + } + if (len < 6) { + tm.tm_sec = 0; + } else { + tm.tm_sec = mp_obj_get_int(items[5]); + } + if (len < 5) { + tm.tm_min = 0; + } else { + tm.tm_min = mp_obj_get_int(items[4]); + } + if (len < 4) { + tm.tm_hour = 0; + } else { + tm.tm_hour = mp_obj_get_int(items[3]); + } + useconds += 1000000ull * timeutils_seconds_since_epoch(tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); + return useconds; +} + +/// The 8-tuple has the same format as CPython's datetime object: +/// +/// (year, month, day, hours, minutes, seconds, milliseconds, tzinfo=None) +/// +//------------------------------------------------------ +STATIC void mach_rtc_datetime(const mp_obj_t datetime) { + uint64_t useconds; + + if (datetime != mp_const_none) { + useconds = mach_rtc_datetime_us(datetime); + mach_rtc_set_us_since_epoch(useconds); + } else { + mach_rtc_set_us_since_epoch(0); + } +} + +/******************************************************************************/ +// Micro Python bindings + +//-------------------------------------------- +STATIC const mp_arg_t mach_rtc_init_args[] = { + { MP_QSTR_id, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_datetime, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_source, MP_ARG_OBJ, {.u_obj = mp_const_none} }, +}; + +//------------------------------------------------------------------------------------------------------------------------ +STATIC mp_obj_t mach_rtc_make_new(const mp_obj_type_t *type, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *all_args) { + // parse args + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, all_args + n_args); + mp_arg_val_t args[MP_ARRAY_SIZE(mach_rtc_init_args)]; + mp_arg_parse_all(n_args, all_args, &kw_args, MP_ARRAY_SIZE(args), mach_rtc_init_args, args); + + // check the peripheral id + if (args[0].u_int != 0) { + mp_raise_msg(&mp_type_OSError, "Resource not avaliable"); + } + + // setup the object + mach_rtc_obj_t *self = &mach_rtc_obj; + self->base.type = &mach_rtc_type; + + // set the time and date + if (args[1].u_obj != mp_const_none) { + mach_rtc_datetime(args[1].u_obj); + } + + // return constant object + return (mp_obj_t)&mach_rtc_obj; +} + +//-------------------------------------------------------------------------------------------- +STATIC mp_obj_t mach_rtc_init(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + // parse args + mp_arg_val_t args[MP_ARRAY_SIZE(mach_rtc_init_args) - 1]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(args), &mach_rtc_init_args[1], args); + mach_rtc_datetime(args[0].u_obj); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mach_rtc_init_obj, 1, mach_rtc_init); + +//----------------------------------------------- +STATIC mp_obj_t mach_rtc_now (mp_obj_t self_in) { + timeutils_struct_time_t tm; + uint64_t useconds; + + // get the time from the RTC + useconds = mach_rtc_get_us_since_epoch(); + timeutils_seconds_since_epoch_to_struct_time((useconds / 1000000ull), &tm); + + mp_obj_t tuple[8] = { + mp_obj_new_int(tm.tm_year), + mp_obj_new_int(tm.tm_mon), + mp_obj_new_int(tm.tm_mday), + mp_obj_new_int(tm.tm_hour), + mp_obj_new_int(tm.tm_min), + mp_obj_new_int(tm.tm_sec), + mp_obj_new_int(useconds % 1000000), + mp_const_none + }; + return mp_obj_new_tuple(8, tuple); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mach_rtc_now_obj, mach_rtc_now); + +//--------------------------------------------------------------------------------------------- +STATIC mp_obj_t mach_rtc_ntp_sync(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_server, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_update_period, MP_ARG_INT, {.u_int = 3600} }, + }; + + mach_rtc_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + sntp_update_period = args[1].u_int * 1000; + if (sntp_update_period < 60000) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "update period cannot be shorter than 60 s")); + } + + mach_rtc_obj.synced = false; + if (sntp_enabled()) { + sntp_stop(); + } + + self->sntp_server_name = args[0].u_obj; + sntp_setservername(0, (char *)mp_obj_str_get_str(self->sntp_server_name)); + if (strlen(sntp_getservername(0)) == 0) { + sntp_setservername(0, DEFAULT_SNTP_SERVER); + } + + // set datetime to 2000/01/01 for 'synced' method to corectly detect synchronization + mp_obj_t tuple[8] = { + mp_obj_new_int(2000), + mp_obj_new_int(1), + mp_obj_new_int(1), + mp_obj_new_int(0), + mp_obj_new_int(0), + mp_obj_new_int(0), + mp_obj_new_int(0), + mp_const_none + }; + mach_rtc_datetime(tuple); + + sntp_init(); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mach_rtc_ntp_sync_obj, 1, mach_rtc_ntp_sync); + +//------------------------------------------------------ +STATIC mp_obj_t mach_rtc_has_synced (mp_obj_t self_in) { + time_t now; + struct tm timeinfo; + time(&now); + localtime_r(&now, &timeinfo); + // Is time set? If not, tm_year will be (1970 - 1900). + if (timeinfo.tm_year > (2016 - 1900)) { + mach_rtc_obj.synced = true; + } + else { + mach_rtc_obj.synced = false; + } + + if (mach_rtc_obj.synced) { + return mp_const_true; + } else { + return mp_const_false; + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mach_rtc_has_synced_obj, mach_rtc_has_synced); + +//---------------------------------------------------------------------------------------------------- +STATIC mp_obj_t machine_rtc_wake_on_ext0(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum {ARG_pin, ARG_level}; + const mp_arg_t allowed_args[] = { + { MP_QSTR_pin, MP_ARG_OBJ, {.u_obj = mp_obj_new_int(machine_rtc_config.ext0_pin)} }, + { MP_QSTR_level, MP_ARG_BOOL, {.u_bool = machine_rtc_config.ext0_level} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + if (args[ARG_pin].u_obj == mp_const_none) { + machine_rtc_config.ext0_pin = -1; // "None" + } else { + gpio_num_t pin_id = machine_pin_get_id(args[ARG_pin].u_obj); + if (pin_id != machine_rtc_config.ext0_pin) { + if (!((1ll << pin_id) & MACHINE_RTC_VALID_EXT_PINS)) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "invalid pin")); + } + machine_rtc_config.ext0_pin = pin_id; + } + } + + machine_rtc_config.ext0_level = args[ARG_level].u_bool; + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_rtc_wake_on_ext0_obj, 1, machine_rtc_wake_on_ext0); + +//---------------------------------------------------------------------------------------------------- +STATIC mp_obj_t machine_rtc_wake_on_ext1(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum {ARG_pins, ARG_level}; + const mp_arg_t allowed_args[] = { + { MP_QSTR_pins, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_level, MP_ARG_BOOL, {.u_bool = machine_rtc_config.ext1_level} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + uint64_t ext1_pins = machine_rtc_config.ext1_pins; + + + // Check that all pins are allowed + if (args[ARG_pins].u_obj != mp_const_none) { + mp_uint_t len = 0; + mp_obj_t *elem; + mp_obj_get_array(args[ARG_pins].u_obj, &len, &elem); + ext1_pins = 0; + + for (int i = 0; i < len; i++) { + + gpio_num_t pin_id = machine_pin_get_id(elem[i]); + // mp_int_t pin = mp_obj_get_int(elem[i]); + uint64_t pin_bit = (1ll << pin_id); + + if (!(pin_bit & MACHINE_RTC_VALID_EXT_PINS)) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "invalid pin")); + break; + } + ext1_pins |= pin_bit; + } + } + + machine_rtc_config.ext1_level = args[ARG_level].u_bool; + machine_rtc_config.ext1_pins = ext1_pins; + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_rtc_wake_on_ext1_obj, 1, machine_rtc_wake_on_ext1); + + +//========================================================= +STATIC const mp_map_elem_t mach_rtc_locals_dict_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR_init), (mp_obj_t)&mach_rtc_init_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_now), (mp_obj_t)&mach_rtc_now_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_ntp_sync), (mp_obj_t)&mach_rtc_ntp_sync_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_synced), (mp_obj_t)&mach_rtc_has_synced_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_wake_on_ext0), (mp_obj_t)&machine_rtc_wake_on_ext0_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_wake_on_ext1), (mp_obj_t)&machine_rtc_wake_on_ext1_obj }, +}; +STATIC MP_DEFINE_CONST_DICT(mach_rtc_locals_dict, mach_rtc_locals_dict_table); + +//=================================== +const mp_obj_type_t mach_rtc_type = { + { &mp_type_type }, + .name = MP_QSTR_RTC, + .make_new = mach_rtc_make_new, + .locals_dict = (mp_obj_t)&mach_rtc_locals_dict, +}; diff --git a/esp32/machrtc.h b/esp32/machrtc.h new file mode 100644 index 000000000..7b033340f --- /dev/null +++ b/esp32/machrtc.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2016, Pycom Limited. + * + * This software is licensed under the GNU GPL version 3 or any + * later version, with permitted additional terms. For more information + * see the Pycom Licence v1.0 document supplied with this file, or + * available at https://www.pycom.io/opensource/licensing + */ + +#ifndef MACHRTC_H_ +#define MACHRTC_H_ + +extern const mp_obj_type_t mach_rtc_type; + +typedef struct { + uint64_t ext1_pins; // set bit == pin# + int8_t ext0_pin; // just the pin#, -1 == None + bool wake_on_touch : 1; + bool ext0_level : 1; + bool ext1_level : 1; +} machine_rtc_config_t; + + +void rtc_init0(void); +void mach_rtc_synced (void); +void mach_rtc_set_us_since_epoch(uint64_t nowus); +uint64_t mach_rtc_get_us_since_epoch(); + +#endif // MACHRTC_H_ diff --git a/esp32/main.c b/esp32/main.c index 091cbddc9..f58f1fd02 100644 --- a/esp32/main.c +++ b/esp32/main.c @@ -48,6 +48,8 @@ #include "uart.h" #include "modmachine.h" #include "mpthreadport.h" +#include "mpsleep.h" +#include "machrtc.h" // MicroPython runs as a task under FreeRTOS #define MP_TASK_PRIORITY (ESP_TASK_PRIO_MIN + 1) @@ -66,6 +68,8 @@ void mp_task(void *pvParameter) { #endif uart_init(); + mpsleep_init0(); // to get reset reason + soft_reset: // initialise the stack pointer for the main thread mp_stack_set_top((void *)sp); @@ -105,6 +109,7 @@ void mp_task(void *pvParameter) { #endif mp_hal_stdout_tx_str("PYB: soft reboot\r\n"); + mpsleep_signal_soft_reset(); // deinitialise peripherals machine_pins_deinit(); diff --git a/esp32/modesp.c b/esp32/modesp.c index 3e4101d23..7d18a3075 100644 --- a/esp32/modesp.c +++ b/esp32/modesp.c @@ -38,18 +38,27 @@ #include "drivers/dht/dht.h" #include "modesp.h" -STATIC wl_handle_t fs_handle = WL_INVALID_HANDLE; -STATIC size_t wl_sect_size = 4096; +#if MICROPY_SDMMC_USE_DRIVER +#include "driver/sdmmc_host.h" +#include "driver/sdmmc_defs.h" +#include "sdmmc_cmd.h" +#endif + +STATIC wl_handle_t fs_handle = WL_INVALID_HANDLE; // not initialized +STATIC size_t wl_sect_size = 4096; // will be set to actual size after initialization + +// esp32 partition configuration needed for wear leveling driver STATIC const esp_partition_t fs_part = { ESP_PARTITION_TYPE_DATA, //type ESP_PARTITION_SUBTYPE_DATA_FAT, //subtype - 0x200000, // address - 0x1FF000, // size (2MB) + MICROPY_INTERNALFS_START, // address (from mpconfigport.h) + MICROPY_INTERNALFS_SIZE, // size (from mpconfigport.h) "uPYpart", // label - 0 // encrypted + MICROPY_INTERNALFS_ENCRIPTED // encrypted (from mpconfigport.h) }; + STATIC mp_obj_t esp_flash_read(mp_obj_t offset_in, mp_obj_t buf_in) { mp_int_t offset = mp_obj_get_int(offset_in); mp_buffer_info_t bufinfo; @@ -88,14 +97,18 @@ STATIC mp_obj_t esp_flash_erase(mp_obj_t sector_in) { STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp_flash_erase_obj, esp_flash_erase); STATIC mp_obj_t esp_flash_size(void) { + // on first call to this function wear leveling partition is not yet initialized! if (fs_handle == WL_INVALID_HANDLE) { + // if wear leveling partition is not 'mounted', do it now esp_err_t res = wl_mount(&fs_part, &fs_handle); if (res != ESP_OK) { + // return the size less than 1MB to indicate an error return mp_obj_new_int_from_uint(0x010000); } wl_sect_size = wl_sector_size(fs_handle); } - return mp_obj_new_int_from_uint(0x1FF000); + // return usable file system size in bytes + return mp_obj_new_int_from_uint(wl_size(fs_handle)); } STATIC MP_DEFINE_CONST_FUN_OBJ_0(esp_flash_size_obj, esp_flash_size); @@ -105,10 +118,221 @@ STATIC mp_obj_t esp_flash_sec_size() { STATIC MP_DEFINE_CONST_FUN_OBJ_0(esp_flash_sec_size_obj, esp_flash_sec_size); STATIC IRAM_ATTR mp_obj_t esp_flash_user_start(void) { + // wL driver starts from address 0, which is mapped to configured physical Flash address return MP_OBJ_NEW_SMALL_INT(0); } STATIC MP_DEFINE_CONST_FUN_OBJ_0(esp_flash_user_start_obj, esp_flash_user_start); +#if MICROPY_SDMMC_USE_DRIVER + +// ======== SD Card support =========================================================================== + +/* + * ##### Using SDCard with sdmmc driver connection: ################################################### + +1-bit ESP32 pin | SDc SD uSD | Notes +----------------------|----------------|------------ +SCLK GPIO14 (MTMS) | CLK 5 5 | 10k pullup +MOSI GPIO15 (MTDO) | CMD 2 3 | 10k pullup +MISO GPIO2 | D0 7 7 | 10k pullup, pull low to go into download mode + GPIO4 | D1 8 8 | 10k pullup; not used in 1-line mode + GPIO12 (MTDI) | D2 9 1 | otherwise 10k pullup (see note below!); not used in 1-line mode +CS GPIO13 (MTCK) | D3 1 2 | 10k pullup needed at card side, even in 1-line mode +VDD 3.3V | VSS 4 4 | +GND GND | GND 3&6 6 | + N/C | CD | + N/C | WP | + +SDcard pinout uSDcard pinout + Contacts view + _________________ 1 2 3 4 5 6 7 8 +| | _______________ +| | |# # # # # # # #| +| | | | +| | | | +| | / | +| | / | +| | |_ | +| | | | +| #| / | +|# # # # # # # # / | | +|_______________/ | | + 8 7 6 5 4 3 2 1 9 |_________________| + + * #################################################################################################### +*/ + + +STATIC sdmmc_card_t sdmmc_card; +STATIC uint8_t sdcard_status = 1; + +//--------------------------------------------------------------- +STATIC void sdcard_print_info(const sdmmc_card_t* card, int mode) +{ + #if MICROPY_SDMMC_SHOW_INFO + printf("---------------------\n"); + if (mode == 1) { + printf(" Mode: 1-line mode\n"); + } else { + printf(" Mode: SD (4bit)\n"); + } + printf(" Name: %s\n", card->cid.name); + printf(" Type: %s\n", (card->ocr & SD_OCR_SDHC_CAP)?"SDHC/SDXC":"SDSC"); + printf("Speed: %s (%d MHz)\n", (card->csd.tr_speed > 25000000)?"high speed":"default speed", card->csd.tr_speed/1000000); + printf(" Size: %u MB\n", (uint32_t)(((uint64_t) card->csd.capacity) * card->csd.sector_size / (1024 * 1024))); + printf(" CSD: ver=%d, sector_size=%d, capacity=%d read_bl_len=%d\n", + card->csd.csd_ver, + card->csd.sector_size, card->csd.capacity, card->csd.read_block_len); + printf(" SCR: sd_spec=%d, bus_width=%d\n\n", card->scr.sd_spec, card->scr.bus_width); + #endif +} + +//---------------------------------------------- +STATIC mp_obj_t esp_sdcard_init(mp_obj_t mode) { + mp_int_t card_mode = mp_obj_get_int(mode); + + if (sdcard_status == 0) { + #if MICROPY_SDMMC_SHOW_INFO + printf("Already initialized:\n"); + sdcard_print_info(&sdmmc_card, card_mode); + #endif + return MP_OBJ_NEW_SMALL_INT(sdcard_status); + } + + // Configure sdmmc interface + sdmmc_host_t host = SDMMC_HOST_DEFAULT(); + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + + // Enable pull-ups on the SD card pins + // ** It is recommended to use external 10K pull-ups ** + gpio_set_pull_mode(2, GPIO_PULLUP_ONLY); + gpio_set_pull_mode(14, GPIO_PULLUP_ONLY); + gpio_set_pull_mode(15, GPIO_PULLUP_ONLY); + + if (card_mode == 1) { + // Use 1-line SD mode + host.flags = SDMMC_HOST_FLAG_1BIT; + slot_config.width = 1; + } + else { + gpio_set_pull_mode(4, GPIO_PULLUP_ONLY); + gpio_set_pull_mode(12, GPIO_PULLUP_ONLY); + gpio_set_pull_mode(13, GPIO_PULLUP_ONLY); + } + + sdmmc_host_init(); + sdmmc_host_init_slot(SDMMC_HOST_SLOT_1, &slot_config); + + // Initialize the sd card + esp_log_level_set("*", ESP_LOG_VERBOSE); + #if MICROPY_SDMMC_SHOW_INFO + printf("---------------------\n"); + printf("Initializing SD Card: "); + #endif + esp_err_t res = sdmmc_card_init(&host, &sdmmc_card); + esp_log_level_set("*", ESP_LOG_ERROR); + + if (res == ESP_OK) { + sdcard_status = ESP_OK; + #if MICROPY_SDMMC_SHOW_INFO + printf("OK.\n"); + #endif + sdcard_print_info(&sdmmc_card, card_mode); + } else { + sdcard_status = 1; + #if MICROPY_SDMMC_SHOW_INFO + printf("Error.\n\n"); + #endif + } + return MP_OBJ_NEW_SMALL_INT(sdcard_status); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp_sdcard_init_obj, esp_sdcard_init); + +//------------------------------------------------------------------------- +STATIC mp_obj_t esp_sdcard_read(mp_obj_t ulSectorNumber, mp_obj_t buf_in) { + mp_int_t sect_num = mp_obj_get_int(ulSectorNumber); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_WRITE); + mp_int_t sect_count = bufinfo.len / sdmmc_card.csd.sector_size; + mp_int_t sect_err = bufinfo.len % sdmmc_card.csd.sector_size; + + if (sdcard_status != ESP_OK) { + mp_raise_OSError(MP_EIO); + } + if (sect_count == 0) { + mp_raise_OSError(MP_EIO); + } + if (sect_err) { + mp_raise_OSError(MP_EIO); + } + + esp_err_t res = sdmmc_read_sectors(&sdmmc_card, bufinfo.buf, sect_num, sect_count); + if (res != ESP_OK) { + mp_raise_OSError(MP_EIO); + } + + //printf("[SD] read sect=%d, count=%d, size=%d\n", sect_num, sect_count, bufinfo.len); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(esp_sdcard_read_obj, esp_sdcard_read); + + +//-------------------------------------------------------------------------- +STATIC mp_obj_t esp_sdcard_write(mp_obj_t ulSectorNumber, mp_obj_t buf_in) { + mp_int_t sect_num = mp_obj_get_int(ulSectorNumber); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_READ); + mp_int_t sect_count = bufinfo.len / sdmmc_card.csd.sector_size; + mp_int_t sect_err = bufinfo.len % sdmmc_card.csd.sector_size; + + if (sdcard_status != ESP_OK) { + mp_raise_OSError(MP_EIO); + } + if (sect_count == 0) { + mp_raise_OSError(MP_EIO); + } + if (sect_err) { + mp_raise_OSError(MP_EIO); + } + + int res = sdmmc_write_sectors(&sdmmc_card, bufinfo.buf, sect_num, sect_count); + if (res != ESP_OK) { + mp_raise_OSError(MP_EIO); + } + + //printf("[SD] write sect=%d, count=%d, size=%d\n", sect_num, sect_count, bufinfo.len); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(esp_sdcard_write_obj, esp_sdcard_write); + + +//------------------------------------------- +STATIC mp_obj_t esp_sdcard_sect_count(void) { + if (sdcard_status == ESP_OK) { + return MP_OBJ_NEW_SMALL_INT(sdmmc_card.csd.capacity); + } + else { + return MP_OBJ_NEW_SMALL_INT(0); + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(esp_sdcard_sect_count_obj, esp_sdcard_sect_count); + +//------------------------------------------ +STATIC mp_obj_t esp_sdcard_sect_size(void) { + if (sdcard_status == ESP_OK) { + return MP_OBJ_NEW_SMALL_INT(sdmmc_card.csd.sector_size); + } + else { + return MP_OBJ_NEW_SMALL_INT(0); + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(esp_sdcard_sect_size_obj, esp_sdcard_sect_size); + + +// ======== ^^^^^^^^^^^^^^^ =========================================================================== + +#endif // MICROPY_SDMMC_USE_DRIVER + STATIC mp_obj_t esp_neopixel_write_(mp_obj_t pin, mp_obj_t buf, mp_obj_t timing) { mp_buffer_info_t bufinfo; @@ -129,6 +353,17 @@ STATIC const mp_rom_map_elem_t esp_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_flash_user_start), MP_ROM_PTR(&esp_flash_user_start_obj) }, { MP_ROM_QSTR(MP_QSTR_flash_sec_size), MP_ROM_PTR(&esp_flash_sec_size_obj) }, + #if MICROPY_SDMMC_USE_DRIVER + { MP_ROM_QSTR(MP_QSTR_sdcard_read), MP_ROM_PTR(&esp_sdcard_read_obj) }, + { MP_ROM_QSTR(MP_QSTR_sdcard_write), MP_ROM_PTR(&esp_sdcard_write_obj) }, + { MP_ROM_QSTR(MP_QSTR_sdcard_init), MP_ROM_PTR(&esp_sdcard_init_obj) }, + { MP_ROM_QSTR(MP_QSTR_sdcard_sect_count), MP_ROM_PTR(&esp_sdcard_sect_count_obj) }, + { MP_ROM_QSTR(MP_QSTR_sdcard_sect_size), MP_ROM_PTR(&esp_sdcard_sect_size_obj) }, + // class constants + { MP_ROM_QSTR(MP_QSTR_SD_1LINE), MP_ROM_INT(1) }, + { MP_ROM_QSTR(MP_QSTR_SD_4LINE), MP_ROM_INT(4) }, + #endif + { MP_ROM_QSTR(MP_QSTR_neopixel_write), MP_ROM_PTR(&esp_neopixel_write_obj) }, { MP_ROM_QSTR(MP_QSTR_dht_readinto), MP_ROM_PTR(&dht_readinto_obj) }, }; diff --git a/esp32/modmachine.c b/esp32/modmachine.c index 6f893dcca..1de05e3c7 100644 --- a/esp32/modmachine.c +++ b/esp32/modmachine.c @@ -29,6 +29,7 @@ #include #include +#include #include "freertos/FreeRTOS.h" #include "freertos/task.h" @@ -42,9 +43,14 @@ #include "extmod/machine_i2c.h" #include "extmod/machine_spi.h" #include "modmachine.h" +#include "esp_deep_sleep.h" +#include "mpsleep.h" +#include "machrtc.h" #if MICROPY_PY_MACHINE +extern machine_rtc_config_t machine_rtc_config; + STATIC mp_obj_t machine_freq(size_t n_args, const mp_obj_t *args) { if (n_args == 0) { // get @@ -96,6 +102,70 @@ STATIC mp_obj_t machine_enable_irq(mp_obj_t state_in) { } MP_DEFINE_CONST_FUN_OBJ_1(machine_enable_irq_obj, machine_enable_irq); + +//-------------------------------------------------------------------- +STATIC mp_obj_t machine_deepsleep(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + + enum {ARG_sleep_ms}; + const mp_arg_t allowed_args[] = { + { MP_QSTR_sleep_ms, MP_ARG_INT, { .u_int = 0 } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + + mp_int_t expiry = args[ARG_sleep_ms].u_int; + + if (expiry != 0) { + esp_deep_sleep_enable_timer_wakeup(expiry * 1000); + } + + if (machine_rtc_config.ext0_pin != -1) { + esp_deep_sleep_enable_ext0_wakeup(machine_rtc_config.ext0_pin, machine_rtc_config.ext0_level ? 1 : 0); + } + + if (machine_rtc_config.ext1_pins != 0) { + esp_deep_sleep_enable_ext1_wakeup( + machine_rtc_config.ext1_pins, + machine_rtc_config.ext1_level ? ESP_EXT1_WAKEUP_ANY_HIGH : ESP_EXT1_WAKEUP_ALL_LOW); + } + + if (machine_rtc_config.wake_on_touch) { + esp_deep_sleep_enable_touchpad_wakeup(); + } + + esp_deep_sleep_start(); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_deepsleep_obj, 0, machine_deepsleep); + +//------------------------------------------ +STATIC mp_obj_t machine_wake_reason (void) { + mpsleep_reset_cause_t reset_reason = mpsleep_get_reset_cause (); + mpsleep_wake_reason_t wake_reason = mpsleep_get_wake_reason(); + mp_obj_t tuple[2]; + + tuple[0] = mp_obj_new_int(wake_reason); + tuple[1] = mp_obj_new_int(reset_reason); + return mp_obj_new_tuple(2, tuple); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(machine_wake_reason_obj, machine_wake_reason); + +//---------------------------------------- +STATIC mp_obj_t machine_wake_desc (void) { + char reason[24] = { 0 }; + mp_obj_t tuple[2]; + + mpsleep_get_reset_desc(reason); + tuple[0] = mp_obj_new_str(reason, strlen(reason), 0); + mpsleep_get_wake_desc(reason); + tuple[1] = mp_obj_new_str(reason, strlen(reason), 0); + return mp_obj_new_tuple(2, tuple); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(machine_wake_desc_obj, machine_wake_desc); + + STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_umachine) }, @@ -107,6 +177,9 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&machine_reset_obj) }, { MP_ROM_QSTR(MP_QSTR_unique_id), MP_ROM_PTR(&machine_unique_id_obj) }, { MP_ROM_QSTR(MP_QSTR_idle), MP_ROM_PTR(&machine_idle_obj) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_deepsleep), MP_ROM_PTR(&machine_deepsleep_obj) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_wake_reason), MP_ROM_PTR(&machine_wake_reason_obj) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_wake_description), MP_ROM_PTR(&machine_wake_desc_obj) }, { MP_ROM_QSTR(MP_QSTR_disable_irq), MP_ROM_PTR(&machine_disable_irq_obj) }, { MP_ROM_QSTR(MP_QSTR_enable_irq), MP_ROM_PTR(&machine_enable_irq_obj) }, @@ -122,6 +195,7 @@ STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_PWM), MP_ROM_PTR(&machine_pwm_type) }, { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&mp_machine_soft_spi_type) }, { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&machine_uart_type) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_RTC), MP_ROM_PTR(&mach_rtc_type) }, }; STATIC MP_DEFINE_CONST_DICT(machine_module_globals, machine_module_globals_table); diff --git a/esp32/modules/flashbdev.py b/esp32/modules/flashbdev.py index ec41445d6..ef5de2883 100644 --- a/esp32/modules/flashbdev.py +++ b/esp32/modules/flashbdev.py @@ -6,8 +6,8 @@ class FlashBdev: SEC_SIZE = esp.flash_sec_size() START_SEC = esp.flash_user_start() // SEC_SIZE - def __init__(self, blocks): - self.blocks = blocks + def __init__(self): + self.blocks = self.FS_SIZE // self.SEC_SIZE def readblocks(self, n, buf): #print("readblocks(%s, %x(%d))" % (n, id(buf), len(buf))) @@ -27,9 +27,8 @@ def ioctl(self, op, arg): return self.SEC_SIZE size = esp.flash_size() -if size < 1024*1024: +if size < 512*1024: # flash too small for a filesystem bdev = None else: - # for now we use a fixed size for the filesystem - bdev = FlashBdev(size // FlashBdev.SEC_SIZE) + bdev = FlashBdev() diff --git a/esp32/modules/sdcard.py b/esp32/modules/sdcard.py new file mode 100644 index 000000000..5b9c2a080 --- /dev/null +++ b/esp32/modules/sdcard.py @@ -0,0 +1,46 @@ +""" +Micro Python driver for SD cards using esp-idf sd_emmc driver. + +Example usage on ESP32: + + import sdcard, uos, esp + sd = sdcard.SDCard(esp.SD_1LINE) + vfs = uos.VfsFat(sd) + uos.mount(vfs, '/sd') + uos.chdir('/sd') + uos.listdir() + +If 'automount' is used + + import sdcard, uos + sd = sdcard.SDCard(esp.SD_4LINE, True) + uos.listdir() + +""" + +import esp + +class SDCard: + def __init__(self, mode, automount): + self.SD_FOUND = esp.sdcard_init(mode) + self.SEC_COUNT = esp.sdcard_sect_count() + self.SEC_SIZE = esp.sdcard_sect_size() + self.SIZE = self.SEC_SIZE * self.SEC_COUNT + # automount sdcard if requested + if self.SD_FOUND == 0 and automount: + import uos + vfs = uos.VfsFat(self) + uos.mount(vfs, '/sd') + uos.chdir('/sd') + + def count(self): + return esp.sdcard_sect_count() + + def readblocks(self, block_num, buf): + esp.sdcard_read(block_num, buf) + return 0 + + def writeblocks(self, block_num, buf): + esp.sdcard_write(block_num, buf) + return 0 + diff --git a/esp32/mpconfigport.h b/esp32/mpconfigport.h index 1dadd397e..2ecce15e4 100644 --- a/esp32/mpconfigport.h +++ b/esp32/mpconfigport.h @@ -149,6 +149,15 @@ #define mp_type_fileio fatfs_type_fileio #define mp_type_textio fatfs_type_textio +// internal flash file system configuration +#define MICROPY_INTERNALFS_START (0x200000) +#define MICROPY_INTERNALFS_SIZE (0x200000) +#define MICROPY_INTERNALFS_ENCRIPTED (0) + +// sdcard using ESP32 sdmmc driver configuration +#define MICROPY_SDMMC_USE_DRIVER (1) +#define MICROPY_SDMMC_SHOW_INFO (1) + // use vfs's functions for import stat and builtin open #define mp_import_stat mp_vfs_import_stat #define mp_builtin_open mp_vfs_open diff --git a/esp32/mpsleep.c b/esp32/mpsleep.c new file mode 100644 index 000000000..3084ce9fa --- /dev/null +++ b/esp32/mpsleep.c @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2016, Pycom Limited. + * + * This software is licensed under the GNU GPL version 3 or any + * later version, with permitted additional terms. For more information + * see the Pycom Licence v1.0 document supplied with this file, or + * available at https://www.pycom.io/opensource/licensing + */ + +#include +#include + +#include "py/mpstate.h" +#include "py/runtime.h" +#include "py/mphal.h" + +#include "sdkconfig.h" +#include "rom/rtc.h" +#include "esp_system.h" +#include "esp_deep_sleep.h" +#include "mpsleep.h" + +/****************************************************************************** + DECLARE PRIVATE DATA + ******************************************************************************/ +STATIC mpsleep_reset_cause_t mpsleep_reset_cause = MPSLEEP_PWRON_RESET; +STATIC mpsleep_wake_reason_t mpsleep_wake_reason = MPSLEEP_PWRON_WAKE; + +/****************************************************************************** + DEFINE PUBLIC FUNCTIONS + ******************************************************************************/ +void mpsleep_init0 (void) { + // check the reset casue (if it's soft reset, leave it as it is) + switch (rtc_get_reset_reason(0)) { + case TG0WDT_SYS_RESET: + mpsleep_reset_cause = MPSLEEP_WDT_RESET; + break; + case DEEPSLEEP_RESET: + mpsleep_reset_cause = MPSLEEP_DEEPSLEEP_RESET; + break; + case RTCWDT_BROWN_OUT_RESET: + mpsleep_reset_cause = MPSLEEP_BROWN_OUT_RESET; + break; + case TG1WDT_SYS_RESET: // machine.reset() + mpsleep_reset_cause = MPSLEEP_HARD_RESET; + break; + case POWERON_RESET: + case RTCWDT_RTC_RESET: // silicon bug after power on + default: + mpsleep_reset_cause = MPSLEEP_PWRON_RESET; + break; + } + + // check the wakeup reason + switch (esp_deep_sleep_get_wakeup_cause()) { + case ESP_DEEP_SLEEP_WAKEUP_EXT0: + case ESP_DEEP_SLEEP_WAKEUP_EXT1: + mpsleep_wake_reason = MPSLEEP_GPIO_WAKE; + break; + case ESP_DEEP_SLEEP_WAKEUP_TIMER: + mpsleep_wake_reason = MPSLEEP_RTC_WAKE; + break; + case ESP_DEEP_SLEEP_WAKEUP_UNDEFINED: + default: + mpsleep_wake_reason = MPSLEEP_PWRON_WAKE; + break; + } +} + +void mpsleep_signal_soft_reset (void) { + mpsleep_reset_cause = MPSLEEP_SOFT_RESET; +} + +mpsleep_reset_cause_t mpsleep_get_reset_cause (void) { + return mpsleep_reset_cause; +} + +mpsleep_wake_reason_t mpsleep_get_wake_reason (void) { + return mpsleep_wake_reason; +} + +void mpsleep_get_reset_desc (char *reset_reason) { + switch (mpsleep_reset_cause) { + case MPSLEEP_PWRON_RESET: + sprintf(reset_reason, "Power on reset"); + break; + case MPSLEEP_HARD_RESET: + sprintf(reset_reason, "Hard reset"); + break; + case MPSLEEP_WDT_RESET: + sprintf(reset_reason, "WDT reset"); + break; + case MPSLEEP_DEEPSLEEP_RESET: + sprintf(reset_reason, "Deepsleep reset"); + break; + case MPSLEEP_SOFT_RESET: + sprintf(reset_reason, "Soft reset"); + break; + case MPSLEEP_BROWN_OUT_RESET: + sprintf(reset_reason, "Brownout reset"); + break; + default: + sprintf(reset_reason, "Unknown reset reason"); + break; + } +} + +void mpsleep_get_wake_desc (char *wake_reason) { + switch (mpsleep_wake_reason) { + case MPSLEEP_PWRON_WAKE: + sprintf(wake_reason, "Power on wake"); + break; + case MPSLEEP_GPIO_WAKE: + sprintf(wake_reason, "GPIO wake"); + break; + case MPSLEEP_RTC_WAKE: + sprintf(wake_reason, "RTC wake"); + break; + case MPSLEEP_ULP_WAKE: + sprintf(wake_reason, "ULP wake"); + break; + default: + sprintf(wake_reason, "Unknown wake reason"); + break; + } +} diff --git a/esp32/mpsleep.h b/esp32/mpsleep.h new file mode 100644 index 000000000..190ee6604 --- /dev/null +++ b/esp32/mpsleep.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2016, Pycom Limited. + * + * This software is licensed under the GNU GPL version 3 or any + * later version, with permitted additional terms. For more information + * see the Pycom Licence v1.0 document supplied with this file, or + * available at https://www.pycom.io/opensource/licensing + */ + +#ifndef MPSLEEP_H_ +#define MPSLEEP_H_ + +/****************************************************************************** + DEFINE CONSTANTS + ******************************************************************************/ + +/****************************************************************************** + DEFINE TYPES + ******************************************************************************/ + +typedef enum { + MPSLEEP_PWRON_RESET = 0, + MPSLEEP_HARD_RESET, + MPSLEEP_WDT_RESET, + MPSLEEP_DEEPSLEEP_RESET, + MPSLEEP_SOFT_RESET, + MPSLEEP_BROWN_OUT_RESET, +} mpsleep_reset_cause_t; + +typedef enum { + MPSLEEP_PWRON_WAKE = 0, + MPSLEEP_GPIO_WAKE, + MPSLEEP_RTC_WAKE, + MPSLEEP_ULP_WAKE +} mpsleep_wake_reason_t; + +/****************************************************************************** + DECLARE FUNCTIONS + ******************************************************************************/ +void mpsleep_init0 (void); +void mpsleep_signal_soft_reset (void); +mpsleep_reset_cause_t mpsleep_get_reset_cause (void); +mpsleep_wake_reason_t mpsleep_get_wake_reason (void); +void mpsleep_get_reset_desc (char *reset_reason); +void mpsleep_get_wake_desc (char *wake_reason); + +#endif /* MPSLEEP_H_ */ diff --git a/esp32/sdkconfig.h b/esp32/sdkconfig.h index 19b1b7035..9bbe4d3e2 100644 --- a/esp32/sdkconfig.h +++ b/esp32/sdkconfig.h @@ -19,7 +19,7 @@ #define CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ 240 #define CONFIG_ESP32_DEFAULT_CPU_FREQ_240 1 #define CONFIG_ESP32_DEBUG_OCDAWARE 1 -#define CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY 0 +#define CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY 5000 #define CONFIG_ESP32_ENABLE_COREDUMP_TO_NONE 1 #define CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE 1 #define CONFIG_ESP32_WIFI_AMPDU_ENABLED 1 @@ -49,7 +49,7 @@ #define CONFIG_FREERTOS_HZ 100 #define CONFIG_FREERTOS_ASSERT_FAIL_ABORT 1 #define CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION 1 -#define CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE 1 +#define CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY 1 #define CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS 2 #define CONFIG_FREERTOS_ISR_STACKSIZE 1536 #define CONFIG_FREERTOS_BREAK_ON_SCHEDULER_START_JTAG 1 @@ -69,7 +69,7 @@ #define CONFIG_PHY_ENABLED 1 #define CONFIG_WIFI_ENABLED 1 #define CONFIG_OPTIMIZATION_LEVEL_DEBUG 1 -#define CONFIG_MEMMAP_SMP 1 +#define CONFIG_MEMMAP_SMP 0 #define CONFIG_PARTITION_TABLE_SINGLE_APP 1 #define CONFIG_PARTITION_TABLE_FILENAME "partitions_singleapp.csv" @@ -108,3 +108,6 @@ #define CONFIG_TOOLPREFIX "xtensa-esp32-elf-" #define CONFIG_PYTHON "python2" + +#define CONFIG_IPC_TASK_STACK_SIZE 1024 +#define CONFIG_FLASHMODE_DIO 1 diff --git a/esp32/timeutils_epoch.c b/esp32/timeutils_epoch.c new file mode 100644 index 000000000..1ccf6a60b --- /dev/null +++ b/esp32/timeutils_epoch.c @@ -0,0 +1,212 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2015 Daniel Campora + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/obj.h" + +#include "timeutils_epoch.h" + +// the epoch is the UNIX 1970-01-01. +#define LEAPOCH (946684800LL + 86400*(31+29)) +#define DAYS_PER_400Y (365*400 + 97) +#define DAYS_PER_100Y (365*100 + 24) +#define DAYS_PER_4Y (365*4 + 1) + +//----------------------------------------------------------------------------------------------------------------------- + +STATIC const uint16_t days_since_jan1[]= { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }; + +STATIC bool timeutils_is_leap_year(mp_uint_t year) { + return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0; +} + +// month is one based +STATIC mp_uint_t timeutils_days_in_month(mp_uint_t year, mp_uint_t month) { + mp_uint_t mdays = days_since_jan1[month] - days_since_jan1[month - 1]; + if (month == 2 && timeutils_is_leap_year(year)) { + mdays++; + } + return mdays; +} + +// compute the day of the year, between 1 and 366 +// month should be between 1 and 12, date should start at 1 +STATIC mp_uint_t timeutils_year_day(mp_uint_t year, mp_uint_t month, mp_uint_t date) { + mp_uint_t yday = days_since_jan1[month - 1] + date; + if (month >= 3 && timeutils_is_leap_year(year)) { + yday += 1; + } + return yday; +} + +void timeutils_seconds_since_epoch_to_struct_time(mp_time_t t, timeutils_struct_time_t *tm) +{ + mp_time_t days, secs; + mp_int_t remdays, remsecs, remyears; + mp_int_t qc_cycles, c_cycles, q_cycles; + mp_int_t years, months; + mp_int_t wday, yday, leap; + static const char days_in_month[] = {31,30,31,30,31,31,30,31,30,31,31,29}; + + secs = t - LEAPOCH; + days = secs / 86400; + remsecs = secs % 86400; + if (remsecs < 0) { + remsecs += 86400; + days--; + } + + wday = (2 + days) % 7; + if (wday < 0) { + wday += 7; + } + + qc_cycles = days / DAYS_PER_400Y; + remdays = days % DAYS_PER_400Y; + if (remdays < 0) { + remdays += DAYS_PER_400Y; + qc_cycles--; + } + + c_cycles = remdays / DAYS_PER_100Y; + if (c_cycles == 4) { + c_cycles--; + } + remdays -= c_cycles * DAYS_PER_100Y; + + q_cycles = remdays / DAYS_PER_4Y; + if (q_cycles == 25) { + q_cycles--; + } + remdays -= q_cycles * DAYS_PER_4Y; + + remyears = remdays / 365; + if (remyears == 4) { + remyears--; + } + remdays -= remyears * 365; + + leap = !remyears && (q_cycles || !c_cycles); + yday = remdays + 31 + 28 + leap; + if (yday >= 365+leap) { + yday -= 365+leap; + } + + years = remyears + 4*q_cycles + 100*c_cycles + 400*qc_cycles; + + for (months=0; days_in_month[months] <= remdays; months++) { + remdays -= days_in_month[months]; + } + + tm->tm_year = 2000 + years; + tm->tm_mon = months + 2; + if (tm->tm_mon >= 12) { + tm->tm_mon -=12; + tm->tm_year++; + } + tm->tm_mon++; + + tm->tm_mday = remdays + 1; + tm->tm_wday = wday; + tm->tm_yday = yday + 1; + + tm->tm_hour = remsecs / 3600; + tm->tm_min = remsecs / 60 % 60; + tm->tm_sec = remsecs % 60; +} + +// returns the number of seconds, as an integer, since 1970-01-01 +mp_time_t timeutils_seconds_since_epoch(mp_uint_t year, mp_uint_t month, + mp_uint_t date, mp_uint_t hour, mp_uint_t minute, mp_uint_t second) { + + return + second + + minute * 60 + + hour * 3600 + + ((mp_time_t) (timeutils_year_day(year, month, date) - 1 + + ((year - 1972 + 3) / 4) // add a day each 4 years starting with 1973 + - ((year - 2000 + 99) / 100) // subtract a day each 100 years starting with 2001 + + ((year - 2000 + 399) / 400) // add a day each 400 years starting with 2001 + )) * 86400 + + ((mp_time_t) (year - 1970)) * 31536000; +} + +mp_time_t timeutils_mktime_64(mp_uint_t year, mp_int_t month, mp_int_t mday, + mp_int_t hours, mp_int_t minutes, mp_int_t seconds) { + + // Normalize the tuple. This allows things like: + // + // tm_tomorrow = list(time.localtime()) + // tm_tomorrow[2] += 1 # Adds 1 to mday + // tomorrow = time.mktime(tm_tommorrow) + // + // And not have to worry about all the weird overflows. + // + // You can subtract dates/times this way as well. + + minutes += seconds / 60; + if ((seconds = seconds % 60) < 0) { + seconds += 60; + minutes--; + } + + hours += minutes / 60; + if ((minutes = minutes % 60) < 0) { + minutes += 60; + hours--; + } + + mday += hours / 24; + if ((hours = hours % 24) < 0) { + hours += 24; + mday--; + } + + month--; // make month zero based + year += month / 12; + if ((month = month % 12) < 0) { + month += 12; + year--; + } + month++; // back to one based + + while (mday < 1) { + if (--month == 0) { + month = 12; + year--; + } + mday += timeutils_days_in_month(year, month); + } + while ((mp_uint_t)mday > timeutils_days_in_month(year, month)) { + mday -= timeutils_days_in_month(year, month); + if (++month == 13) { + month = 1; + year++; + } + } + + return timeutils_seconds_since_epoch(year, month, mday, hours, minutes, seconds); +} diff --git a/esp32/timeutils_epoch.h b/esp32/timeutils_epoch.h new file mode 100644 index 000000000..6ab2a2fe4 --- /dev/null +++ b/esp32/timeutils_epoch.h @@ -0,0 +1,54 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2015 Daniel Campora + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef __MICROPY_INCLUDED_LIB_TIMEUTILS_H__ +#define __MICROPY_INCLUDED_LIB_TIMEUTILS_H__ + +typedef struct _timeutils_struct_time_t { + uint16_t tm_year; // i.e. 2014 + uint8_t tm_mon; // 1..12 + uint8_t tm_mday; // 1..31 + uint8_t tm_hour; // 0..23 + uint8_t tm_min; // 0..59 + uint8_t tm_sec; // 0..59 + uint8_t tm_wday; // 0..6 0 = Monday + uint16_t tm_yday; // 1..366 +} timeutils_struct_time_t; + +typedef int64_t mp_time_t; + +void timeutils_seconds_since_epoch_to_struct_time(mp_time_t t, + timeutils_struct_time_t *tm); + +mp_time_t timeutils_seconds_since_epoch(mp_uint_t year, mp_uint_t month, + mp_uint_t date, mp_uint_t hour, mp_uint_t minute, mp_uint_t second); + +mp_time_t timeutils_mktime_64(mp_uint_t year, mp_int_t month, mp_int_t mday, + mp_int_t hours, mp_int_t minutes, mp_int_t seconds); + + +#endif // __MICROPY_INCLUDED_LIB_TIMEUTILS_H__ From 99a6f8f9da0df8c5e86c8841fcb2a6369f09ab19 Mon Sep 17 00:00:00 2001 From: loboris Date: Wed, 12 Jul 2017 16:44:45 +0200 Subject: [PATCH 4/5] bug in ntp_sync corrected --- esp32/machrtc.c | 3 ++- esp32/modesp.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/esp32/machrtc.c b/esp32/machrtc.c index 73848f48d..13132a876 100644 --- a/esp32/machrtc.c +++ b/esp32/machrtc.c @@ -268,7 +268,8 @@ STATIC mp_obj_t mach_rtc_ntp_sync(size_t n_args, const mp_obj_t *pos_args, mp_ma mp_obj_new_int(0), mp_const_none }; - mach_rtc_datetime(tuple); + mp_obj_t settime = mp_obj_new_tuple(8, tuple); + mach_rtc_datetime(settime); sntp_init(); diff --git a/esp32/modesp.c b/esp32/modesp.c index 7d18a3075..c7dcfe75c 100644 --- a/esp32/modesp.c +++ b/esp32/modesp.c @@ -224,7 +224,7 @@ STATIC mp_obj_t esp_sdcard_init(mp_obj_t mode) { sdmmc_host_init_slot(SDMMC_HOST_SLOT_1, &slot_config); // Initialize the sd card - esp_log_level_set("*", ESP_LOG_VERBOSE); + esp_log_level_set("*", ESP_LOG_NONE); #if MICROPY_SDMMC_SHOW_INFO printf("---------------------\n"); printf("Initializing SD Card: "); From d2b7d5235d8c355c95865b97ededce7814d65202 Mon Sep 17 00:00:00 2001 From: loboris Date: Wed, 12 Jul 2017 21:27:58 +0200 Subject: [PATCH 5/5] more bugs in ntp_sync corrected, should work now as expected --- esp32/machrtc.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/esp32/machrtc.c b/esp32/machrtc.c index 13132a876..5957e133f 100644 --- a/esp32/machrtc.c +++ b/esp32/machrtc.c @@ -107,6 +107,7 @@ uint64_t mach_rtc_get_us_since_epoch(void) { STATIC uint64_t mach_rtc_datetime_us(const mp_obj_t datetime) { timeutils_struct_time_t tm; uint64_t useconds; + uint32_t seconds; // set date and time mp_obj_t *items; @@ -141,7 +142,14 @@ STATIC uint64_t mach_rtc_datetime_us(const mp_obj_t datetime) { } else { tm.tm_hour = mp_obj_get_int(items[3]); } - useconds += 1000000ull * timeutils_seconds_since_epoch(tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); + seconds = timeutils_seconds_since_epoch(tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); + useconds += 1000000ull * seconds; + + struct timeval now; + now.tv_sec = seconds; + now.tv_usec = 0; + settimeofday(&now, NULL); + return useconds; } @@ -251,15 +259,17 @@ STATIC mp_obj_t mach_rtc_ntp_sync(size_t n_args, const mp_obj_t *pos_args, mp_ma sntp_stop(); } + sntp_setoperatingmode(SNTP_OPMODE_POLL); self->sntp_server_name = args[0].u_obj; sntp_setservername(0, (char *)mp_obj_str_get_str(self->sntp_server_name)); if (strlen(sntp_getservername(0)) == 0) { sntp_setservername(0, DEFAULT_SNTP_SERVER); } - // set datetime to 2000/01/01 for 'synced' method to corectly detect synchronization + // set datetime to 1970/01/01 (epoch=0) + // for 'synced' method to corectly detect synchronization mp_obj_t tuple[8] = { - mp_obj_new_int(2000), + mp_obj_new_int(1970), mp_obj_new_int(1), mp_obj_new_int(1), mp_obj_new_int(0), @@ -279,12 +289,13 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mach_rtc_ntp_sync_obj, 1, mach_rtc_ntp_sync); //------------------------------------------------------ STATIC mp_obj_t mach_rtc_has_synced (mp_obj_t self_in) { - time_t now; - struct tm timeinfo; - time(&now); - localtime_r(&now, &timeinfo); - // Is time set? If not, tm_year will be (1970 - 1900). - if (timeinfo.tm_year > (2016 - 1900)) { + uint32_t seconds; + + // get the time from the RTC + seconds = mach_rtc_get_us_since_epoch() / 1000000ull; + + // check if date > 2017/01/01 + if (seconds > 1483228800) { mach_rtc_obj.synced = true; } else {