diff --git a/ports/esp32/Makefile b/ports/esp32/Makefile new file mode 100644 index 0000000000..2eae802be5 --- /dev/null +++ b/ports/esp32/Makefile @@ -0,0 +1,719 @@ +include ../../py/mkenv.mk + +# qstr definitions (must come before including py.mk) +QSTR_DEFS = qstrdefsport.h + +MICROPY_PY_USSL = 0 +MICROPY_SSL_AXTLS = 0 +MICROPY_FATFS = 1 +MICROPY_PY_BTREE = 1 + +#FROZEN_DIR = scripts +FROZEN_MPY_DIR = modules + +# include py core make definitions +include $(TOP)/py/py.mk + +PORT ?= /dev/ttyUSB0 +BAUD ?= 460800 +FLASH_MODE ?= dio +FLASH_FREQ ?= 40m +FLASH_SIZE ?= 4MB +CROSS_COMPILE ?= xtensa-esp32-elf- + +# paths to ESP IDF and its components +ifeq ($(ESPIDF),) +$(error Please configure the ESPIDF variable) +endif +ESPCOMP = $(ESPIDF)/components +ESPTOOL ?= $(ESPCOMP)/esptool_py/esptool/esptool.py + +# verify the ESP IDF version +ESPIDF_SUPHASH := 9a26296a0e88a4c3ae27e9c848be970946fff87e +ESPIDF_CURHASH := $(shell git -C $(ESPIDF) show -s --pretty=format:'%H') +ifneq ($(ESPIDF_CURHASH),$(ESPIDF_SUPHASH)) +$(info ** WARNING **) +$(info The git hash of ESP IDF does not match the supported version) +$(info The build may complete and the firmware may work but it is not guaranteed) +$(info ESP IDF path: $(ESPIDF)) +$(info Current git hash: $(ESPIDF_CURHASH)) +$(info Supported git hash: $(ESPIDF_SUPHASH)) +endif + +# pretty format of ESP IDF version, used internally by the IDF +IDF_VER := $(shell git -C $(ESPIDF) describe) + +INC += -I. +INC += -I$(TOP) +INC += -I$(TOP)/lib/mp-readline +INC += -I$(TOP)/lib/netutils +INC += -I$(TOP)/lib/timeutils +INC += -I$(BUILD) + +INC_ESPCOMP += -I$(ESPCOMP)/bootloader_support/include +INC_ESPCOMP += -I$(ESPCOMP)/driver/include +INC_ESPCOMP += -I$(ESPCOMP)/driver/include/driver +INC_ESPCOMP += -I$(ESPCOMP)/nghttp/port/include +INC_ESPCOMP += -I$(ESPCOMP)/nghttp/nghttp2/lib/includes +INC_ESPCOMP += -I$(ESPCOMP)/esp32/include +INC_ESPCOMP += -I$(ESPCOMP)/soc/include +INC_ESPCOMP += -I$(ESPCOMP)/soc/esp32/include +INC_ESPCOMP += -I$(ESPCOMP)/ethernet/include +INC_ESPCOMP += -I$(ESPCOMP)/expat/include/expat +INC_ESPCOMP += -I$(ESPCOMP)/expat/port/include +INC_ESPCOMP += -I$(ESPCOMP)/heap/include +INC_ESPCOMP += -I$(ESPCOMP)/json/include +INC_ESPCOMP += -I$(ESPCOMP)/json/port/include +INC_ESPCOMP += -I$(ESPCOMP)/log/include +INC_ESPCOMP += -I$(ESPCOMP)/newlib/include +INC_ESPCOMP += -I$(ESPCOMP)/nvs_flash/include +INC_ESPCOMP += -I$(ESPCOMP)/freertos/include +INC_ESPCOMP += -I$(ESPCOMP)/tcpip_adapter/include +INC_ESPCOMP += -I$(ESPCOMP)/lwip/include/lwip +INC_ESPCOMP += -I$(ESPCOMP)/lwip/include/lwip/port +INC_ESPCOMP += -I$(ESPCOMP)/lwip/include/lwip/posix +INC_ESPCOMP += -I$(ESPCOMP)/mbedtls/include +INC_ESPCOMP += -I$(ESPCOMP)/mbedtls/port/include +INC_ESPCOMP += -I$(ESPCOMP)/spi_flash/include +INC_ESPCOMP += -I$(ESPCOMP)/vfs/include +INC_ESPCOMP += -I$(ESPCOMP)/newlib/platform_include +INC_ESPCOMP += -I$(ESPCOMP)/xtensa-debug-module/include +INC_ESPCOMP += -I$(ESPCOMP)/wpa_supplicant/include +INC_ESPCOMP += -I$(ESPCOMP)/wpa_supplicant/port/include +INC_ESPCOMP += -I$(ESPCOMP)/ethernet/include +INC_ESPCOMP += -I$(ESPCOMP)/app_trace/include + +CFLAGS_BASE = -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 +CFLAGS = $(CFLAGS_BASE) $(INC) $(INC_ESPCOMP) +CFLAGS += -DIDF_VER=\"$(IDF_VER)\" +CFLAGS += $(CFLAGS_MOD) + +# this is what ESPIDF uses for c++ compilation +CXXFLAGS = -std=gnu++11 -fno-exceptions -fno-rtti -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 -DESP_PLATFORM $(INC) $(INC_ESPCOMP) + +LDFLAGS = -nostdlib -Map=$(@:.elf=.map) --cref +LDFLAGS += --gc-sections -static -EL +LDFLAGS += -u call_user_start_cpu0 -u uxTopUsedPriority -u ld_include_panic_highint_hdl +LDFLAGS += -u __cxa_guard_dummy # so that implementation of static guards is taken from cxx_guards.o instead of libstdc++.a +LDFLAGS += -L$(ESPCOMP)/esp32/ld +LDFLAGS += -T $(BUILD)/esp32_out.ld +LDFLAGS += -T ./esp32.custom_common.ld +LDFLAGS += -T esp32.rom.ld +LDFLAGS += -T esp32.rom.spiram_incompatible_fns.ld +LDFLAGS += -T esp32.peripherals.ld + +LIBGCC_FILE_NAME = $(shell $(CC) $(CFLAGS) -print-libgcc-file-name) +LIBSTDCXX_FILE_NAME = $(shell $(CXX) $(CXXFLAGS) -print-file-name=libstdc++.a) + +# Debugging/Optimization +ifeq ($(DEBUG), 1) +CFLAGS += -g +COPT = -O0 +else +#CFLAGS += -fdata-sections -ffunction-sections +COPT += -Os -DNDEBUG +#LDFLAGS += --gc-sections +endif + +################################################################################ +# List of MicroPython source and object files + +SRC_C = \ + main.c \ + uart.c \ + gccollect.c \ + mphalport.c \ + fatfs_port.c \ + help.c \ + modutime.c \ + moduos.c \ + machine_timer.c \ + machine_pin.c \ + machine_touchpad.c \ + machine_adc.c \ + machine_dac.c \ + machine_pwm.c \ + machine_uart.c \ + modmachine.c \ + modnetwork.c \ + modsocket.c \ + modesp.c \ + moduhashlib.c \ + espneopixel.c \ + machine_hw_spi.c \ + mpthreadport.c \ + $(SRC_MOD) + +EXTMOD_SRC_C = $(addprefix extmod/,\ + modonewire.c \ + ) + +LIB_SRC_C = $(addprefix lib/,\ + libm/math.c \ + libm/fmodf.c \ + libm/roundf.c \ + libm/ef_sqrt.c \ + libm/kf_rem_pio2.c \ + libm/kf_sin.c \ + libm/kf_cos.c \ + libm/kf_tan.c \ + libm/ef_rem_pio2.c \ + libm/sf_sin.c \ + libm/sf_cos.c \ + libm/sf_tan.c \ + libm/sf_frexp.c \ + libm/sf_modf.c \ + libm/sf_ldexp.c \ + libm/asinfacosf.c \ + libm/atanf.c \ + libm/atan2f.c \ + mp-readline/readline.c \ + netutils/netutils.c \ + timeutils/timeutils.c \ + utils/pyexec.c \ + utils/interrupt_char.c \ + utils/sys_stdio_mphal.c \ + ) + +ifeq ($(MICROPY_FATFS), 1) +LIB_SRC_C += \ + lib/oofatfs/ff.c \ + lib/oofatfs/option/unicode.c +endif + +DRIVERS_SRC_C = $(addprefix drivers/,\ + dht/dht.c \ + ) + +OBJ_MP = +OBJ_MP += $(PY_O) +OBJ_MP += $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) +OBJ_MP += $(addprefix $(BUILD)/, $(EXTMOD_SRC_C:.c=.o)) +OBJ_MP += $(addprefix $(BUILD)/, $(LIB_SRC_C:.c=.o)) +OBJ_MP += $(addprefix $(BUILD)/, $(DRIVERS_SRC_C:.c=.o)) + +# List of sources for qstr extraction +SRC_QSTR += $(SRC_C) $(EXTMOD_SRC_C) $(LIB_SRC_C) $(DRIVERS_SRC_C) +# Append any auto-generated sources that are needed by sources listed in SRC_QSTR +SRC_QSTR_AUTO_DEPS += + +################################################################################ +# List of object files from the ESP32 IDF components + +ESPIDF_DRIVER_O = $(addprefix $(ESPCOMP)/driver/,\ + uart.o \ + periph_ctrl.o \ + ledc.o \ + gpio.o \ + timer.o \ + spi_master.o \ + spi_common.o \ + rtc_module.o \ + ) + +$(BUILD)/$(ESPCOMP)/esp32/dport_access.o: CFLAGS += -Wno-array-bounds +ESPIDF_ESP32_O = $(addprefix $(ESPCOMP)/esp32/,\ + panic.o \ + esp_timer.o \ + esp_timer_esp32.o \ + ets_timer_legacy.o \ + event_default_handlers.o \ + fast_crypto_ops.o \ + task_wdt.o \ + cache_err_int.o \ + clk.o \ + core_dump.o \ + cpu_start.o \ + gdbstub.o \ + crosscore_int.o \ + ipc.o \ + int_wdt.o \ + event_loop.o \ + hwcrypto/sha.o \ + hwcrypto/aes.o \ + lib_printf.o \ + freertos_hooks.o \ + system_api.o \ + hw_random.o \ + phy_init.o \ + intr_alloc.o \ + dport_access.o \ + wifi_init.o \ + ) + +ESPIDF_HEAP_O = $(addprefix $(ESPCOMP)/heap/,\ + heap_caps.o \ + heap_caps_init.o \ + multi_heap.o \ + ) + +ESPIDF_SOC_O = $(addprefix $(ESPCOMP)/soc/,\ + esp32/cpu_util.o \ + esp32/rtc_clk.o \ + esp32/rtc_init.o \ + esp32/rtc_pm.o \ + esp32/rtc_sleep.o \ + esp32/rtc_time.o \ + esp32/soc_memory_layout.o \ + ) + +ESPIDF_CXX_O = $(addprefix $(ESPCOMP)/cxx/,\ + cxx_guards.o \ + ) + +ESPIDF_ETHERNET_O = $(addprefix $(ESPCOMP)/ethernet/,\ + emac_dev.o \ + emac_main.o \ + ) + +$(BUILD)/$(ESPCOMP)/expat/%.o: CFLAGS += -Wno-unused-function +ESPIDF_EXPAT_O = $(addprefix $(ESPCOMP)/expat/,\ + library/xmltok_ns.o \ + library/xmltok.o \ + library/xmlparse.o \ + library/xmlrole.o \ + library/xmltok_impl.o \ + port/minicheck.o \ + port/expat_element.o \ + port/chardata.o \ + ) + +ESPIDF_PTHREAD_O = $(addprefix $(ESPCOMP)/pthread/,\ + pthread.o \ + ) + +# Assembler .S files need only basic flags, and in particular should not have +# -Os because that generates subtly different code. +# We also need custom CFLAGS for .c files because FreeRTOS has headers with +# generic names (eg queue.h) which can clash with other files in the port. +CFLAGS_ASM = -I$(ESPCOMP)/esp32/include -I$(ESPCOMP)/soc/esp32/include -I$(ESPCOMP)/freertos/include/freertos -I. +$(BUILD)/$(ESPCOMP)/freertos/portasm.o: CFLAGS = $(CFLAGS_ASM) +$(BUILD)/$(ESPCOMP)/freertos/xtensa_context.o: CFLAGS = $(CFLAGS_ASM) +$(BUILD)/$(ESPCOMP)/freertos/xtensa_intr_asm.o: CFLAGS = $(CFLAGS_ASM) +$(BUILD)/$(ESPCOMP)/freertos/xtensa_vectors.o: CFLAGS = $(CFLAGS_ASM) +$(BUILD)/$(ESPCOMP)/freertos/%.o: CFLAGS = $(CFLAGS_BASE) -I. $(INC_ESPCOMP) -I$(ESPCOMP)/freertos/include/freertos +ESPIDF_FREERTOS_O = $(addprefix $(ESPCOMP)/freertos/,\ + croutine.o \ + event_groups.o \ + FreeRTOS-openocd.o \ + list.o \ + portasm.o \ + port.o \ + queue.o \ + ringbuf.o \ + tasks.o \ + timers.o \ + xtensa_context.o \ + xtensa_init.o \ + xtensa_intr_asm.o \ + xtensa_intr.o \ + xtensa_overlay_os_hook.o \ + xtensa_vector_defaults.o \ + xtensa_vectors.o \ + ) + +ESPIDF_VFS_O = $(addprefix $(ESPCOMP)/vfs/,\ + vfs_uart.o \ + vfs.o \ + ) + +ESPIDF_JSON_O = $(addprefix $(ESPCOMP)/json/,\ + library/cJSON.o \ + port/cJSON_Utils.o \ + ) + +ESPIDF_LOG_O = $(addprefix $(ESPCOMP)/log/,\ + log.o \ + ) + +ESPIDF_XTENSA_DEBUG_MODULE_O = $(addprefix $(ESPCOMP)/xtensa-debug-module/,\ + eri.o \ + trax.o \ + ) + +ESPIDF_TCPIP_ADAPTER_O = $(addprefix $(ESPCOMP)/tcpip_adapter/,\ + tcpip_adapter_lwip.o \ + ) + +ESPIDF_APP_TRACE_O = $(addprefix $(ESPCOMP)/app_trace/,\ + app_trace.o \ + ) + +ESPIDF_NEWLIB_O = $(addprefix $(ESPCOMP)/newlib/,\ + time.o \ + syscalls.o \ + syscall_table.o \ + reent_init.o \ + locks.o \ + ) + +ESPIDF_NGHTTP_O = $(addprefix $(ESPCOMP)/nghttp/,\ + nghttp2/lib/nghttp2_http.o \ + nghttp2/lib/nghttp2_version.o \ + nghttp2/lib/nghttp2_mem.o \ + nghttp2/lib/nghttp2_hd_huffman.o \ + nghttp2/lib/nghttp2_rcbuf.o \ + nghttp2/lib/nghttp2_callbacks.o \ + nghttp2/lib/nghttp2_session.o \ + nghttp2/lib/nghttp2_stream.o \ + nghttp2/lib/nghttp2_hd.o \ + nghttp2/lib/nghttp2_priority_spec.o \ + nghttp2/lib/nghttp2_buf.o \ + nghttp2/lib/nghttp2_option.o \ + nghttp2/lib/nghttp2_npn.o \ + nghttp2/lib/nghttp2_helper.o \ + nghttp2/lib/nghttp2_frame.o \ + nghttp2/lib/nghttp2_outbound_item.o \ + nghttp2/lib/nghttp2_hd_huffman_data.o \ + nghttp2/lib/nghttp2_pq.o \ + nghttp2/lib/nghttp2_queue.o \ + nghttp2/lib/nghttp2_submit.o \ + nghttp2/lib/nghttp2_map.o \ + port/http_parser.o \ + ) + +ESPIDF_NVS_FLASH_O = $(addprefix $(ESPCOMP)/nvs_flash/,\ + src/nvs_types.o \ + src/nvs_page.o \ + src/nvs_item_hash_list.o \ + src/nvs_pagemanager.o \ + src/nvs_storage.o \ + src/nvs_api.o \ + ) + +ESPIDF_OPENSSL_O = $(addprefix $(ESPCOMP)/openssl/,\ + ) + +ESPIDF_SPI_FLASH_O = $(addprefix $(ESPCOMP)/spi_flash/,\ + flash_mmap.o \ + partition.o \ + spi_flash_rom_patch.o \ + cache_utils.o \ + flash_ops.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 \ + api/netbuf.o \ + api/api_lib.o \ + api/netifapi.o \ + api/tcpip.o \ + api/netdb.o \ + api/err.o \ + api/api_msg.o \ + api/sockets.o \ + apps/sntp/sntp.o \ + apps/dhcpserver.o \ + core/ipv4/ip_frag.o \ + core/ipv4/dhcp.o \ + core/ipv4/ip4_addr.o \ + core/ipv4/igmp.o \ + core/ipv4/ip4.o \ + core/ipv4/autoip.o \ + core/ipv4/icmp.o \ + core/ipv6/ip6_frag.o \ + core/ipv6/dhcp6.o \ + core/ipv6/inet6.o \ + core/ipv6/ip6_addr.o \ + core/ipv6/ip6.o \ + core/ipv6/nd6.o \ + core/ipv6/mld6.o \ + core/ipv6/ethip6.o \ + core/ipv6/icmp6.o \ + core/mem.o \ + core/init.o \ + core/memp.o \ + core/sys.o \ + core/tcp_in.o \ + core/dns.o \ + core/ip.o \ + core/pbuf.o \ + core/raw.o \ + core/tcp.o \ + core/def.o \ + core/netif.o \ + core/stats.o \ + core/timers.o \ + core/inet_chksum.o \ + core/udp.o \ + core/tcp_out.o \ + netif/slipif.o \ + netif/etharp.o \ + netif/ethernet.o \ + netif/lowpan6.o \ + netif/ethernetif.o \ + port/freertos/sys_arch.o \ + port/netif/wlanif.o \ + port/netif/ethernetif.o \ + ) + +ESPIDF_MBEDTLS_O = $(addprefix $(ESPCOMP)/mbedtls/,\ + library/entropy.o \ + library/pkcs12.o \ + library/ccm.o \ + library/pk.o \ + library/sha1.o \ + library/x509_csr.o \ + library/ssl_cli.o \ + library/ecp.o \ + library/blowfish.o \ + library/x509.o \ + library/ecp_curves.o \ + library/error.o \ + library/ssl_ticket.o \ + library/entropy_poll.o \ + library/cipher.o \ + library/version_features.o \ + library/ripemd160.o \ + library/rsa.o \ + library/md.o \ + library/md_wrap.o \ + library/sha256.o \ + library/dhm.o \ + library/ssl_cache.o \ + library/pkwrite.o \ + library/base64.o \ + library/asn1parse.o \ + library/ssl_tls.o \ + library/hmac_drbg.o \ + library/pem.o \ + library/version.o \ + library/gcm.o \ + library/memory_buffer_alloc.o \ + library/md2.o \ + library/ecdsa.o \ + library/ssl_srv.o \ + library/x509_crt.o \ + library/ecdh.o \ + library/asn1write.o \ + library/md4.o \ + library/debug.o \ + library/x509_create.o \ + library/ecjpake.o \ + library/oid.o \ + library/md5.o \ + library/ssl_ciphersuites.o \ + library/sha512.o \ + library/xtea.o \ + library/aes.o \ + library/cipher_wrap.o \ + library/arc4.o \ + library/bignum.o \ + library/pkparse.o \ + library/padlock.o \ + library/threading.o \ + library/x509_crl.o \ + library/pkcs11.o \ + library/aesni.o \ + library/timing.o \ + library/certs.o \ + library/pkcs5.o \ + library/ssl_cookie.o \ + library/camellia.o \ + library/havege.o \ + library/des.o \ + library/x509write_csr.o \ + library/platform.o \ + library/ctr_drbg.o \ + library/x509write_crt.o \ + library/pk_wrap.o \ + port/esp_bignum.o \ + port/esp_hardware.o \ + port/esp_sha1.o \ + port/esp_sha256.o \ + port/esp_sha512.o \ + ) + +$(BUILD)/$(ESPCOMP)/wpa_supplicant/%.o: CFLAGS += -DEMBEDDED_SUPP -D__ets__ -Wno-strict-aliasing +ESPIDF_WPA_SUPPLICANT_O = $(addprefix $(ESPCOMP)/wpa_supplicant/,\ + src/crypto/aes-internal-enc.o \ + src/crypto/sha256-internal.o \ + src/crypto/md5-internal.o \ + src/crypto/aes-internal.o \ + src/crypto/sha1.o \ + src/crypto/aes-internal-dec.o \ + src/crypto/aes-unwrap.o \ + src/crypto/crypto_internal-rsa.o \ + src/crypto/dh_groups.o \ + src/crypto/crypto_internal.o \ + src/crypto/aes-wrap.o \ + src/crypto/sha1-internal.o \ + src/crypto/dh_group5.o \ + src/crypto/sha256.o \ + src/crypto/rc4.o \ + src/crypto/md5.o \ + src/crypto/aes-cbc.o \ + src/crypto/sha1-pbkdf2.o \ + src/crypto/bignum.o \ + src/crypto/crypto_internal-modexp.o \ + src/crypto/crypto_internal-cipher.o \ + src/fast_crypto/fast_aes-unwrap.o \ + src/fast_crypto/fast_aes-wrap.o \ + src/fast_crypto/fast_sha256.o \ + src/fast_crypto/fast_sha256-internal.o \ + port/os_xtensa.o \ + ) + +OBJ_ESPIDF = +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_NEWLIB_O)) +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_DRIVER_O)) +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_ESP32_O)) +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_HEAP_O)) +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_SOC_O)) +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_CXX_O)) +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_ETHERNET_O)) +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_EXPAT_O)) +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_PTHREAD_O)) +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_FREERTOS_O)) +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_VFS_O)) +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_JSON_O)) +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_LOG_O)) +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_LWIP_O)) +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_MBEDTLS_O)) +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_XTENSA_DEBUG_MODULE_O)) +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_TCPIP_ADAPTER_O)) +OBJ_ESPIDF += $(addprefix $(BUILD)/, $(ESPIDF_APP_TRACE_O)) +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_WPA_SUPPLICANT_O)) +################################################################################ +# Main targets + +all: $(BUILD)/firmware.bin + +.PHONY: idf-version deploy erase + +idf-version: + $(ECHO) "ESP IDF supported hash: $(ESPIDF_SUPHASH)" + +$(BUILD)/firmware.bin: $(BUILD)/bootloader.bin $(BUILD)/partitions.bin $(BUILD)/application.bin + $(ECHO) "Create $@" + $(Q)$(PYTHON) makeimg.py $^ $@ + +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) 0x1000 $^ + +erase: + $(ECHO) "Erasing flash" + $(Q)$(ESPTOOL) --chip esp32 --port $(PORT) --baud $(BAUD) erase_flash + +################################################################################ +# Declarations to build the application + +OBJ = $(OBJ_MP) $(OBJ_ESPIDF) + +APP_LD_ARGS = +APP_LD_ARGS += $(LDFLAGS_MOD) +APP_LD_ARGS += --start-group +APP_LD_ARGS += -L$(dir $(LIBGCC_FILE_NAME)) -lgcc +APP_LD_ARGS += -L$(dir $(LIBSTDCXX_FILE_NAME)) -lstdc++ +APP_LD_ARGS += $(ESPCOMP)/newlib/lib/libc.a +APP_LD_ARGS += $(ESPCOMP)/newlib/lib/libm.a +APP_LD_ARGS += $(ESPCOMP)/esp32/libhal.a +APP_LD_ARGS += -L$(ESPCOMP)/esp32/lib -lcore -lnet80211 -lphy -lrtc -lpp -lwpa -lsmartconfig -lcoexist +APP_LD_ARGS += $(OBJ) +APP_LD_ARGS += --end-group + +$(BUILD)/esp32_out.ld: sdkconfig.h + $(Q)$(CC) -I. -C -P -x c -E $(ESPCOMP)/esp32/ld/esp32.ld -o $@ + +$(BUILD)/application.bin: $(BUILD)/application.elf + $(ECHO) "Create $@" + $(Q)$(ESPTOOL) --chip esp32 elf2image --flash_mode $(FLASH_MODE) --flash_freq $(FLASH_FREQ) --flash_size $(FLASH_SIZE) $< + +$(BUILD)/application.elf: $(OBJ) $(BUILD)/esp32_out.ld + $(ECHO) "LINK $@" + $(Q)$(LD) $(LDFLAGS) -o $@ $(APP_LD_ARGS) + $(Q)$(SIZE) $@ + +define compile_cxx +$(ECHO) "CXX $<" +$(Q)$(CXX) $(CXXFLAGS) -c -MD -o $@ $< +@# The following fixes the dependency file. +@# See http://make.paulandlesley.org/autodep.html for details. +@# Regex adjusted from the above to play better with Windows paths, etc. +@$(CP) $(@:.o=.d) $(@:.o=.P); \ + $(SED) -e 's/#.*//' -e 's/^.*: *//' -e 's/ *\\$$//' \ + -e '/^$$/ d' -e 's/$$/ :/' < $(@:.o=.d) >> $(@:.o=.P); \ + $(RM) -f $(@:.o=.d) +endef + +vpath %.cpp . $(TOP) +$(BUILD)/%.o: %.cpp + $(call compile_cxx) + +################################################################################ +# Declarations to build the bootloader + +$(BUILD)/bootloader/$(ESPCOMP)/%.o: CFLAGS += -DBOOTLOADER_BUILD=1 -I$(ESPCOMP)/bootloader_support/include_priv -I$(ESPCOMP)/bootloader_support/include -I$(ESPCOMP)/micro-ecc/micro-ecc -I$(ESPCOMP)/esp32 -Wno-error=format +BOOTLOADER_OBJ = $(addprefix $(BUILD)/bootloader/$(ESPCOMP)/,\ + bootloader_support/src/bootloader_flash.o \ + bootloader_support/src/bootloader_random.o \ + bootloader_support/src/bootloader_sha.o \ + bootloader_support/src/secure_boot_signatures.o \ + bootloader_support/src/secure_boot.o \ + bootloader_support/src/esp_image_format.o \ + bootloader_support/src/flash_encrypt.o \ + bootloader_support/src/flash_partitions.o \ + log/log.o \ + spi_flash/spi_flash_rom_patch.o \ + soc/esp32/rtc_clk.o \ + soc/esp32/rtc_time.o \ + micro-ecc/micro-ecc/uECC.o \ + bootloader/subproject/main/bootloader_start.o \ + ) + +BOOTLOADER_LIBS = +BOOTLOADER_LIBS += -Wl,--start-group +BOOTLOADER_LIBS += $(BOOTLOADER_OBJ) +BOOTLOADER_LIBS += -L$(ESPCOMP)/esp32/lib -lrtc +BOOTLOADER_LIBS += -L$(dir $(LIBGCC_FILE_NAME)) -lgcc +BOOTLOADER_LIBS += -Wl,--end-group + +BOOTLOADER_LDFLAGS = +BOOTLOADER_LDFLAGS += -nostdlib +BOOTLOADER_LDFLAGS += -L$(ESPIDF)/lib +BOOTLOADER_LDFLAGS += -L$(ESPIDF)/ld +BOOTLOADER_LDFLAGS += -u call_user_start_cpu0 +BOOTLOADER_LDFLAGS += -Wl,--gc-sections +BOOTLOADER_LDFLAGS += -static +BOOTLOADER_LDFLAGS += -Wl,-EL +BOOTLOADER_LDFLAGS += -Wl,-Map=$(@:.elf=.map) -Wl,--cref +BOOTLOADER_LDFLAGS += -T $(ESPCOMP)/bootloader/subproject/main/esp32.bootloader.ld +BOOTLOADER_LDFLAGS += -T $(ESPCOMP)/bootloader/subproject/main/esp32.bootloader.rom.ld +BOOTLOADER_LDFLAGS += -T $(ESPCOMP)/esp32/ld/esp32.rom.ld +BOOTLOADER_LDFLAGS += -T $(ESPCOMP)/esp32/ld/esp32.rom.spiram_incompatible_fns.ld + +BOOTLOADER_OBJ_DIRS = $(sort $(dir $(BOOTLOADER_OBJ))) +$(BOOTLOADER_OBJ): | $(BOOTLOADER_OBJ_DIRS) +$(BOOTLOADER_OBJ_DIRS): + $(MKDIR) -p $@ + +$(BUILD)/bootloader/%.o: %.c + $(call compile_c) + +$(BUILD)/bootloader.bin: $(BUILD)/bootloader.elf + $(ECHO) "Create $@" + $(Q)$(ESPTOOL) --chip esp32 elf2image --flash_mode $(FLASH_MODE) --flash_freq $(FLASH_FREQ) --flash_size $(FLASH_SIZE) $< + +$(BUILD)/bootloader.elf: $(BOOTLOADER_OBJ) + $(ECHO) "LINK $@" + $(Q)$(CC) $(BOOTLOADER_LDFLAGS) -o $@ $(BOOTLOADER_LIBS) + +################################################################################ +# Declarations to build the partitions + +PYTHON2 ?= python2 +PART_SRC = $(ESPCOMP)/partition_table/partitions_singleapp.csv + +$(BUILD)/partitions.bin: $(PART_SRC) + $(ECHO) "Create $@" + $(Q)$(PYTHON2) $(ESPCOMP)/partition_table/gen_esp32part.py -q $< $@ + +################################################################################ + +include $(TOP)/py/mkrules.mk diff --git a/ports/esp32/README.md b/ports/esp32/README.md new file mode 100644 index 0000000000..7591f040a6 --- /dev/null +++ b/ports/esp32/README.md @@ -0,0 +1,199 @@ +MicroPython port to the ESP32 +============================= + +This is an experimental port of MicroPython to the Espressif ESP32 +microcontroller. It uses the ESP-IDF framework and MicroPython runs as +a task under FreeRTOS. + +Supported features include: +- REPL (Python prompt) over UART0. +- 16k stack for the MicroPython task and 64k Python heap. +- Many of MicroPython's features are enabled: unicode, arbitrary-precision + integers, single-precision floats, complex numbers, frozen bytecode, as + well as many of the internal modules. +- Internal filesystem using the flash (currently 256k in size). +- The machine module with basic GPIO and bit-banging I2C, SPI support. + +Development of this ESP32 port was sponsored in part by Microbric Pty Ltd. + +Setting up the toolchain and ESP-IDF +------------------------------------ + +There are two main components that are needed to build the firmware: +- the Xtensa cross-compiler that targets the CPU in the ESP32 (this is + different to the compiler used by the ESP8266) +- the Espressif IDF (IoT development framework, aka SDK) + +Instructions for setting up both of these components are provided by the +ESP-IDF itself, which is found at https://github.com/espressif/esp-idf . +Follow the guide "Setting Up ESP-IDF", for Windows, Mac or Linux. You +only need to perform up to "Step 2" of the guide, by which stage you +should have installed the cross-compile and cloned the ESP-IDF repository. + +If you are on a Windows machine then the +[Windows Subsystem for Linux](https://msdn.microsoft.com/en-au/commandline/wsl/install_guide) +is the most efficient way to install the ESP32 toolchain and build the project. +If you use WSL then follow the +[Linux guidelines](http://esp-idf.readthedocs.io/en/latest/get-started/linux-setup.html) +for the ESP-IDF instead of the Windows ones. + +Be advised that the ESP-IDF is still undergoing changes and only some +versions are supported. To find which build is compatible refer to the line +in the makefile containing the following: +``` +ESPIDF_SUPHASH := +``` +After finishing "Step 2" you can roll back your current build of +the ESP-IDF (and update the submodules accordingly) using: +``` +$ git checkout +$ git submodule update --recursive +``` +Note that you will get a warning when building the code if the ESP-IDF +version is incorrect. + +The Espressif ESP-IDF instructions above only install pyserial for Python 2, +so if you're running Python 3 or a non-system Python you'll also need to +install `pyserial` (or `esptool`) so that the Makefile can flash the board +and set parameters: +```bash +$ pip install pyserial +``` + +Once everything is set up you should have a functioning toolchain with +prefix xtensa-esp32-elf- (or otherwise if you configured it differently) +as well as a copy of the ESP-IDF repository. + +You then need to set the `ESPIDF` environment/makefile variable to point to +the root of the ESP-IDF repository. You can set the variable in your PATH, +or at the command line when calling make, or in your own custom `makefile`. +The last option is recommended as it allows you to easily configure other +variables for the build. In that case, create a new file in the esp32 +directory called `makefile` and add the following lines to that file: +``` +ESPIDF = +#PORT = /dev/ttyUSB0 +#FLASH_MODE = qio +#FLASH_SIZE = 4MB +#CROSS_COMPILE = xtensa-esp32-elf- + +include Makefile +``` +Be sure to enter the correct path to your local copy of the IDF repository +(and use `$(HOME)`, not tilde, to reference your home directory). +If your filesystem is case-insensitive then you'll need to use `GNUmakefile` +instead of `makefile`. +If the Xtensa cross-compiler is not in your path you can use the +`CROSS_COMPILE` variable to set its location. Other options of interest +are `PORT` for the serial port of your esp32 module, and `FLASH_MODE` +(which may need to be `dio` for some modules) +and `FLASH_SIZE`. See the Makefile for further information. + +Building the firmware +--------------------- + +The MicroPython cross-compiler must be built to pre-compile some of the +built-in scripts to bytecode. This can be done by (from the root of +this repository): +```bash +$ make -C mpy-cross +``` + +The ESP32 port has a dependency on Berkeley DB, which is an external +dependency (git submodule). You'll need to have git initialize that +module using the commands: +```bash +$ git submodule init lib/berkeley-db-1.xx +$ git submodule update +``` + +Then to build MicroPython for the ESP32 run: +```bash +$ cd ports/esp32 +$ make +``` +This will produce binary firmware images in the `build/` subdirectory +(three of them: bootloader.bin, partitions.bin and application.bin). + +To flash the firmware you must have your ESP32 module in the bootloader +mode and connected to a serial port on your PC. Refer to the documentation +for your particular ESP32 module for how to do this. The serial port and +flash settings are set in the `Makefile`, and can be overridden in your +local `makefile`; see above for more details. + +You will also need to have user permissions to access the /dev/ttyUSB0 device. +On Linux, you can enable this by adding your user to the `dialout` group, +and rebooting or logging out and in again. +```bash +$ sudo adduser dialout +``` + +If you are installing MicroPython to your module for the first time, or +after installing any other firmware, you should first erase the flash +completely: +```bash +$ make erase +``` + +To flash the MicroPython firmware to your ESP32 use: +```bash +$ make deploy +``` +This will use the `esptool.py` script (provided by ESP-IDF) to download the +binary images. + +Getting a Python prompt +----------------------- + +You can get a prompt via the serial port, via UART0, which is the same UART +that is used for programming the firmware. The baudrate for the REPL is +115200 and you can use a command such as: +```bash +$ picocom -b 115200 /dev/ttyUSB0 +``` + +Configuring the WiFi and using the board +---------------------------------------- + +The ESP32 port is designed to be (almost) equivalent to the ESP8266 in +terms of the modules and user-facing API. There are some small differences, +notably that the ESP32 does not automatically connect to the last access +point when booting up. But for the most part the documentation and tutorials +for the ESP8266 should apply to the ESP32 (at least for the components that +are implemented). + +See http://docs.micropython.org/en/latest/esp8266/esp8266/quickref.html for +a quick reference, and http://docs.micropython.org/en/latest/esp8266/esp8266/tutorial/intro.html +for a tutorial. + +The following function can be used to connect to a WiFi access point (you can +either pass in your own SSID and password, or change the defaults so you can +quickly call `wlan_connect()` and it just works): +```python +def wlan_connect(ssid='MYSSID', password='MYPASS'): + import network + wlan = network.WLAN(network.STA_IF) + if not wlan.active() or not wlan.isconnected(): + wlan.active(True) + print('connecting to:', ssid) + wlan.connect(ssid, password) + while not wlan.isconnected(): + pass + print('network config:', wlan.ifconfig()) +``` + +Note that some boards require you to configure the WiFi antenna before using +the WiFi. On Pycom boards like the LoPy and WiPy 2.0 you need to execute the +following code to select the internal antenna (best to put this line in your +boot.py file): +```python +import machine +antenna = machine.Pin(16, machine.Pin.OUT, value=0) +``` + +Troubleshooting +--------------- + +* Continuous reboots after programming: Ensure FLASH_MODE is correct for your + board (e.g. ESP-WROOM-32 should be DIO). Then perform a `make clean`, rebuild, + redeploy. diff --git a/ports/esp32/esp32.custom_common.ld b/ports/esp32/esp32.custom_common.ld new file mode 100644 index 0000000000..45242f9af2 --- /dev/null +++ b/ports/esp32/esp32.custom_common.ld @@ -0,0 +1,215 @@ +/* Default entry point: */ +ENTRY(call_start_cpu0); + +SECTIONS +{ + /* RTC fast memory holds RTC wake stub code, + including from any source file named rtc_wake_stub*.c + */ + .rtc.text : + { + . = ALIGN(4); + *(.rtc.literal .rtc.text) + *rtc_wake_stub*.o(.literal .text .literal.* .text.*) + } >rtc_iram_seg + + /* RTC slow memory holds RTC wake stub + data/rodata, including from any source file + named rtc_wake_stub*.c + */ + .rtc.data : + { + _rtc_data_start = ABSOLUTE(.); + *(.rtc.data) + *(.rtc.rodata) + *rtc_wake_stub*.o(.data .rodata .data.* .rodata.* .bss .bss.*) + _rtc_data_end = ABSOLUTE(.); + } > rtc_slow_seg + + /* RTC bss, from any source file named rtc_wake_stub*.c */ + .rtc.bss (NOLOAD) : + { + _rtc_bss_start = ABSOLUTE(.); + *rtc_wake_stub*.o(.bss .bss.*) + *rtc_wake_stub*.o(COMMON) + _rtc_bss_end = ABSOLUTE(.); + } > rtc_slow_seg + + /* Send .iram0 code to iram */ + .iram0.vectors : + { + /* Vectors go to IRAM */ + _init_start = ABSOLUTE(.); + /* Vectors according to builds/RF-2015.2-win32/esp108_v1_2_s5_512int_2/config.html */ + . = 0x0; + KEEP(*(.WindowVectors.text)); + . = 0x180; + KEEP(*(.Level2InterruptVector.text)); + . = 0x1c0; + KEEP(*(.Level3InterruptVector.text)); + . = 0x200; + KEEP(*(.Level4InterruptVector.text)); + . = 0x240; + KEEP(*(.Level5InterruptVector.text)); + . = 0x280; + KEEP(*(.DebugExceptionVector.text)); + . = 0x2c0; + KEEP(*(.NMIExceptionVector.text)); + . = 0x300; + KEEP(*(.KernelExceptionVector.text)); + . = 0x340; + KEEP(*(.UserExceptionVector.text)); + . = 0x3C0; + KEEP(*(.DoubleExceptionVector.text)); + . = 0x400; + *(.*Vector.literal) + + *(.UserEnter.literal); + *(.UserEnter.text); + . = ALIGN (16); + *(.entry.text) + *(.init.literal) + *(.init) + _init_end = ABSOLUTE(.); + + /* This goes here, not at top of linker script, so addr2line finds it last, + and uses it in preference to the first symbol in IRAM */ + _iram_start = ABSOLUTE(0); + } > iram0_0_seg + + .iram0.text : + { + /* Code marked as runnning out of IRAM */ + _iram_text_start = ABSOLUTE(.); + *(.iram1 .iram1.*) + *freertos/*(.literal .text .literal.* .text.*) + *heap/multi_heap.o(.literal .text .literal.* .text.*) + *heap/multi_heap_poisoning.o(.literal .text .literal.* .text.*) + *esp32/panic.o(.literal .text .literal.* .text.*) + *esp32/core_dump.o(.literal .text .literal.* .text.*) + *app_trace/*(.literal .text .literal.* .text.*) + *xtensa-debug-module/eri.o(.literal .text .literal.* .text.*) + *esp32/app_trace.o(.literal .text .literal.* .text.*) + *libphy.a:(.literal .text .literal.* .text.*) + *librtc.a:(.literal .text .literal.* .text.*) + *libsoc.a:(.literal .text .literal.* .text.*) + *libhal.a:(.literal .text .literal.* .text.*) + *libgcc.a:lib2funcs.o(.literal .text .literal.* .text.*) + *spi_flash/spi_flash_rom_patch.o(.literal .text .literal.* .text.*) + *py/scheduler.o*(.literal .text .literal.* .text.*) + _iram_text_end = ABSOLUTE(.); + } > iram0_0_seg + + .dram0.data : + { + _data_start = ABSOLUTE(.); + *(.data) + *(.data.*) + *(.gnu.linkonce.d.*) + *(.data1) + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s.*) + *(.sdata2) + *(.sdata2.*) + *(.gnu.linkonce.s2.*) + *(.jcr) + *(.dram1 .dram1.*) + *esp32/panic.o(.rodata .rodata.*) + *libphy.a:(.rodata .rodata.*) + *app_trace/app_trace.o:(.rodata .rodata.*) + *heap/multi_heap.o(.rodata .rodata.*) + *heap/multi_heap_poisoning.o(.rodata .rodata.*) + _data_end = ABSOLUTE(.); + . = ALIGN(4); + } >dram0_0_seg + + /* Shared RAM */ + .dram0.bss (NOLOAD) : + { + . = ALIGN (8); + _bss_start = ABSOLUTE(.); + *(.dynsbss) + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb.*) + *(.scommon) + *(.sbss2) + *(.sbss2.*) + *(.gnu.linkonce.sb2.*) + *(.dynbss) + *(.bss) + *(.bss.*) + *(.share.mem) + *(.gnu.linkonce.b.*) + *(COMMON) + . = ALIGN (8); + _bss_end = ABSOLUTE(.); + _heap_start = ABSOLUTE(.); + } >dram0_0_seg + + .flash.rodata : + { + _rodata_start = ABSOLUTE(.); + *(.rodata) + *(.rodata.*) + *(.irom1.text) /* catch stray ICACHE_RODATA_ATTR */ + *(.gnu.linkonce.r.*) + *(.rodata1) + __XT_EXCEPTION_TABLE_ = ABSOLUTE(.); + *(.xt_except_table) + *(.gcc_except_table .gcc_except_table.*) + *(.gnu.linkonce.e.*) + *(.gnu.version_r) + . = (. + 3) & ~ 3; + __eh_frame = ABSOLUTE(.); + KEEP(*(.eh_frame)) + . = (. + 7) & ~ 3; + /* C++ constructor and destructor tables, properly ordered: */ + __init_array_start = ABSOLUTE(.); + KEEP (*crtbegin.o(.ctors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors)) + KEEP (*(SORT(.ctors.*))) + KEEP (*(.ctors)) + __init_array_end = ABSOLUTE(.); + KEEP (*crtbegin.o(.dtors)) + KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors)) + KEEP (*(SORT(.dtors.*))) + KEEP (*(.dtors)) + /* C++ exception handlers table: */ + __XT_EXCEPTION_DESCS_ = ABSOLUTE(.); + *(.xt_except_desc) + *(.gnu.linkonce.h.*) + __XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.); + *(.xt_except_desc_end) + *(.dynamic) + *(.gnu.version_d) + _rodata_end = ABSOLUTE(.); + /* Literals are also RO data. */ + _lit4_start = ABSOLUTE(.); + *(*.lit4) + *(.lit4.*) + *(.gnu.linkonce.lit4.*) + _lit4_end = ABSOLUTE(.); + . = ALIGN(4); + } >drom0_0_seg + + .flash.text : + { + _stext = .; + _text_start = ABSOLUTE(.); + *(.literal .text .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*) + *(.irom0.text) /* catch stray ICACHE_RODATA_ATTR */ + *(.fini.literal) + *(.fini) + *(.gnu.version) + _text_end = ABSOLUTE(.); + _etext = .; + + /* Similar to _iram_start, this symbol goes here so it is + resolved by addr2line in preference to the first symbol in + the flash.text segment. + */ + _flash_cache_start = ABSOLUTE(0); + } >iram0_2_seg +} diff --git a/ports/esp32/espneopixel.c b/ports/esp32/espneopixel.c new file mode 100644 index 0000000000..829c8b1c80 --- /dev/null +++ b/ports/esp32/espneopixel.c @@ -0,0 +1,53 @@ +// Original version from https://github.com/adafruit/Adafruit_NeoPixel +// Modifications by dpgeorge to support auto-CPU-frequency detection + +// This is a mash-up of the Due show() code + insights from Michael Miller's +// ESP8266 work for the NeoPixelBus library: github.com/Makuna/NeoPixelBus +// Needs to be a separate .c file to enforce ICACHE_RAM_ATTR execution. + +#include "py/mpconfig.h" +#include "py/mphal.h" +#include "modesp.h" + +void IRAM_ATTR esp_neopixel_write(uint8_t pin, uint8_t *pixels, uint32_t numBytes, uint8_t timing) { + uint8_t *p, *end, pix, mask; + uint32_t t, time0, time1, period, c, startTime, pinMask; + + pinMask = 1 << pin; + p = pixels; + end = p + numBytes; + pix = *p++; + mask = 0x80; + startTime = 0; + + uint32_t fcpu = ets_get_cpu_frequency() * 1000000; + + if (timing == 1) { + // 800 KHz + time0 = (fcpu * 0.35) / 1000000; // 0.35us + time1 = (fcpu * 0.8) / 1000000; // 0.8us + period = (fcpu * 1.25) / 1000000; // 1.25us per bit + } else { + // 400 KHz + time0 = (fcpu * 0.5) / 1000000; // 0.35us + time1 = (fcpu * 1.2) / 1000000; // 0.8us + period = (fcpu * 2.5) / 1000000; // 1.25us per bit + } + + uint32_t irq_state = mp_hal_quiet_timing_enter(); + for (t = time0;; t = time0) { + if (pix & mask) t = time1; // Bit high duration + while (((c = mp_hal_ticks_cpu()) - startTime) < period); // Wait for bit start + GPIO_REG_WRITE(GPIO_OUT_W1TS_REG, pinMask); // Set high + startTime = c; // Save start time + while (((c = mp_hal_ticks_cpu()) - startTime) < t); // Wait high duration + GPIO_REG_WRITE(GPIO_OUT_W1TC_REG, pinMask); // Set low + if (!(mask >>= 1)) { // Next bit/byte + if(p >= end) break; + pix = *p++; + mask = 0x80; + } + } + while ((mp_hal_ticks_cpu() - startTime) < period); // Wait for last bit + mp_hal_quiet_timing_exit(irq_state); +} diff --git a/ports/esp32/esponewire.c b/ports/esp32/esponewire.c new file mode 100644 index 0000000000..781616cbe4 --- /dev/null +++ b/ports/esp32/esponewire.c @@ -0,0 +1,80 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2015-2017 Damien P. George + * + * 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/mphal.h" +#include "esp8266/esponewire.h" + +#define TIMING_RESET1 (0) +#define TIMING_RESET2 (1) +#define TIMING_RESET3 (2) +#define TIMING_READ1 (3) +#define TIMING_READ2 (4) +#define TIMING_READ3 (5) +#define TIMING_WRITE1 (6) +#define TIMING_WRITE2 (7) +#define TIMING_WRITE3 (8) + +uint16_t esp_onewire_timings[9] = {480, 40, 420, 5, 5, 40, 10, 50, 10}; + +#define DELAY_US mp_hal_delay_us_fast + +int esp_onewire_reset(mp_hal_pin_obj_t pin) { + mp_hal_pin_write(pin, 0); + DELAY_US(esp_onewire_timings[TIMING_RESET1]); + uint32_t i = MICROPY_BEGIN_ATOMIC_SECTION(); + mp_hal_pin_write(pin, 1); + DELAY_US(esp_onewire_timings[TIMING_RESET2]); + int status = !mp_hal_pin_read(pin); + MICROPY_END_ATOMIC_SECTION(i); + DELAY_US(esp_onewire_timings[TIMING_RESET3]); + return status; +} + +int esp_onewire_readbit(mp_hal_pin_obj_t pin) { + mp_hal_pin_write(pin, 1); + uint32_t i = MICROPY_BEGIN_ATOMIC_SECTION(); + mp_hal_pin_write(pin, 0); + DELAY_US(esp_onewire_timings[TIMING_READ1]); + mp_hal_pin_write(pin, 1); + DELAY_US(esp_onewire_timings[TIMING_READ2]); + int value = mp_hal_pin_read(pin); + MICROPY_END_ATOMIC_SECTION(i); + DELAY_US(esp_onewire_timings[TIMING_READ3]); + return value; +} + +void esp_onewire_writebit(mp_hal_pin_obj_t pin, int value) { + uint32_t i = MICROPY_BEGIN_ATOMIC_SECTION(); + mp_hal_pin_write(pin, 0); + DELAY_US(esp_onewire_timings[TIMING_WRITE1]); + if (value) { + mp_hal_pin_write(pin, 1); + } + DELAY_US(esp_onewire_timings[TIMING_WRITE2]); + mp_hal_pin_write(pin, 1); + DELAY_US(esp_onewire_timings[TIMING_WRITE3]); + MICROPY_END_ATOMIC_SECTION(i); +} diff --git a/ports/esp32/fatfs_port.c b/ports/esp32/fatfs_port.c new file mode 100644 index 0000000000..b3690a01fb --- /dev/null +++ b/ports/esp32/fatfs_port.c @@ -0,0 +1,46 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * Development of the code in this file was sponsored by Microbric Pty Ltd + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014, 2016 Damien P. George + * + * 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 "lib/oofatfs/ff.h" +#include "timeutils.h" +//#include "modmachine.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; + + timeutils_struct_time_t tm; + timeutils_seconds_since_2000_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/ports/esp32/gccollect.c b/ports/esp32/gccollect.c new file mode 100644 index 0000000000..9843cef008 --- /dev/null +++ b/ports/esp32/gccollect.c @@ -0,0 +1,66 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * Development of the code in this file was sponsored by Microbric Pty Ltd + * + * The MIT License (MIT) + * + * Copyright (c) 2014 Damien P. George + * Copyright (c) 2017 Pycom Limited + * + * 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 + +#include "py/mpconfig.h" +#include "py/mpstate.h" +#include "py/gc.h" +#include "py/mpthread.h" +#include "gccollect.h" +#include "soc/cpu.h" +#include "xtensa/hal.h" + + +static void gc_collect_inner(int level) { + if (level < XCHAL_NUM_AREGS / 8) { + gc_collect_inner(level + 1); + if (level != 0) { + return; + } + } + + if (level == XCHAL_NUM_AREGS / 8) { + // get the sp + volatile uint32_t sp = (uint32_t)get_sp(); + gc_collect_root((void**)sp, ((mp_uint_t)MP_STATE_THREAD(stack_top) - sp) / sizeof(uint32_t)); + return; + } + + // trace root pointers from any threads + #if MICROPY_PY_THREAD + mp_thread_gc_others(); + #endif +} + +void gc_collect(void) { + gc_collect_start(); + gc_collect_inner(0); + gc_collect_end(); +} diff --git a/ports/esp32/gccollect.h b/ports/esp32/gccollect.h new file mode 100644 index 0000000000..fe02cc62be --- /dev/null +++ b/ports/esp32/gccollect.h @@ -0,0 +1,42 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * Development of the code in this file was sponsored by Microbric Pty Ltd + * + * The MIT License (MIT) + * + * Copyright (c) 2014 Damien P. George + * + * 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. + */ + +extern uint32_t _text_start; +extern uint32_t _text_end; +extern uint32_t _irom0_text_start; +extern uint32_t _irom0_text_end; +extern uint32_t _data_start; +extern uint32_t _data_end; +extern uint32_t _rodata_start; +extern uint32_t _rodata_end; +extern uint32_t _bss_start; +extern uint32_t _bss_end; +extern uint32_t _heap_start; +extern uint32_t _heap_end; + +void gc_collect(void); diff --git a/ports/esp32/help.c b/ports/esp32/help.c new file mode 100644 index 0000000000..95d115c563 --- /dev/null +++ b/ports/esp32/help.c @@ -0,0 +1,65 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * Development of the code in this file was sponsored by Microbric Pty Ltd + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2016 Damien P. George + * + * 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/builtin.h" + +const char esp32_help_text[] = +"Welcome to MicroPython on the ESP32!\n" +"\n" +"For generic online docs please visit http://docs.micropython.org/\n" +"\n" +"For access to the hardware use the 'machine' module:\n" +"\n" +"import machine\n" +"pin12 = machine.Pin(12, machine.Pin.OUT)\n" +"pin12.value(1)\n" +"pin13 = machine.Pin(13, machine.Pin.IN, machine.Pin.PULL_UP)\n" +"print(pin13.value())\n" +"i2c = machine.I2C(scl=machine.Pin(21), sda=machine.Pin(22))\n" +"i2c.scan()\n" +"i2c.writeto(addr, b'1234')\n" +"i2c.readfrom(addr, 4)\n" +"\n" +"Basic WiFi configuration:\n" +"\n" +"import network\n" +"sta_if = network.WLAN(network.STA_IF); sta_if.active(True)\n" +"sta_if.scan() # Scan for available access points\n" +"sta_if.connect(\"\", \"\") # Connect to an AP\n" +"sta_if.isconnected() # Check for successful connection\n" +"\n" +"Control commands:\n" +" CTRL-A -- on a blank line, enter raw REPL mode\n" +" CTRL-B -- on a blank line, enter normal REPL mode\n" +" CTRL-C -- interrupt a running program\n" +" CTRL-D -- on a blank line, do a soft reset of the board\n" +" CTRL-E -- on a blank line, enter paste mode\n" +"\n" +"For further help on a specific object, type help(obj)\n" +"For a list of available modules, type help('modules')\n" +; diff --git a/ports/esp32/machine_adc.c b/ports/esp32/machine_adc.c new file mode 100644 index 0000000000..d62f362e96 --- /dev/null +++ b/ports/esp32/machine_adc.c @@ -0,0 +1,132 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Nick Moore + * + * 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 + +#include "esp_log.h" + +#include "driver/gpio.h" +#include "driver/adc.h" + +#include "py/runtime.h" +#include "py/mphal.h" +#include "modmachine.h" + +typedef struct _madc_obj_t { + mp_obj_base_t base; + gpio_num_t gpio_id; + adc1_channel_t adc1_id; +} madc_obj_t; + +STATIC const madc_obj_t madc_obj[] = { + {{&machine_adc_type}, GPIO_NUM_36, ADC1_CHANNEL_0}, + {{&machine_adc_type}, GPIO_NUM_37, ADC1_CHANNEL_1}, + {{&machine_adc_type}, GPIO_NUM_38, ADC1_CHANNEL_2}, + {{&machine_adc_type}, GPIO_NUM_39, ADC1_CHANNEL_3}, + {{&machine_adc_type}, GPIO_NUM_32, ADC1_CHANNEL_4}, + {{&machine_adc_type}, GPIO_NUM_33, ADC1_CHANNEL_5}, + {{&machine_adc_type}, GPIO_NUM_34, ADC1_CHANNEL_6}, + {{&machine_adc_type}, GPIO_NUM_35, ADC1_CHANNEL_7}, +}; + +STATIC mp_obj_t madc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, + const mp_obj_t *args) { + + static int initialized = 0; + if (!initialized) { + adc1_config_width(ADC_WIDTH_12Bit); + initialized = 1; + } + + mp_arg_check_num(n_args, n_kw, 1, 1, true); + gpio_num_t pin_id = machine_pin_get_id(args[0]); + const madc_obj_t *self = NULL; + for (int i = 0; i < MP_ARRAY_SIZE(madc_obj); i++) { + if (pin_id == madc_obj[i].gpio_id) { self = &madc_obj[i]; break; } + } + if (!self) mp_raise_ValueError("invalid Pin for ADC"); + esp_err_t err = adc1_config_channel_atten(self->adc1_id, ADC_ATTEN_0db); + if (err == ESP_OK) return MP_OBJ_FROM_PTR(self); + mp_raise_ValueError("Parameter Error"); +} + +STATIC void madc_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + madc_obj_t *self = self_in; + mp_printf(print, "ADC(Pin(%u))", self->gpio_id); +} + +STATIC mp_obj_t madc_read(mp_obj_t self_in) { + madc_obj_t *self = self_in; + int val = adc1_get_raw(self->adc1_id); + if (val == -1) mp_raise_ValueError("Parameter Error"); + return MP_OBJ_NEW_SMALL_INT(val); +} +MP_DEFINE_CONST_FUN_OBJ_1(madc_read_obj, madc_read); + +STATIC mp_obj_t madc_atten(mp_obj_t self_in, mp_obj_t atten_in) { + madc_obj_t *self = self_in; + adc_atten_t atten = mp_obj_get_int(atten_in); + esp_err_t err = adc1_config_channel_atten(self->adc1_id, atten); + if (err == ESP_OK) return mp_const_none; + mp_raise_ValueError("Parameter Error"); +} +MP_DEFINE_CONST_FUN_OBJ_2(madc_atten_obj, madc_atten); + +STATIC mp_obj_t madc_width(mp_obj_t cls_in, mp_obj_t width_in) { + adc_bits_width_t width = mp_obj_get_int(width_in); + esp_err_t err = adc1_config_width(width); + if (err == ESP_OK) return mp_const_none; + mp_raise_ValueError("Parameter Error"); +} +MP_DEFINE_CONST_FUN_OBJ_2(madc_width_fun_obj, madc_width); +MP_DEFINE_CONST_CLASSMETHOD_OBJ(madc_width_obj, MP_ROM_PTR(&madc_width_fun_obj)); + +STATIC const mp_rom_map_elem_t madc_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&madc_read_obj) }, + { MP_ROM_QSTR(MP_QSTR_atten), MP_ROM_PTR(&madc_atten_obj) }, + { MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&madc_width_obj) }, + + { MP_ROM_QSTR(MP_QSTR_ATTN_0DB), MP_ROM_INT(ADC_ATTEN_0db) }, + { MP_ROM_QSTR(MP_QSTR_ATTN_2_5DB), MP_ROM_INT(ADC_ATTEN_2_5db) }, + { MP_ROM_QSTR(MP_QSTR_ATTN_6DB), MP_ROM_INT(ADC_ATTEN_6db) }, + { MP_ROM_QSTR(MP_QSTR_ATTN_11DB), MP_ROM_INT(ADC_ATTEN_11db) }, + + { MP_ROM_QSTR(MP_QSTR_WIDTH_9BIT), MP_ROM_INT(ADC_WIDTH_9Bit) }, + { MP_ROM_QSTR(MP_QSTR_WIDTH_10BIT), MP_ROM_INT(ADC_WIDTH_10Bit) }, + { MP_ROM_QSTR(MP_QSTR_WIDTH_11BIT), MP_ROM_INT(ADC_WIDTH_11Bit) }, + { MP_ROM_QSTR(MP_QSTR_WIDTH_12BIT), MP_ROM_INT(ADC_WIDTH_12Bit) }, +}; + +STATIC MP_DEFINE_CONST_DICT(madc_locals_dict, madc_locals_dict_table); + +const mp_obj_type_t machine_adc_type = { + { &mp_type_type }, + .name = MP_QSTR_ADC, + .print = madc_print, + .make_new = madc_make_new, + .locals_dict = (mp_obj_t)&madc_locals_dict, +}; diff --git a/ports/esp32/machine_dac.c b/ports/esp32/machine_dac.c new file mode 100644 index 0000000000..bd0804ec41 --- /dev/null +++ b/ports/esp32/machine_dac.c @@ -0,0 +1,97 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Nick Moore + * + * 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 + +#include "esp_log.h" + +#include "driver/gpio.h" +#include "driver/dac.h" + +#include "py/runtime.h" +#include "py/mphal.h" +#include "modmachine.h" + +typedef struct _mdac_obj_t { + mp_obj_base_t base; + gpio_num_t gpio_id; + dac_channel_t dac_id; +} mdac_obj_t; + +STATIC const mdac_obj_t mdac_obj[] = { + {{&machine_dac_type}, GPIO_NUM_25, DAC_CHANNEL_1}, + {{&machine_dac_type}, GPIO_NUM_26, DAC_CHANNEL_2}, +}; + +STATIC mp_obj_t mdac_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, + const mp_obj_t *args) { + + mp_arg_check_num(n_args, n_kw, 1, 1, true); + gpio_num_t pin_id = machine_pin_get_id(args[0]); + const mdac_obj_t *self = NULL; + for (int i = 0; i < MP_ARRAY_SIZE(mdac_obj); i++) { + if (pin_id == mdac_obj[i].gpio_id) { self = &mdac_obj[i]; break; } + } + if (!self) mp_raise_ValueError("invalid Pin for DAC"); + + esp_err_t err = dac_output_enable(self->dac_id); + if (err == ESP_OK) { + err = dac_output_voltage(self->dac_id, 0); + } + if (err == ESP_OK) return MP_OBJ_FROM_PTR(self); + mp_raise_ValueError("Parameter Error"); +} + +STATIC void mdac_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + mdac_obj_t *self = self_in; + mp_printf(print, "DAC(Pin(%u))", self->gpio_id); +} + +STATIC mp_obj_t mdac_write(mp_obj_t self_in, mp_obj_t value_in) { + mdac_obj_t *self = self_in; + int value = mp_obj_get_int(value_in); + if (value < 0 || value > 255) mp_raise_ValueError("Value out of range"); + + esp_err_t err = dac_output_voltage(self->dac_id, value); + if (err == ESP_OK) return mp_const_none; + mp_raise_ValueError("Parameter Error"); +} +MP_DEFINE_CONST_FUN_OBJ_2(mdac_write_obj, mdac_write); + +STATIC const mp_rom_map_elem_t mdac_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mdac_write_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(mdac_locals_dict, mdac_locals_dict_table); + +const mp_obj_type_t machine_dac_type = { + { &mp_type_type }, + .name = MP_QSTR_DAC, + .print = mdac_print, + .make_new = mdac_make_new, + .locals_dict = (mp_obj_t)&mdac_locals_dict, +}; diff --git a/ports/esp32/machine_hw_spi.c b/ports/esp32/machine_hw_spi.c new file mode 100644 index 0000000000..437b620f5e --- /dev/null +++ b/ports/esp32/machine_hw_spi.c @@ -0,0 +1,370 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 "Eric Poulsen" + * + * 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 +#include +#include + +#include "py/runtime.h" +#include "py/stream.h" +#include "py/mphal.h" +#include "extmod/machine_spi.h" +#include "modmachine.h" + +#include "driver/spi_master.h" + +typedef struct _machine_hw_spi_obj_t { + mp_obj_base_t base; + spi_host_device_t host; + uint32_t baudrate; + uint8_t polarity; + uint8_t phase; + uint8_t bits; + uint8_t firstbit; + int8_t sck; + int8_t mosi; + int8_t miso; + spi_device_handle_t spi; + enum { + MACHINE_HW_SPI_STATE_NONE, + MACHINE_HW_SPI_STATE_INIT, + MACHINE_HW_SPI_STATE_DEINIT + } state; +} machine_hw_spi_obj_t; + +STATIC void machine_hw_spi_deinit_internal(machine_hw_spi_obj_t *self) { + switch (spi_bus_remove_device(self->spi)) { + case ESP_ERR_INVALID_ARG: + mp_raise_msg(&mp_type_OSError, "invalid configuration"); + return; + + case ESP_ERR_INVALID_STATE: + mp_raise_msg(&mp_type_OSError, "SPI device already freed"); + return; + } + + switch (spi_bus_free(self->host)) { + case ESP_ERR_INVALID_ARG: + mp_raise_msg(&mp_type_OSError, "invalid configuration"); + return; + + case ESP_ERR_INVALID_STATE: + mp_raise_msg(&mp_type_OSError, "SPI bus already freed"); + return; + } + + int8_t pins[3] = {self->miso, self->mosi, self->sck}; + + for (int i = 0; i < 3; i++) { + if (pins[i] != -1) { + gpio_pad_select_gpio(pins[i]); + gpio_matrix_out(pins[i], SIG_GPIO_OUT_IDX, false, false); + gpio_set_direction(pins[i], GPIO_MODE_INPUT); + } + } +} + +STATIC void machine_hw_spi_init_internal( + machine_hw_spi_obj_t *self, + int8_t host, + int32_t baudrate, + int8_t polarity, + int8_t phase, + int8_t bits, + int8_t firstbit, + int8_t sck, + int8_t mosi, + int8_t miso) { + + // if we're not initialized, then we're + // implicitly 'changed', since this is the init routine + bool changed = self->state != MACHINE_HW_SPI_STATE_INIT; + + esp_err_t ret; + + machine_hw_spi_obj_t old_self = *self; + + if (host != -1 && host != self->host) { + self->host = host; + changed = true; + } + + if (baudrate != -1 && baudrate != self->baudrate) { + self->baudrate = baudrate; + changed = true; + } + + if (polarity != -1 && polarity != self->polarity) { + self->polarity = polarity; + changed = true; + } + + if (phase != -1 && phase != self->phase) { + self->phase = phase; + changed = true; + } + + if (bits != -1 && bits != self->bits) { + self->bits = bits; + changed = true; + } + + if (firstbit != -1 && firstbit != self->firstbit) { + self->firstbit = firstbit; + changed = true; + } + + if (sck != -2 && sck != self->sck) { + self->sck = sck; + changed = true; + } + + if (mosi != -2 && mosi != self->mosi) { + self->mosi = mosi; + changed = true; + } + + if (miso != -2 && miso != self->miso) { + self->miso = miso; + changed = true; + } + + if (self->host != HSPI_HOST && self->host != VSPI_HOST) { + mp_raise_ValueError("SPI ID must be either HSPI(1) or VSPI(2)"); + } + + if (changed) { + if (self->state == MACHINE_HW_SPI_STATE_INIT) { + self->state = MACHINE_HW_SPI_STATE_DEINIT; + machine_hw_spi_deinit_internal(&old_self); + } + } else { + return; // no changes + } + + spi_bus_config_t buscfg = { + .miso_io_num = self->miso, + .mosi_io_num = self->mosi, + .sclk_io_num = self->sck, + .quadwp_io_num = -1, + .quadhd_io_num = -1 + }; + + spi_device_interface_config_t devcfg = { + .clock_speed_hz = self->baudrate, + .mode = self->phase | (self->polarity << 1), + .spics_io_num = -1, // No CS pin + .queue_size = 1, + .flags = self->firstbit == MICROPY_PY_MACHINE_SPI_LSB ? SPI_DEVICE_TXBIT_LSBFIRST | SPI_DEVICE_RXBIT_LSBFIRST : 0, + .pre_cb = NULL + }; + + //Initialize the SPI bus + // FIXME: Does the DMA matter? There are two + + ret = spi_bus_initialize(self->host, &buscfg, 1); + switch (ret) { + case ESP_ERR_INVALID_ARG: + mp_raise_msg(&mp_type_OSError, "invalid configuration"); + return; + + case ESP_ERR_INVALID_STATE: + mp_raise_msg(&mp_type_OSError, "SPI device already in use"); + return; + } + + ret = spi_bus_add_device(self->host, &devcfg, &self->spi); + switch (ret) { + case ESP_ERR_INVALID_ARG: + mp_raise_msg(&mp_type_OSError, "invalid configuration"); + spi_bus_free(self->host); + return; + + case ESP_ERR_NO_MEM: + mp_raise_msg(&mp_type_OSError, "out of memory"); + spi_bus_free(self->host); + return; + + case ESP_ERR_NOT_FOUND: + mp_raise_msg(&mp_type_OSError, "no free slots"); + spi_bus_free(self->host); + return; + } + self->state = MACHINE_HW_SPI_STATE_INIT; +} + +STATIC void machine_hw_spi_deinit(mp_obj_base_t *self_in) { + machine_hw_spi_obj_t *self = (machine_hw_spi_obj_t *) self_in; + if (self->state == MACHINE_HW_SPI_STATE_INIT) { + self->state = MACHINE_HW_SPI_STATE_DEINIT; + machine_hw_spi_deinit_internal(self); + } +} + +STATIC void machine_hw_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint8_t *src, uint8_t *dest) { + machine_hw_spi_obj_t *self = MP_OBJ_TO_PTR(self_in); + + int bits_to_send = len * self->bits; + bool shortMsg = len <= 4; + + if (self->state == MACHINE_HW_SPI_STATE_DEINIT) { + mp_raise_msg(&mp_type_OSError, "transfer on deinitialized SPI"); + return; + } + + struct spi_transaction_t transaction = { + .flags = 0, + .length = bits_to_send, + .tx_buffer = NULL, + .rx_buffer = NULL, + }; + + if (shortMsg) { + if (src != NULL) { + memcpy(&transaction.tx_data, src, len); + } + transaction.flags |= (SPI_TRANS_USE_TXDATA | SPI_TRANS_USE_RXDATA); + } else { + transaction.tx_buffer = src; + transaction.rx_buffer = dest; + } + + spi_device_transmit(self->spi, &transaction); + + if (shortMsg && dest != NULL) { + memcpy(dest, &transaction.rx_data, len); + } +} + +/******************************************************************************/ +// MicroPython bindings for hw_spi + +STATIC void machine_hw_spi_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + machine_hw_spi_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, "SPI(id=%u, baudrate=%u, polarity=%u, phase=%u, bits=%u, firstbit=%u, sck=%d, mosi=%d, miso=%d)", + self->host, self->baudrate, self->polarity, + self->phase, self->bits, self->firstbit, + self->sck, self->mosi, self->miso); +} + +STATIC void machine_hw_spi_init(mp_obj_base_t *self_in, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + machine_hw_spi_obj_t *self = (machine_hw_spi_obj_t *) self_in; + + enum { ARG_id, ARG_baudrate, ARG_polarity, ARG_phase, ARG_bits, ARG_firstbit, ARG_sck, ARG_mosi, ARG_miso }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_id, MP_ARG_INT , {.u_int = -1} }, + { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_phase, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_firstbit, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_sck, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_mosi, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_miso, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + }; + + 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); + int8_t sck, mosi, miso; + + if (args[ARG_sck].u_obj == MP_OBJ_NULL) { + sck = -2; + } else if (args[ARG_sck].u_obj == mp_const_none) { + sck = -1; + } else { + sck = machine_pin_get_id(args[ARG_sck].u_obj); + } + + if (args[ARG_miso].u_obj == MP_OBJ_NULL) { + miso = -2; + } else if (args[ARG_miso].u_obj == mp_const_none) { + miso = -1; + } else { + miso = machine_pin_get_id(args[ARG_miso].u_obj); + } + + if (args[ARG_mosi].u_obj == MP_OBJ_NULL) { + mosi = -2; + } else if (args[ARG_mosi].u_obj == mp_const_none) { + mosi = -1; + } else { + mosi = machine_pin_get_id(args[ARG_mosi].u_obj); + } + + machine_hw_spi_init_internal(self, args[ARG_id].u_int, args[ARG_baudrate].u_int, + args[ARG_polarity].u_int, args[ARG_phase].u_int, args[ARG_bits].u_int, + args[ARG_firstbit].u_int, sck, mosi, miso); +} + +mp_obj_t machine_hw_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_id, ARG_baudrate, ARG_polarity, ARG_phase, ARG_bits, ARG_firstbit, ARG_sck, ARG_mosi, ARG_miso }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_INT , {.u_int = -1} }, + { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 500000} }, + { MP_QSTR_polarity, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_phase, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_bits, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 8} }, + { MP_QSTR_firstbit, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = MICROPY_PY_MACHINE_SPI_MSB} }, + { MP_QSTR_sck, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_mosi, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_miso, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + machine_hw_spi_obj_t *self = m_new_obj(machine_hw_spi_obj_t); + self->base.type = &machine_hw_spi_type; + + machine_hw_spi_init_internal( + self, + args[ARG_id].u_int, + args[ARG_baudrate].u_int, + args[ARG_polarity].u_int, + args[ARG_phase].u_int, + args[ARG_bits].u_int, + args[ARG_firstbit].u_int, + args[ARG_sck].u_obj == MP_OBJ_NULL ? -1 : machine_pin_get_id(args[ARG_sck].u_obj), + args[ARG_mosi].u_obj == MP_OBJ_NULL ? -1 : machine_pin_get_id(args[ARG_mosi].u_obj), + args[ARG_miso].u_obj == MP_OBJ_NULL ? -1 : machine_pin_get_id(args[ARG_miso].u_obj)); + + return MP_OBJ_FROM_PTR(self); +} + +STATIC const mp_machine_spi_p_t machine_hw_spi_p = { + .init = machine_hw_spi_init, + .deinit = machine_hw_spi_deinit, + .transfer = machine_hw_spi_transfer, +}; + +const mp_obj_type_t machine_hw_spi_type = { + { &mp_type_type }, + .name = MP_QSTR_SPI, + .print = machine_hw_spi_print, + .make_new = machine_hw_spi_make_new, + .protocol = &machine_hw_spi_p, + .locals_dict = (mp_obj_dict_t *) &mp_machine_spi_locals_dict, +}; diff --git a/ports/esp32/machine_pin.c b/ports/esp32/machine_pin.c new file mode 100644 index 0000000000..493dff3f50 --- /dev/null +++ b/ports/esp32/machine_pin.c @@ -0,0 +1,375 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * Development of the code in this file was sponsored by Microbric Pty Ltd + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Damien P. George + * + * 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 +#include + +#include "driver/gpio.h" + +#include "py/runtime.h" +#include "py/mphal.h" +#include "modmachine.h" +#include "extmod/virtpin.h" + +typedef struct _machine_pin_obj_t { + mp_obj_base_t base; + gpio_num_t id; +} machine_pin_obj_t; + +typedef struct _machine_pin_irq_obj_t { + mp_obj_base_t base; + gpio_num_t id; +} machine_pin_irq_obj_t; + +STATIC const machine_pin_obj_t machine_pin_obj[] = { + {{&machine_pin_type}, GPIO_NUM_0}, + {{&machine_pin_type}, GPIO_NUM_1}, + {{&machine_pin_type}, GPIO_NUM_2}, + {{&machine_pin_type}, GPIO_NUM_3}, + {{&machine_pin_type}, GPIO_NUM_4}, + {{&machine_pin_type}, GPIO_NUM_5}, + {{&machine_pin_type}, GPIO_NUM_6}, + {{&machine_pin_type}, GPIO_NUM_7}, + {{&machine_pin_type}, GPIO_NUM_8}, + {{&machine_pin_type}, GPIO_NUM_9}, + {{&machine_pin_type}, GPIO_NUM_10}, + {{&machine_pin_type}, GPIO_NUM_11}, + {{&machine_pin_type}, GPIO_NUM_12}, + {{&machine_pin_type}, GPIO_NUM_13}, + {{&machine_pin_type}, GPIO_NUM_14}, + {{&machine_pin_type}, GPIO_NUM_15}, + {{&machine_pin_type}, GPIO_NUM_16}, + {{&machine_pin_type}, GPIO_NUM_17}, + {{&machine_pin_type}, GPIO_NUM_18}, + {{&machine_pin_type}, GPIO_NUM_19}, + {{NULL}, -1}, + {{&machine_pin_type}, GPIO_NUM_21}, + {{&machine_pin_type}, GPIO_NUM_22}, + {{&machine_pin_type}, GPIO_NUM_23}, + {{NULL}, -1}, + {{&machine_pin_type}, GPIO_NUM_25}, + {{&machine_pin_type}, GPIO_NUM_26}, + {{&machine_pin_type}, GPIO_NUM_27}, + {{NULL}, -1}, + {{NULL}, -1}, + {{NULL}, -1}, + {{NULL}, -1}, + {{&machine_pin_type}, GPIO_NUM_32}, + {{&machine_pin_type}, GPIO_NUM_33}, + {{&machine_pin_type}, GPIO_NUM_34}, + {{&machine_pin_type}, GPIO_NUM_35}, + {{&machine_pin_type}, GPIO_NUM_36}, + {{&machine_pin_type}, GPIO_NUM_37}, + {{&machine_pin_type}, GPIO_NUM_38}, + {{&machine_pin_type}, GPIO_NUM_39}, +}; + +// forward declaration +STATIC const machine_pin_irq_obj_t machine_pin_irq_object[]; + +void machine_pins_init(void) { + static bool did_install = false; + if (!did_install) { + gpio_install_isr_service(0); + did_install = true; + } + memset(&MP_STATE_PORT(machine_pin_irq_handler[0]), 0, sizeof(MP_STATE_PORT(machine_pin_irq_handler))); +} + +void machine_pins_deinit(void) { + for (int i = 0; i < MP_ARRAY_SIZE(machine_pin_obj); ++i) { + if (machine_pin_obj[i].id != (gpio_num_t)-1) { + gpio_isr_handler_remove(machine_pin_obj[i].id); + } + } +} + +STATIC void IRAM_ATTR machine_pin_isr_handler(void *arg) { + machine_pin_obj_t *self = arg; + mp_obj_t handler = MP_STATE_PORT(machine_pin_irq_handler)[self->id]; + mp_sched_schedule(handler, MP_OBJ_FROM_PTR(self)); +} + +gpio_num_t machine_pin_get_id(mp_obj_t pin_in) { + if (mp_obj_get_type(pin_in) != &machine_pin_type) { + mp_raise_ValueError("expecting a pin"); + } + machine_pin_obj_t *self = pin_in; + return self->id; +} + +STATIC void machine_pin_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + machine_pin_obj_t *self = self_in; + mp_printf(print, "Pin(%u)", self->id); +} + +// pin.init(mode, pull=None, *, value) +STATIC mp_obj_t machine_pin_obj_init_helper(const machine_pin_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_mode, ARG_pull, ARG_value }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_mode, MP_ARG_OBJ, {.u_obj = mp_const_none}}, + { MP_QSTR_pull, MP_ARG_OBJ, {.u_obj = mp_const_none}}, + { MP_QSTR_value, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, + }; + + // parse args + 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); + + // configure the pin for gpio + gpio_pad_select_gpio(self->id); + + // set initial value (do this before configuring mode/pull) + if (args[ARG_value].u_obj != MP_OBJ_NULL) { + gpio_set_level(self->id, mp_obj_is_true(args[ARG_value].u_obj)); + } + + // configure mode + if (args[ARG_mode].u_obj != mp_const_none) { + mp_int_t pin_io_mode = mp_obj_get_int(args[ARG_mode].u_obj); + if (self->id >= 34 && (pin_io_mode & GPIO_MODE_DEF_OUTPUT)) { + mp_raise_ValueError("pin can only be input"); + } else { + gpio_set_direction(self->id, pin_io_mode); + } + } + + // configure pull + if (args[ARG_pull].u_obj != mp_const_none) { + gpio_set_pull_mode(self->id, mp_obj_get_int(args[ARG_pull].u_obj)); + } + + return mp_const_none; +} + +// constructor(id, ...) +mp_obj_t mp_pin_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); + + // get the wanted pin object + int wanted_pin = mp_obj_get_int(args[0]); + const machine_pin_obj_t *self = NULL; + if (0 <= wanted_pin && wanted_pin < MP_ARRAY_SIZE(machine_pin_obj)) { + self = (machine_pin_obj_t*)&machine_pin_obj[wanted_pin]; + } + if (self == NULL || self->base.type == NULL) { + mp_raise_ValueError("invalid pin"); + } + + if (n_args > 1 || n_kw > 0) { + // pin mode given, so configure this GPIO + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + machine_pin_obj_init_helper(self, n_args - 1, args + 1, &kw_args); + } + + return MP_OBJ_FROM_PTR(self); +} + +// fast method for getting/setting pin value +STATIC mp_obj_t machine_pin_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 0, 1, false); + machine_pin_obj_t *self = self_in; + if (n_args == 0) { + // get pin + return MP_OBJ_NEW_SMALL_INT(gpio_get_level(self->id)); + } else { + // set pin + gpio_set_level(self->id, mp_obj_is_true(args[0])); + return mp_const_none; + } +} + +// pin.init(mode, pull) +STATIC mp_obj_t machine_pin_obj_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + return machine_pin_obj_init_helper(args[0], n_args - 1, args + 1, kw_args); +} +MP_DEFINE_CONST_FUN_OBJ_KW(machine_pin_init_obj, 1, machine_pin_obj_init); + +// pin.value([value]) +STATIC mp_obj_t machine_pin_value(size_t n_args, const mp_obj_t *args) { + return machine_pin_call(args[0], n_args - 1, 0, args + 1); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pin_value_obj, 1, 2, machine_pin_value); + +// pin.irq(handler=None, trigger=IRQ_FALLING|IRQ_RISING) +STATIC mp_obj_t machine_pin_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_handler, ARG_trigger, ARG_hard }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_handler, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_trigger, MP_ARG_INT, {.u_int = GPIO_PIN_INTR_POSEDGE | GPIO_PIN_INTR_NEGEDGE} }, + }; + machine_pin_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); + + if (n_args > 1 || kw_args->used != 0) { + // configure irq + mp_obj_t handler = args[ARG_handler].u_obj; + uint32_t trigger = args[ARG_trigger].u_int; + if (handler == mp_const_none) { + handler = MP_OBJ_NULL; + trigger = 0; + } + gpio_isr_handler_remove(self->id); + MP_STATE_PORT(machine_pin_irq_handler)[self->id] = handler; + gpio_set_intr_type(self->id, trigger); + gpio_isr_handler_add(self->id, machine_pin_isr_handler, (void*)self); + } + + // return the irq object + return MP_OBJ_FROM_PTR(&machine_pin_irq_object[self->id]); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_pin_irq_obj, 1, machine_pin_irq); + +STATIC const mp_rom_map_elem_t machine_pin_locals_dict_table[] = { + // instance methods + { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_pin_init_obj) }, + { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&machine_pin_value_obj) }, + { MP_ROM_QSTR(MP_QSTR_irq), MP_ROM_PTR(&machine_pin_irq_obj) }, + + // class constants + { MP_ROM_QSTR(MP_QSTR_IN), MP_ROM_INT(GPIO_MODE_INPUT) }, + { MP_ROM_QSTR(MP_QSTR_OUT), MP_ROM_INT(GPIO_MODE_INPUT_OUTPUT) }, + { MP_ROM_QSTR(MP_QSTR_OPEN_DRAIN), MP_ROM_INT(GPIO_MODE_INPUT_OUTPUT_OD) }, + { MP_ROM_QSTR(MP_QSTR_PULL_UP), MP_ROM_INT(GPIO_PULLUP_ONLY) }, + { MP_ROM_QSTR(MP_QSTR_PULL_DOWN), MP_ROM_INT(GPIO_PULLDOWN_ONLY) }, + { MP_ROM_QSTR(MP_QSTR_IRQ_RISING), MP_ROM_INT(GPIO_PIN_INTR_POSEDGE) }, + { MP_ROM_QSTR(MP_QSTR_IRQ_FALLING), MP_ROM_INT(GPIO_PIN_INTR_NEGEDGE) }, +}; + +STATIC mp_uint_t pin_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) { + (void)errcode; + machine_pin_obj_t *self = self_in; + + switch (request) { + case MP_PIN_READ: { + return gpio_get_level(self->id); + } + case MP_PIN_WRITE: { + gpio_set_level(self->id, arg); + return 0; + } + } + return -1; +} + +STATIC MP_DEFINE_CONST_DICT(machine_pin_locals_dict, machine_pin_locals_dict_table); + +STATIC const mp_pin_p_t pin_pin_p = { + .ioctl = pin_ioctl, +}; + +const mp_obj_type_t machine_pin_type = { + { &mp_type_type }, + .name = MP_QSTR_Pin, + .print = machine_pin_print, + .make_new = mp_pin_make_new, + .call = machine_pin_call, + .protocol = &pin_pin_p, + .locals_dict = (mp_obj_t)&machine_pin_locals_dict, +}; + +/******************************************************************************/ +// Pin IRQ object + +STATIC const mp_obj_type_t machine_pin_irq_type; + +STATIC const machine_pin_irq_obj_t machine_pin_irq_object[] = { + {{&machine_pin_irq_type}, GPIO_NUM_0}, + {{&machine_pin_irq_type}, GPIO_NUM_1}, + {{&machine_pin_irq_type}, GPIO_NUM_2}, + {{&machine_pin_irq_type}, GPIO_NUM_3}, + {{&machine_pin_irq_type}, GPIO_NUM_4}, + {{&machine_pin_irq_type}, GPIO_NUM_5}, + {{&machine_pin_irq_type}, GPIO_NUM_6}, + {{&machine_pin_irq_type}, GPIO_NUM_7}, + {{&machine_pin_irq_type}, GPIO_NUM_8}, + {{&machine_pin_irq_type}, GPIO_NUM_9}, + {{&machine_pin_irq_type}, GPIO_NUM_10}, + {{&machine_pin_irq_type}, GPIO_NUM_11}, + {{&machine_pin_irq_type}, GPIO_NUM_12}, + {{&machine_pin_irq_type}, GPIO_NUM_13}, + {{&machine_pin_irq_type}, GPIO_NUM_14}, + {{&machine_pin_irq_type}, GPIO_NUM_15}, + {{&machine_pin_irq_type}, GPIO_NUM_16}, + {{&machine_pin_irq_type}, GPIO_NUM_17}, + {{&machine_pin_irq_type}, GPIO_NUM_18}, + {{&machine_pin_irq_type}, GPIO_NUM_19}, + {{NULL}, -1}, + {{&machine_pin_irq_type}, GPIO_NUM_21}, + {{&machine_pin_irq_type}, GPIO_NUM_22}, + {{&machine_pin_irq_type}, GPIO_NUM_23}, + {{NULL}, -1}, + {{&machine_pin_irq_type}, GPIO_NUM_25}, + {{&machine_pin_irq_type}, GPIO_NUM_26}, + {{&machine_pin_irq_type}, GPIO_NUM_27}, + {{NULL}, -1}, + {{NULL}, -1}, + {{NULL}, -1}, + {{NULL}, -1}, + {{&machine_pin_irq_type}, GPIO_NUM_32}, + {{&machine_pin_irq_type}, GPIO_NUM_33}, + {{&machine_pin_irq_type}, GPIO_NUM_34}, + {{&machine_pin_irq_type}, GPIO_NUM_35}, + {{&machine_pin_irq_type}, GPIO_NUM_36}, + {{&machine_pin_irq_type}, GPIO_NUM_37}, + {{&machine_pin_irq_type}, GPIO_NUM_38}, + {{&machine_pin_irq_type}, GPIO_NUM_39}, +}; + +STATIC mp_obj_t machine_pin_irq_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + machine_pin_irq_obj_t *self = self_in; + mp_arg_check_num(n_args, n_kw, 0, 0, false); + machine_pin_isr_handler((void*)&machine_pin_obj[self->id]); + return mp_const_none; +} + +STATIC mp_obj_t machine_pin_irq_trigger(size_t n_args, const mp_obj_t *args) { + machine_pin_irq_obj_t *self = args[0]; + uint32_t orig_trig = GPIO.pin[self->id].int_type; + if (n_args == 2) { + // set trigger + gpio_set_intr_type(self->id, mp_obj_get_int(args[1])); + } + // return original trigger value + return MP_OBJ_NEW_SMALL_INT(orig_trig); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_pin_irq_trigger_obj, 1, 2, machine_pin_irq_trigger); + +STATIC const mp_rom_map_elem_t machine_pin_irq_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_trigger), MP_ROM_PTR(&machine_pin_irq_trigger_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(machine_pin_irq_locals_dict, machine_pin_irq_locals_dict_table); + +STATIC const mp_obj_type_t machine_pin_irq_type = { + { &mp_type_type }, + .name = MP_QSTR_IRQ, + .call = machine_pin_irq_call, + .locals_dict = (mp_obj_dict_t*)&machine_pin_irq_locals_dict, +}; diff --git a/ports/esp32/machine_pwm.c b/ports/esp32/machine_pwm.c new file mode 100644 index 0000000000..489833e7c2 --- /dev/null +++ b/ports/esp32/machine_pwm.c @@ -0,0 +1,277 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Damien P. George + * + * 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 +#include "driver/ledc.h" +#include "esp_err.h" + +#include "py/nlr.h" +#include "py/runtime.h" +#include "modmachine.h" +#include "mphalport.h" + +// Forward dec'l +extern const mp_obj_type_t machine_pwm_type; + +typedef struct _esp32_pwm_obj_t { + mp_obj_base_t base; + gpio_num_t pin; + uint8_t active; + uint8_t channel; +} esp32_pwm_obj_t; + +// Which channel has which GPIO pin assigned? +// (-1 if not assigned) +STATIC int chan_gpio[LEDC_CHANNEL_MAX]; + +// Params for PW operation +// 5khz +#define PWFREQ (5000) +// High speed mode +#define PWMODE (LEDC_HIGH_SPEED_MODE) +// 10-bit resolution (compatible with esp8266 PWM) +#define PWRES (LEDC_TIMER_10_BIT) +// Timer 1 +#define PWTIMER (LEDC_TIMER_1) + +// Config of timer upon which we run all PWM'ed GPIO pins +STATIC bool pwm_inited = false; +STATIC ledc_timer_config_t timer_cfg = { + .bit_num = PWRES, + .freq_hz = PWFREQ, + .speed_mode = PWMODE, + .timer_num = PWTIMER +}; + +STATIC void pwm_init(void) { + + // Initial condition: no channels assigned + for (int x = 0; x < LEDC_CHANNEL_MAX; ++x) { + chan_gpio[x] = -1; + } + + // Init with default timer params + ledc_timer_config(&timer_cfg); +} + +STATIC int set_freq(int newval) { + int oval = timer_cfg.freq_hz; + + timer_cfg.freq_hz = newval; + if (ledc_timer_config(&timer_cfg) != ESP_OK) { + timer_cfg.freq_hz = oval; + return 0; + } + return 1; +} + +/******************************************************************************/ + +// MicroPython bindings for PWM + +STATIC void esp32_pwm_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + esp32_pwm_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_printf(print, "PWM(%u", self->pin); + if (self->active) { + mp_printf(print, ", freq=%u, duty=%u", timer_cfg.freq_hz, + ledc_get_duty(PWMODE, self->channel)); + } + mp_printf(print, ")"); +} + +STATIC void esp32_pwm_init_helper(esp32_pwm_obj_t *self, + size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_freq, ARG_duty }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_freq, MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_duty, MP_ARG_INT, {.u_int = -1} }, + }; + 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); + + int channel; + int avail = -1; + + // Find a free PWM channel, also spot if our pin is + // already mentioned. + for (channel = 0; channel < LEDC_CHANNEL_MAX; ++channel) { + if (chan_gpio[channel] == self->pin) { + break; + } + if ((avail == -1) && (chan_gpio[channel] == -1)) { + avail = channel; + } + } + if (channel >= LEDC_CHANNEL_MAX) { + if (avail == -1) { + mp_raise_ValueError("out of PWM channels"); + } + channel = avail; + } + + // New PWM assignment + self->active = 1; + if (chan_gpio[channel] == -1) { + ledc_channel_config_t cfg = { + .channel = channel, + .duty = (1 << PWRES) / 2, + .gpio_num = self->pin, + .intr_type = LEDC_INTR_DISABLE, + .speed_mode = PWMODE, + .timer_sel = PWTIMER, + }; + if (ledc_channel_config(&cfg) != ESP_OK) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "PWM not supported on pin %d", self->pin)); + } + chan_gpio[channel] = self->pin; + self->channel = channel; + } + + // Maybe change PWM timer + int tval = args[ARG_freq].u_int; + if (tval != -1) { + if (tval != timer_cfg.freq_hz) { + if (!set_freq(tval)) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "Bad frequency %d", tval)); + } + } + } + + // Set duty cycle? + int dval = args[ARG_duty].u_int; + if (dval != -1) { + dval &= ((1 << PWRES)-1); + ledc_set_duty(PWMODE, channel, dval); + ledc_update_duty(PWMODE, channel); + } +} + +STATIC mp_obj_t esp32_pwm_make_new(const mp_obj_type_t *type, + size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); + gpio_num_t pin_id = machine_pin_get_id(args[0]); + + // create PWM object from the given pin + esp32_pwm_obj_t *self = m_new_obj(esp32_pwm_obj_t); + self->base.type = &machine_pwm_type; + self->pin = pin_id; + self->active = 0; + self->channel = -1; + + // start the PWM subsystem if it's not already running + if (!pwm_inited) { + pwm_init(); + pwm_inited = true; + } + + // start the PWM running for this channel + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + esp32_pwm_init_helper(self, n_args - 1, args + 1, &kw_args); + + return MP_OBJ_FROM_PTR(self); +} + +STATIC mp_obj_t esp32_pwm_init(size_t n_args, + const mp_obj_t *args, mp_map_t *kw_args) { + esp32_pwm_init_helper(args[0], n_args - 1, args + 1, kw_args); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(esp32_pwm_init_obj, 1, esp32_pwm_init); + +STATIC mp_obj_t esp32_pwm_deinit(mp_obj_t self_in) { + esp32_pwm_obj_t *self = MP_OBJ_TO_PTR(self_in); + int chan = self->channel; + + // Valid channel? + if ((chan >= 0) && (chan < LEDC_CHANNEL_MAX)) { + // Mark it unused, and tell the hardware to stop routing + chan_gpio[chan] = -1; + ledc_stop(PWMODE, chan, 0); + self->active = 0; + self->channel = -1; + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp32_pwm_deinit_obj, esp32_pwm_deinit); + +STATIC mp_obj_t esp32_pwm_freq(size_t n_args, const mp_obj_t *args) { + if (n_args == 1) { + // get + return MP_OBJ_NEW_SMALL_INT(timer_cfg.freq_hz); + } + + // set + int tval = mp_obj_get_int(args[1]); + if (!set_freq(tval)) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, + "Bad frequency %d", tval)); + } + return mp_const_none; +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp32_pwm_freq_obj, 1, 2, esp32_pwm_freq); + +STATIC mp_obj_t esp32_pwm_duty(size_t n_args, const mp_obj_t *args) { + esp32_pwm_obj_t *self = MP_OBJ_TO_PTR(args[0]); + int duty; + + if (n_args == 1) { + // get + duty = ledc_get_duty(PWMODE, self->channel); + return MP_OBJ_NEW_SMALL_INT(duty); + } + + // set + duty = mp_obj_get_int(args[1]); + duty &= ((1 << PWRES)-1); + ledc_set_duty(PWMODE, self->channel, duty); + ledc_update_duty(PWMODE, self->channel); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp32_pwm_duty_obj, + 1, 2, esp32_pwm_duty); + +STATIC const mp_rom_map_elem_t esp32_pwm_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&esp32_pwm_init_obj) }, + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&esp32_pwm_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_freq), MP_ROM_PTR(&esp32_pwm_freq_obj) }, + { MP_ROM_QSTR(MP_QSTR_duty), MP_ROM_PTR(&esp32_pwm_duty_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(esp32_pwm_locals_dict, + esp32_pwm_locals_dict_table); + +const mp_obj_type_t machine_pwm_type = { + { &mp_type_type }, + .name = MP_QSTR_PWM, + .print = esp32_pwm_print, + .make_new = esp32_pwm_make_new, + .locals_dict = (mp_obj_dict_t*)&esp32_pwm_locals_dict, +}; diff --git a/ports/esp32/machine_timer.c b/ports/esp32/machine_timer.c new file mode 100644 index 0000000000..d75efb8fc1 --- /dev/null +++ b/ports/esp32/machine_timer.c @@ -0,0 +1,192 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * Development of the code in this file was sponsored by Microbric Pty Ltd + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2015 Damien P. George + * Copyright (c) 2016 Paul Sokolovsky + * + * 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 +#include + +#include "driver/timer.h" +#include "py/obj.h" +#include "py/runtime.h" +#include "modmachine.h" + +#define TIMER_INTR_SEL TIMER_INTR_LEVEL +#define TIMER_DIVIDER 40000 +#define TIMER_SCALE (TIMER_BASE_CLK / TIMER_DIVIDER) + +#define TIMER_FLAGS 0 + +typedef struct _machine_timer_obj_t { + mp_obj_base_t base; + mp_uint_t group; + mp_uint_t index; + + mp_uint_t repeat; + mp_uint_t period; + + mp_obj_t callback; + + intr_handle_t handle; +} machine_timer_obj_t; + +const mp_obj_type_t machine_timer_type; + +STATIC esp_err_t check_esp_err(esp_err_t code) { + if (code) { + mp_raise_OSError(code); + } + + return code; +} + +STATIC void machine_timer_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + machine_timer_obj_t *self = self_in; + + timer_config_t config; + mp_printf(print, "Timer(%p; ", self); + + timer_get_config(self->group, self->index, &config); + + mp_printf(print, "alarm_en=%d, ", config.alarm_en); + mp_printf(print, "auto_reload=%d, ", config.auto_reload); + mp_printf(print, "counter_en=%d)", config.counter_en); +} + +STATIC mp_obj_t machine_timer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 1, 1, false); + machine_timer_obj_t *self = m_new_obj(machine_timer_obj_t); + self->base.type = &machine_timer_type; + + self->group = (mp_obj_get_int(args[0]) >> 1) & 1; + self->index = mp_obj_get_int(args[0]) & 1; + + return self; +} + +STATIC void machine_timer_disable(machine_timer_obj_t *self) { + if (self->handle) { + timer_pause(self->group, self->index); + esp_intr_free(self->handle); + self->handle = NULL; + } +} + +STATIC void machine_timer_isr(void *self_in) { + machine_timer_obj_t *self = self_in; + timg_dev_t *device = self->group ? &(TIMERG1) : &(TIMERG0); + + device->hw_timer[self->index].update = 1; + if (self->index) { + device->int_clr_timers.t1 = 1; + } else { + device->int_clr_timers.t0 = 1; + } + device->hw_timer[self->index].config.alarm_en = self->repeat; + + mp_sched_schedule(self->callback, self); +} + +STATIC void machine_timer_enable(machine_timer_obj_t *self) { + timer_config_t config; + config.alarm_en = TIMER_ALARM_EN; + config.auto_reload = self->repeat; + config.counter_dir = TIMER_COUNT_UP; + config.divider = TIMER_DIVIDER; + config.intr_type = TIMER_INTR_LEVEL; + config.counter_en = TIMER_PAUSE; + + check_esp_err(timer_init(self->group, self->index, &config)); + check_esp_err(timer_set_counter_value(self->group, self->index, 0x00000000)); + check_esp_err(timer_set_alarm_value(self->group, self->index, self->period)); + check_esp_err(timer_enable_intr(self->group, self->index)); + check_esp_err(timer_isr_register(self->group, self->index, machine_timer_isr, (void*)self, TIMER_FLAGS, &self->handle)); + check_esp_err(timer_start(self->group, self->index)); +} + +STATIC mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + static const mp_arg_t allowed_args[] = { + { MP_QSTR_period, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} }, + { MP_QSTR_mode, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} }, + { MP_QSTR_callback, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + }; + + machine_timer_disable(self); + + 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); + + // Timer uses an 80MHz base clock, which is divided by the divider/scalar, we then convert to ms. + self->period = (args[0].u_int * TIMER_BASE_CLK) / (1000 * TIMER_DIVIDER); + self->repeat = args[1].u_int; + self->callback = args[2].u_obj; + self->handle = NULL; + + machine_timer_enable(self); + + return mp_const_none; +} + +STATIC mp_obj_t machine_timer_deinit(mp_obj_t self_in) { + machine_timer_disable(self_in); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_timer_deinit_obj, machine_timer_deinit); + +STATIC mp_obj_t machine_timer_init(mp_uint_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + return machine_timer_init_helper(args[0], n_args - 1, args + 1, kw_args); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(machine_timer_init_obj, 1, machine_timer_init); + +STATIC mp_obj_t machine_timer_value(mp_obj_t self_in) { + machine_timer_obj_t *self = self_in; + double result; + + timer_get_counter_time_sec(self->group, self->index, &result); + + return MP_OBJ_NEW_SMALL_INT((mp_uint_t)(result * 1000)); // value in ms +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_timer_value_obj, machine_timer_value); + +STATIC const mp_map_elem_t machine_timer_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR___del__), (mp_obj_t)&machine_timer_deinit_obj }, + { MP_ROM_QSTR(MP_QSTR_deinit), (mp_obj_t)&machine_timer_deinit_obj }, + { MP_ROM_QSTR(MP_QSTR_init), (mp_obj_t)&machine_timer_init_obj }, + { MP_ROM_QSTR(MP_QSTR_value), (mp_obj_t)&machine_timer_value_obj }, + { MP_ROM_QSTR(MP_QSTR_ONE_SHOT), MP_ROM_INT(false) }, + { MP_ROM_QSTR(MP_QSTR_PERIODIC), MP_ROM_INT(true) }, +}; +STATIC MP_DEFINE_CONST_DICT(machine_timer_locals_dict, machine_timer_locals_dict_table); + +const mp_obj_type_t machine_timer_type = { + { &mp_type_type }, + .name = MP_QSTR_Timer, + .print = machine_timer_print, + .make_new = machine_timer_make_new, + .locals_dict = (mp_obj_t)&machine_timer_locals_dict, +}; diff --git a/ports/esp32/machine_touchpad.c b/ports/esp32/machine_touchpad.c new file mode 100644 index 0000000000..96de1a2a1d --- /dev/null +++ b/ports/esp32/machine_touchpad.c @@ -0,0 +1,110 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2017 Nick Moore + * + * 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 + +#include "esp_log.h" + +#include "driver/gpio.h" +#include "driver/touch_pad.h" + +#include "py/runtime.h" +#include "py/mphal.h" +#include "modmachine.h" + +typedef struct _mtp_obj_t { + mp_obj_base_t base; + gpio_num_t gpio_id; + touch_pad_t touchpad_id; +} mtp_obj_t; + +STATIC const mtp_obj_t touchpad_obj[] = { + {{&machine_touchpad_type}, GPIO_NUM_4, TOUCH_PAD_NUM0}, + {{&machine_touchpad_type}, GPIO_NUM_0, TOUCH_PAD_NUM1}, + {{&machine_touchpad_type}, GPIO_NUM_2, TOUCH_PAD_NUM2}, + {{&machine_touchpad_type}, GPIO_NUM_15, TOUCH_PAD_NUM3}, + {{&machine_touchpad_type}, GPIO_NUM_13, TOUCH_PAD_NUM4}, + {{&machine_touchpad_type}, GPIO_NUM_12, TOUCH_PAD_NUM5}, + {{&machine_touchpad_type}, GPIO_NUM_14, TOUCH_PAD_NUM6}, + {{&machine_touchpad_type}, GPIO_NUM_27, TOUCH_PAD_NUM7}, + {{&machine_touchpad_type}, GPIO_NUM_33, TOUCH_PAD_NUM8}, + {{&machine_touchpad_type}, GPIO_NUM_32, TOUCH_PAD_NUM9}, +}; + +STATIC mp_obj_t mtp_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, + const mp_obj_t *args) { + + mp_arg_check_num(n_args, n_kw, 1, 1, true); + gpio_num_t pin_id = machine_pin_get_id(args[0]); + const mtp_obj_t *self = NULL; + for (int i = 0; i < MP_ARRAY_SIZE(touchpad_obj); i++) { + if (pin_id == touchpad_obj[i].gpio_id) { self = &touchpad_obj[i]; break; } + } + if (!self) mp_raise_ValueError("invalid pin for touchpad"); + + static int initialized = 0; + if (!initialized) { + touch_pad_init(); + initialized = 1; + } + esp_err_t err = touch_pad_config(self->touchpad_id, 0); + if (err == ESP_OK) return MP_OBJ_FROM_PTR(self); + mp_raise_ValueError("Touch pad error"); +} + +STATIC mp_obj_t mtp_config(mp_obj_t self_in, mp_obj_t value_in) { + mtp_obj_t *self = self_in; + uint16_t value = mp_obj_get_int(value_in); + esp_err_t err = touch_pad_config(self->touchpad_id, value); + if (err == ESP_OK) return mp_const_none; + mp_raise_ValueError("Touch pad error"); +} +MP_DEFINE_CONST_FUN_OBJ_2(mtp_config_obj, mtp_config); + +STATIC mp_obj_t mtp_read(mp_obj_t self_in) { + mtp_obj_t *self = self_in; + uint16_t value; + esp_err_t err = touch_pad_read(self->touchpad_id, &value); + if (err == ESP_OK) return MP_OBJ_NEW_SMALL_INT(value); + mp_raise_ValueError("Touch pad error"); +} +MP_DEFINE_CONST_FUN_OBJ_1(mtp_read_obj, mtp_read); + +STATIC const mp_rom_map_elem_t mtp_locals_dict_table[] = { + // instance methods + { MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&mtp_config_obj) }, + { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mtp_read_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(mtp_locals_dict, mtp_locals_dict_table); + +const mp_obj_type_t machine_touchpad_type = { + { &mp_type_type }, + .name = MP_QSTR_TouchPad, + .make_new = mtp_make_new, + .locals_dict = (mp_obj_t)&mtp_locals_dict, +}; diff --git a/ports/esp32/machine_uart.c b/ports/esp32/machine_uart.c new file mode 100644 index 0000000000..0b303d424d --- /dev/null +++ b/ports/esp32/machine_uart.c @@ -0,0 +1,359 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Damien P. George + * + * 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 +#include +#include + +#include "driver/uart.h" +#include "freertos/FreeRTOS.h" + +#include "py/runtime.h" +#include "py/stream.h" +#include "py/mperrno.h" +#include "modmachine.h" + +typedef struct _machine_uart_obj_t { + mp_obj_base_t base; + uart_port_t uart_num; + uint8_t bits; + uint8_t parity; + uint8_t stop; + int8_t tx; + int8_t rx; + int8_t rts; + int8_t cts; + uint16_t timeout; // timeout waiting for first char (in ms) + uint16_t timeout_char; // timeout waiting between chars (in ms) +} machine_uart_obj_t; + +STATIC const char *_parity_name[] = {"None", "1", "0"}; + +QueueHandle_t UART_QUEUE[UART_NUM_MAX] = {}; + +/******************************************************************************/ +// MicroPython bindings for UART + +STATIC void machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + uint32_t baudrate; + uart_get_baudrate(self->uart_num, &baudrate); + mp_printf(print, "UART(%u, baudrate=%u, bits=%u, parity=%s, stop=%u, tx=%d, rx=%d, rts=%d, cts=%d, timeout=%u, timeout_char=%u)", + self->uart_num, baudrate, self->bits, _parity_name[self->parity], + self->stop, self->tx, self->rx, self->rts, self->cts, self->timeout, self->timeout_char); +} + +STATIC void machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_baudrate, ARG_bits, ARG_parity, ARG_stop, ARG_tx, ARG_rx, ARG_rts, ARG_cts, ARG_timeout, ARG_timeout_char }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_bits, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_parity, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_stop, MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_tx, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = UART_PIN_NO_CHANGE} }, + { MP_QSTR_rx, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = UART_PIN_NO_CHANGE} }, + { MP_QSTR_rts, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = UART_PIN_NO_CHANGE} }, + { MP_QSTR_cts, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = UART_PIN_NO_CHANGE} }, + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_timeout_char, MP_ARG_KW_ONLY | 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); + + // wait for all data to be transmitted before changing settings + uart_wait_tx_done(self->uart_num, pdMS_TO_TICKS(1000)); + + // set baudrate + uint32_t baudrate = 115200; + if (args[ARG_baudrate].u_int > 0) { + uart_set_baudrate(self->uart_num, args[ARG_baudrate].u_int); + uart_get_baudrate(self->uart_num, &baudrate); + } + + uart_set_pin(self->uart_num, args[ARG_tx].u_int, args[ARG_rx].u_int, args[ARG_rts].u_int, args[ARG_cts].u_int); + if (args[ARG_tx].u_int != UART_PIN_NO_CHANGE) { + self->tx = args[ARG_tx].u_int; + } + + if (args[ARG_rx].u_int != UART_PIN_NO_CHANGE) { + self->rx = args[ARG_rx].u_int; + } + + if (args[ARG_rts].u_int != UART_PIN_NO_CHANGE) { + self->rts = args[ARG_rts].u_int; + } + + if (args[ARG_cts].u_int != UART_PIN_NO_CHANGE) { + self->cts = args[ARG_cts].u_int; + } + + // set data bits + switch (args[ARG_bits].u_int) { + case 0: + break; + case 5: + uart_set_word_length(self->uart_num, UART_DATA_5_BITS); + self->bits = 5; + break; + case 6: + uart_set_word_length(self->uart_num, UART_DATA_6_BITS); + self->bits = 6; + break; + case 7: + uart_set_word_length(self->uart_num, UART_DATA_7_BITS); + self->bits = 7; + break; + case 8: + uart_set_word_length(self->uart_num, UART_DATA_8_BITS); + self->bits = 8; + break; + default: + mp_raise_ValueError("invalid data bits"); + break; + } + + // set parity + if (args[ARG_parity].u_obj != MP_OBJ_NULL) { + if (args[ARG_parity].u_obj == mp_const_none) { + uart_set_parity(self->uart_num, UART_PARITY_DISABLE); + self->parity = 0; + } else { + mp_int_t parity = mp_obj_get_int(args[ARG_parity].u_obj); + if (parity & 1) { + uart_set_parity(self->uart_num, UART_PARITY_ODD); + self->parity = 1; + } else { + uart_set_parity(self->uart_num, UART_PARITY_EVEN); + self->parity = 2; + } + } + } + + // set stop bits + switch (args[ARG_stop].u_int) { + // FIXME: ESP32 also supports 1.5 stop bits + case 0: + break; + case 1: + uart_set_stop_bits(self->uart_num, UART_STOP_BITS_1); + self->stop = 1; + break; + case 2: + uart_set_stop_bits(self->uart_num, UART_STOP_BITS_2); + self->stop = 2; + break; + default: + mp_raise_ValueError("invalid stop bits"); + break; + } + + // set timeout + self->timeout = args[ARG_timeout].u_int; + + // set timeout_char + // make sure it is at least as long as a whole character (13 bits to be safe) + self->timeout_char = args[ARG_timeout_char].u_int; + uint32_t min_timeout_char = 13000 / baudrate + 1; + if (self->timeout_char < min_timeout_char) { + self->timeout_char = min_timeout_char; + } +} + +STATIC mp_obj_t machine_uart_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); + + // get uart id + mp_int_t uart_num = mp_obj_get_int(args[0]); + if (uart_num < 0 || uart_num > UART_NUM_MAX) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "UART(%d) does not exist", uart_num)); + } + + // Attempts to use UART0 from Python has resulted in all sorts of fun errors. + // FIXME: UART0 is disabled for now. + if (uart_num == UART_NUM_0) { + nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "UART(%d) is disabled (dedicated to REPL)", uart_num)); + } + + // Defaults + uart_config_t uartcfg = { + .baud_rate = 115200, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, + .rx_flow_ctrl_thresh = 0 + }; + + // create instance + machine_uart_obj_t *self = m_new_obj(machine_uart_obj_t); + self->base.type = &machine_uart_type; + self->uart_num = uart_num; + self->bits = 8; + self->parity = 0; + self->stop = 1; + self->rts = UART_PIN_NO_CHANGE; + self->cts = UART_PIN_NO_CHANGE; + self->timeout = 0; + self->timeout_char = 0; + + switch (uart_num) { + case UART_NUM_0: + self->rx = UART_PIN_NO_CHANGE; // GPIO 3 + self->tx = UART_PIN_NO_CHANGE; // GPIO 1 + break; + case UART_NUM_1: + self->rx = 9; + self->tx = 10; + break; + case UART_NUM_2: + self->rx = 16; + self->tx = 17; + break; + } + + // Remove any existing configuration + uart_driver_delete(self->uart_num); + + // init the peripheral + // Setup + uart_param_config(self->uart_num, &uartcfg); + + // RX and TX buffers are currently hardcoded at 256 bytes each (IDF minimum). + uart_driver_install(uart_num, 256, 256, 10, &UART_QUEUE[self->uart_num], 0); + + mp_map_t kw_args; + mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); + machine_uart_init_helper(self, n_args - 1, args + 1, &kw_args); + + // Make sure pins are connected. + uart_set_pin(self->uart_num, self->tx, self->rx, self->rts, self->cts); + + return MP_OBJ_FROM_PTR(self); +} + +STATIC mp_obj_t machine_uart_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + machine_uart_init_helper(args[0], n_args - 1, args + 1, kw_args); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(machine_uart_init_obj, 1, machine_uart_init); + +STATIC mp_obj_t machine_uart_any(mp_obj_t self_in) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + size_t rxbufsize; + uart_get_buffered_data_len(self->uart_num, &rxbufsize); + return MP_OBJ_NEW_SMALL_INT(rxbufsize); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(machine_uart_any_obj, machine_uart_any); + +STATIC const mp_rom_map_elem_t machine_uart_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&machine_uart_init_obj) }, + + { MP_ROM_QSTR(MP_QSTR_any), MP_ROM_PTR(&machine_uart_any_obj) }, + { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, + { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) }, + { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, + { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(machine_uart_locals_dict, machine_uart_locals_dict_table); + +STATIC mp_uint_t machine_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + + // make sure we want at least 1 char + if (size == 0) { + return 0; + } + + TickType_t time_to_wait; + if (self->timeout == 0) { + time_to_wait = 0; + } else { + time_to_wait = pdMS_TO_TICKS(self->timeout); + } + + int bytes_read = uart_read_bytes(self->uart_num, buf_in, size, time_to_wait); + + if (bytes_read < 0) { + *errcode = MP_EAGAIN; + return MP_STREAM_ERROR; + } + + return bytes_read; +} + +STATIC mp_uint_t machine_uart_write(mp_obj_t self_in, const void *buf_in, mp_uint_t size, int *errcode) { + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + + int bytes_written = uart_write_bytes(self->uart_num, buf_in, size); + + if (bytes_written < 0) { + *errcode = MP_EAGAIN; + return MP_STREAM_ERROR; + } + + // return number of bytes written + return bytes_written; +} + +STATIC mp_uint_t machine_uart_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) { + machine_uart_obj_t *self = self_in; + mp_uint_t ret; + if (request == MP_STREAM_POLL) { + mp_uint_t flags = arg; + ret = 0; + size_t rxbufsize; + uart_get_buffered_data_len(self->uart_num, &rxbufsize); + if ((flags & MP_STREAM_POLL_RD) && rxbufsize > 0) { + ret |= MP_STREAM_POLL_RD; + } + if ((flags & MP_STREAM_POLL_WR) && 1) { // FIXME: uart_tx_any_room(self->uart_num) + ret |= MP_STREAM_POLL_WR; + } + } else { + *errcode = MP_EINVAL; + ret = MP_STREAM_ERROR; + } + return ret; +} + +STATIC const mp_stream_p_t uart_stream_p = { + .read = machine_uart_read, + .write = machine_uart_write, + .ioctl = machine_uart_ioctl, + .is_text = false, +}; + +const mp_obj_type_t machine_uart_type = { + { &mp_type_type }, + .name = MP_QSTR_UART, + .print = machine_uart_print, + .make_new = machine_uart_make_new, + .getiter = mp_identity_getiter, + .iternext = mp_stream_unbuffered_iter, + .protocol = &uart_stream_p, + .locals_dict = (mp_obj_dict_t*)&machine_uart_locals_dict, +}; diff --git a/ports/esp32/main.c b/ports/esp32/main.c new file mode 100644 index 0000000000..091cbddc98 --- /dev/null +++ b/ports/esp32/main.c @@ -0,0 +1,131 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * Development of the code in this file was sponsored by Microbric Pty Ltd + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Damien P. George + * + * 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 +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_system.h" +#include "nvs_flash.h" +#include "esp_task.h" +#include "soc/cpu.h" + +#include "py/stackctrl.h" +#include "py/nlr.h" +#include "py/compile.h" +#include "py/runtime.h" +#include "py/repl.h" +#include "py/gc.h" +#include "py/mphal.h" +#include "lib/mp-readline/readline.h" +#include "lib/utils/pyexec.h" +#include "uart.h" +#include "modmachine.h" +#include "mpthreadport.h" + +// MicroPython runs as a task under FreeRTOS +#define MP_TASK_PRIORITY (ESP_TASK_PRIO_MIN + 1) +#define MP_TASK_STACK_SIZE (16 * 1024) +#define MP_TASK_STACK_LEN (MP_TASK_STACK_SIZE / sizeof(StackType_t)) +#define MP_TASK_HEAP_SIZE (96 * 1024) + +STATIC StaticTask_t mp_task_tcb; +STATIC StackType_t mp_task_stack[MP_TASK_STACK_LEN] __attribute__((aligned (8))); +STATIC uint8_t mp_task_heap[MP_TASK_HEAP_SIZE]; + +void mp_task(void *pvParameter) { + volatile uint32_t sp = (uint32_t)get_sp(); + #if MICROPY_PY_THREAD + mp_thread_init(&mp_task_stack[0], MP_TASK_STACK_LEN); + #endif + uart_init(); + +soft_reset: + // initialise the stack pointer for the main thread + mp_stack_set_top((void *)sp); + mp_stack_set_limit(MP_TASK_STACK_SIZE - 1024); + gc_init(mp_task_heap, mp_task_heap + sizeof(mp_task_heap)); + mp_init(); + mp_obj_list_init(mp_sys_path, 0); + mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR_)); + mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR__slash_lib)); + mp_obj_list_init(mp_sys_argv, 0); + readline_init0(); + + // initialise peripherals + machine_pins_init(); + + // run boot-up scripts + pyexec_frozen_module("_boot.py"); + pyexec_file("boot.py"); + if (pyexec_mode_kind == PYEXEC_MODE_FRIENDLY_REPL) { + pyexec_file("main.py"); + } + + for (;;) { + if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) { + if (pyexec_raw_repl() != 0) { + break; + } + } else { + if (pyexec_friendly_repl() != 0) { + break; + } + } + } + + #if MICROPY_PY_THREAD + mp_thread_deinit(); + #endif + + mp_hal_stdout_tx_str("PYB: soft reboot\r\n"); + + // deinitialise peripherals + machine_pins_deinit(); + + mp_deinit(); + fflush(stdout); + goto soft_reset; +} + +void app_main(void) { + nvs_flash_init(); + xTaskCreateStaticPinnedToCore(mp_task, "mp_task", MP_TASK_STACK_LEN, NULL, MP_TASK_PRIORITY, + &mp_task_stack[0], &mp_task_tcb, 0); +} + +void nlr_jump_fail(void *val) { + printf("NLR jump failed, val=%p\n", val); + esp_restart(); +} + +// modussl_mbedtls uses this function but it's not enabled in ESP IDF +void mbedtls_debug_set_threshold(int threshold) { + (void)threshold; +} diff --git a/ports/esp32/makeimg.py b/ports/esp32/makeimg.py new file mode 100644 index 0000000000..aeedbff7ef --- /dev/null +++ b/ports/esp32/makeimg.py @@ -0,0 +1,25 @@ +import sys + +OFFSET_BOOTLOADER = 0x1000 +OFFSET_PARTITIONS = 0x8000 +OFFSET_APPLICATION = 0x10000 + +files_in = [ + ('bootloader', OFFSET_BOOTLOADER, sys.argv[1]), + ('partitions', OFFSET_PARTITIONS, sys.argv[2]), + ('application', OFFSET_APPLICATION, sys.argv[3]), +] +file_out = sys.argv[4] + +cur_offset = OFFSET_BOOTLOADER +with open(file_out, 'wb') as fout: + for name, offset, file_in in files_in: + assert offset >= cur_offset + fout.write(b'\xff' * (offset - cur_offset)) + cur_offset = offset + with open(file_in, 'rb') as fin: + data = fin.read() + fout.write(data) + cur_offset += len(data) + print('%-12s% 8d' % (name, len(data))) + print('%-12s% 8d' % ('total', cur_offset)) diff --git a/ports/esp32/memory.h b/ports/esp32/memory.h new file mode 100644 index 0000000000..f3777b0e39 --- /dev/null +++ b/ports/esp32/memory.h @@ -0,0 +1,2 @@ +// this is needed for extmod/crypto-algorithms/sha256.c +#include diff --git a/ports/esp32/modesp.c b/ports/esp32/modesp.c new file mode 100644 index 0000000000..caa0551646 --- /dev/null +++ b/ports/esp32/modesp.c @@ -0,0 +1,129 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * Development of the code in this file was sponsored by Microbric Pty Ltd + * + * The MIT License (MIT) + * + * Copyright (c) 2015 Paul Sokolovsky + * Copyright (c) 2016 Damien P. George + * + * 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 + +#include "rom/gpio.h" +#include "esp_spi_flash.h" + +#include "py/runtime.h" +#include "py/mperrno.h" +#include "py/mphal.h" +#include "drivers/dht/dht.h" +#include "modesp.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; + mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_WRITE); + esp_err_t res = spi_flash_read(offset, bufinfo.buf, bufinfo.len); + if (res != ESP_OK) { + mp_raise_OSError(MP_EIO); + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(esp_flash_read_obj, esp_flash_read); + +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); + if (res != ESP_OK) { + mp_raise_OSError(MP_EIO); + } + return mp_const_none; +} +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); + if (res != ESP_OK) { + mp_raise_OSError(MP_EIO); + } + return mp_const_none; +} +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()); +} +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_DEFINE_CONST_FUN_OBJ_0(esp_flash_user_start_obj, esp_flash_user_start); + +STATIC mp_obj_t esp_gpio_matrix_in(mp_obj_t pin, mp_obj_t sig, mp_obj_t inv) { + gpio_matrix_in(mp_obj_get_int(pin), mp_obj_get_int(sig), mp_obj_get_int(inv)); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(esp_gpio_matrix_in_obj, esp_gpio_matrix_in); + +STATIC mp_obj_t esp_gpio_matrix_out(size_t n_args, const mp_obj_t *args) { + (void)n_args; + gpio_matrix_out(mp_obj_get_int(args[0]), mp_obj_get_int(args[1]), mp_obj_get_int(args[2]), mp_obj_get_int(args[3])); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp_gpio_matrix_out_obj, 4, 4, esp_gpio_matrix_out); + +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); + esp_neopixel_write(mp_hal_get_pin_obj(pin), + (uint8_t*)bufinfo.buf, bufinfo.len, mp_obj_get_int(timing)); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(esp_neopixel_write_obj, esp_neopixel_write_); + +STATIC const mp_rom_map_elem_t esp_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_esp) }, + + { MP_ROM_QSTR(MP_QSTR_flash_read), MP_ROM_PTR(&esp_flash_read_obj) }, + { MP_ROM_QSTR(MP_QSTR_flash_write), MP_ROM_PTR(&esp_flash_write_obj) }, + { 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_gpio_matrix_in), MP_ROM_PTR(&esp_gpio_matrix_in_obj) }, + { MP_ROM_QSTR(MP_QSTR_gpio_matrix_out), MP_ROM_PTR(&esp_gpio_matrix_out_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) }, +}; + +STATIC MP_DEFINE_CONST_DICT(esp_module_globals, esp_module_globals_table); + +const mp_obj_module_t esp_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&esp_module_globals, +}; + diff --git a/ports/esp32/modesp.h b/ports/esp32/modesp.h new file mode 100644 index 0000000000..a822c02ecc --- /dev/null +++ b/ports/esp32/modesp.h @@ -0,0 +1 @@ +void esp_neopixel_write(uint8_t pin, uint8_t *pixels, uint32_t numBytes, uint8_t timing); diff --git a/ports/esp32/modmachine.c b/ports/esp32/modmachine.c new file mode 100644 index 0000000000..51038f3141 --- /dev/null +++ b/ports/esp32/modmachine.c @@ -0,0 +1,135 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * Development of the code in this file was sponsored by Microbric Pty Ltd + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2015 Damien P. George + * Copyright (c) 2016 Paul Sokolovsky + * + * 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 +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "rom/ets_sys.h" +#include "esp_system.h" + +#include "py/obj.h" +#include "py/runtime.h" +#include "extmod/machine_mem.h" +#include "extmod/machine_signal.h" +#include "extmod/machine_pulse.h" +#include "extmod/machine_i2c.h" +#include "extmod/machine_spi.h" +#include "modmachine.h" + +#if MICROPY_PY_MACHINE + +STATIC mp_obj_t machine_freq(size_t n_args, const mp_obj_t *args) { + if (n_args == 0) { + // get + return mp_obj_new_int(ets_get_cpu_frequency() * 1000000); + } else { + // set + mp_int_t freq = mp_obj_get_int(args[0]) / 1000000; + if (freq != 80 && freq != 160 && freq != 240) { + mp_raise_ValueError("frequency can only be either 80Mhz, 160MHz or 240MHz"); + } + /* + system_update_cpu_freq(freq); + */ + return mp_const_none; + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(machine_freq_obj, 0, 1, machine_freq); + +STATIC mp_obj_t machine_reset(void) { + esp_restart(); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(machine_reset_obj, machine_reset); + +STATIC mp_obj_t machine_unique_id(void) { + uint8_t chipid[6]; + esp_efuse_mac_get_default(chipid); + return mp_obj_new_bytes(chipid, 6); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(machine_unique_id_obj, machine_unique_id); + +STATIC mp_obj_t machine_idle(void) { + taskYIELD(); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(machine_idle_obj, machine_idle); + +STATIC mp_obj_t machine_disable_irq(void) { + uint32_t state = MICROPY_BEGIN_ATOMIC_SECTION(); + return mp_obj_new_int(state); +} +MP_DEFINE_CONST_FUN_OBJ_0(machine_disable_irq_obj, machine_disable_irq); + +STATIC mp_obj_t machine_enable_irq(mp_obj_t state_in) { + uint32_t state = mp_obj_get_int(state_in); + MICROPY_END_ATOMIC_SECTION(state); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(machine_enable_irq_obj, machine_enable_irq); + +STATIC const mp_rom_map_elem_t machine_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_umachine) }, + + { MP_ROM_QSTR(MP_QSTR_mem8), MP_ROM_PTR(&machine_mem8_obj) }, + { MP_ROM_QSTR(MP_QSTR_mem16), MP_ROM_PTR(&machine_mem16_obj) }, + { MP_ROM_QSTR(MP_QSTR_mem32), MP_ROM_PTR(&machine_mem32_obj) }, + + { MP_ROM_QSTR(MP_QSTR_freq), MP_ROM_PTR(&machine_freq_obj) }, + { 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_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) }, + + { MP_ROM_QSTR(MP_QSTR_time_pulse_us), MP_ROM_PTR(&machine_time_pulse_us_obj) }, + + { MP_ROM_QSTR(MP_QSTR_Timer), MP_ROM_PTR(&machine_timer_type) }, + { MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&machine_pin_type) }, + { MP_ROM_QSTR(MP_QSTR_Signal), MP_ROM_PTR(&machine_signal_type) }, + { MP_ROM_QSTR(MP_QSTR_TouchPad), MP_ROM_PTR(&machine_touchpad_type) }, + { MP_ROM_QSTR(MP_QSTR_ADC), MP_ROM_PTR(&machine_adc_type) }, + { MP_ROM_QSTR(MP_QSTR_DAC), MP_ROM_PTR(&machine_dac_type) }, + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&machine_i2c_type) }, + { 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) }, +}; + +STATIC MP_DEFINE_CONST_DICT(machine_module_globals, machine_module_globals_table); + +const mp_obj_module_t mp_module_machine = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&machine_module_globals, +}; + +#endif // MICROPY_PY_MACHINE diff --git a/ports/esp32/modmachine.h b/ports/esp32/modmachine.h new file mode 100644 index 0000000000..4909235b46 --- /dev/null +++ b/ports/esp32/modmachine.h @@ -0,0 +1,18 @@ +#ifndef MICROPY_INCLUDED_ESP32_MODMACHINE_H +#define MICROPY_INCLUDED_ESP32_MODMACHINE_H + +#include "py/obj.h" + +extern const mp_obj_type_t machine_timer_type; +extern const mp_obj_type_t machine_pin_type; +extern const mp_obj_type_t machine_touchpad_type; +extern const mp_obj_type_t machine_adc_type; +extern const mp_obj_type_t machine_dac_type; +extern const mp_obj_type_t machine_pwm_type; +extern const mp_obj_type_t machine_hw_spi_type; +extern const mp_obj_type_t machine_uart_type; + +void machine_pins_init(void); +void machine_pins_deinit(void); + +#endif // MICROPY_INCLUDED_ESP32_MODMACHINE_H diff --git a/ports/esp32/modnetwork.c b/ports/esp32/modnetwork.c new file mode 100644 index 0000000000..73a4718617 --- /dev/null +++ b/ports/esp32/modnetwork.c @@ -0,0 +1,561 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * Development of the code in this file was sponsored by Microbric Pty Ltd + * and Mnemote Pty Ltd + * + * The MIT License (MIT) + * + * Copyright (c) 2016, 2017 Nick Moore @mnemote + * + * Based on esp8266/modnetwork.c which is Copyright (c) 2015 Paul Sokolovsky + * And the ESP IDF example code which is Public Domain / CC0 + * + * 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 +#include +#include + +#include "py/nlr.h" +#include "py/objlist.h" +#include "py/runtime.h" +#include "py/mphal.h" +#include "py/mperrno.h" +#include "netutils.h" +#include "esp_wifi.h" +#include "esp_wifi_types.h" +#include "esp_log.h" +#include "esp_event_loop.h" +#include "esp_log.h" +#include "lwip/dns.h" +#include "tcpip_adapter.h" + +#define MODNETWORK_INCLUDE_CONSTANTS (1) + +NORETURN void _esp_exceptions(esp_err_t e) { + switch (e) { + case ESP_ERR_WIFI_NOT_INIT: + mp_raise_msg(&mp_type_OSError, "Wifi Not Initialized"); + case ESP_ERR_WIFI_NOT_STARTED: + mp_raise_msg(&mp_type_OSError, "Wifi Not Started"); + case ESP_ERR_WIFI_CONN: + mp_raise_msg(&mp_type_OSError, "Wifi Internal Error"); + case ESP_ERR_WIFI_SSID: + mp_raise_msg(&mp_type_OSError, "Wifi SSID Invalid"); + case ESP_ERR_WIFI_FAIL: + mp_raise_msg(&mp_type_OSError, "Wifi Internal Failure"); + case ESP_ERR_WIFI_IF: + mp_raise_msg(&mp_type_OSError, "Wifi Invalid Interface"); + case ESP_ERR_WIFI_MAC: + mp_raise_msg(&mp_type_OSError, "Wifi Invalid MAC Address"); + case ESP_ERR_WIFI_ARG: + mp_raise_msg(&mp_type_OSError, "Wifi Invalid Argument"); + case ESP_ERR_WIFI_MODE: + mp_raise_msg(&mp_type_OSError, "Wifi Invalid Mode"); + case ESP_ERR_WIFI_PASSWORD: + mp_raise_msg(&mp_type_OSError, "Wifi Invalid Password"); + case ESP_ERR_WIFI_NVS: + mp_raise_msg(&mp_type_OSError, "Wifi Internal NVS Error"); + case ESP_ERR_TCPIP_ADAPTER_INVALID_PARAMS: + mp_raise_msg(&mp_type_OSError, "TCP/IP Invalid Parameters"); + case ESP_ERR_TCPIP_ADAPTER_IF_NOT_READY: + mp_raise_msg(&mp_type_OSError, "TCP/IP IF Not Ready"); + case ESP_ERR_TCPIP_ADAPTER_DHCPC_START_FAILED: + mp_raise_msg(&mp_type_OSError, "TCP/IP DHCP Client Start Failed"); + case ESP_ERR_WIFI_TIMEOUT: + mp_raise_OSError(MP_ETIMEDOUT); + case ESP_ERR_TCPIP_ADAPTER_NO_MEM: + case ESP_ERR_WIFI_NO_MEM: + mp_raise_OSError(MP_ENOMEM); + default: + nlr_raise(mp_obj_new_exception_msg_varg( + &mp_type_RuntimeError, "Wifi Unknown Error 0x%04x", e + )); + } +} + +static inline void esp_exceptions(esp_err_t e) { + if (e != ESP_OK) _esp_exceptions(e); +} + +#define ESP_EXCEPTIONS(x) do { esp_exceptions(x); } while (0); + +typedef struct _wlan_if_obj_t { + mp_obj_base_t base; + int if_id; +} wlan_if_obj_t; + +const mp_obj_type_t wlan_if_type; +STATIC const wlan_if_obj_t wlan_sta_obj = {{&wlan_if_type}, WIFI_IF_STA}; +STATIC const wlan_if_obj_t wlan_ap_obj = {{&wlan_if_type}, WIFI_IF_AP}; + +//static wifi_config_t wifi_ap_config = {{{0}}}; +static wifi_config_t wifi_sta_config = {{{0}}}; + +// Set to "true" if the STA interface is requested to be connected by the +// user, used for automatic reassociation. +static bool wifi_sta_connected = false; + +// This function is called by the system-event task and so runs in a different +// thread to the main MicroPython task. It must not raise any Python exceptions. +static esp_err_t event_handler(void *ctx, system_event_t *event) { + switch(event->event_id) { + case SYSTEM_EVENT_STA_START: + ESP_LOGI("wifi", "STA_START"); + break; + case SYSTEM_EVENT_STA_GOT_IP: + ESP_LOGI("wifi", "GOT_IP"); + break; + case SYSTEM_EVENT_STA_DISCONNECTED: { + // This is a workaround as ESP32 WiFi libs don't currently + // auto-reassociate. + system_event_sta_disconnected_t *disconn = &event->event_info.disconnected; + ESP_LOGI("wifi", "STA_DISCONNECTED, reason:%d", disconn->reason); + switch (disconn->reason) { + case WIFI_REASON_AUTH_FAIL: + mp_printf(MP_PYTHON_PRINTER, "authentication failed"); + wifi_sta_connected = false; + break; + default: + // Let other errors through and try to reconnect. + break; + } + if (wifi_sta_connected) { + wifi_mode_t mode; + if (esp_wifi_get_mode(&mode) == ESP_OK) { + if (mode & WIFI_MODE_STA) { + // STA is active so attempt to reconnect. + esp_err_t e = esp_wifi_connect(); + if (e != ESP_OK) { + mp_printf(MP_PYTHON_PRINTER, "error attempting to reconnect: 0x%04x", e); + } + } + } + } + break; + } + default: + ESP_LOGI("wifi", "event %d", event->event_id); + break; + } + return ESP_OK; +} + +/*void error_check(bool status, const char *msg) { + if (!status) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, msg)); + } +} +*/ + +STATIC void require_if(mp_obj_t wlan_if, int if_no) { + wlan_if_obj_t *self = MP_OBJ_TO_PTR(wlan_if); + if (self->if_id != if_no) { + mp_raise_msg(&mp_type_OSError, if_no == WIFI_IF_STA ? "STA required" : "AP required"); + } +} + +STATIC mp_obj_t get_wlan(size_t n_args, const mp_obj_t *args) { + int idx = (n_args > 0) ? mp_obj_get_int(args[0]) : WIFI_IF_STA; + if (idx == WIFI_IF_STA) { + return MP_OBJ_FROM_PTR(&wlan_sta_obj); + } else if (idx == WIFI_IF_AP) { + return MP_OBJ_FROM_PTR(&wlan_ap_obj); + } else { + mp_raise_ValueError("invalid WLAN interface identifier"); + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(get_wlan_obj, 0, 1, get_wlan); + +STATIC mp_obj_t esp_initialize() { + static int initialized = 0; + if (!initialized) { + ESP_LOGD("modnetwork", "Initializing TCP/IP"); + tcpip_adapter_init(); + ESP_LOGD("modnetwork", "Initializing Event Loop"); + ESP_EXCEPTIONS( esp_event_loop_init(event_handler, NULL) ); + ESP_LOGD("modnetwork", "esp_event_loop_init done"); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_LOGD("modnetwork", "Initializing WiFi"); + ESP_EXCEPTIONS( esp_wifi_init(&cfg) ); + ESP_EXCEPTIONS( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); + ESP_LOGD("modnetwork", "Initialized"); + ESP_EXCEPTIONS( esp_wifi_set_mode(0) ); + ESP_EXCEPTIONS( esp_wifi_start() ); + ESP_LOGD("modnetwork", "Started"); + initialized = 1; + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(esp_initialize_obj, esp_initialize); + +#if (WIFI_MODE_STA & WIFI_MODE_AP != WIFI_MODE_NULL || WIFI_MODE_STA | WIFI_MODE_AP != WIFI_MODE_APSTA) +#error WIFI_MODE_STA and WIFI_MODE_AP are supposed to be bitfields! +#endif + +STATIC mp_obj_t esp_active(size_t n_args, const mp_obj_t *args) { + + wlan_if_obj_t *self = MP_OBJ_TO_PTR(args[0]); + wifi_mode_t mode; + ESP_EXCEPTIONS( esp_wifi_get_mode(&mode) ); + int bit = (self->if_id == WIFI_IF_STA) ? WIFI_MODE_STA : WIFI_MODE_AP; + + if (n_args > 1) { + bool active = mp_obj_is_true(args[1]); + mode = active ? (mode | bit) : (mode & ~bit); + ESP_EXCEPTIONS( esp_wifi_set_mode(mode) ); + } + + return (mode & bit) ? mp_const_true : mp_const_false; +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp_active_obj, 1, 2, esp_active); + +STATIC mp_obj_t esp_connect(size_t n_args, const mp_obj_t *args) { + + mp_uint_t len; + const char *p; + if (n_args > 1) { + memset(&wifi_sta_config, 0, sizeof(wifi_sta_config)); + p = mp_obj_str_get_data(args[1], &len); + memcpy(wifi_sta_config.sta.ssid, p, MIN(len, sizeof(wifi_sta_config.sta.ssid))); + p = (n_args > 2) ? mp_obj_str_get_data(args[2], &len) : ""; + memcpy(wifi_sta_config.sta.password, p, MIN(len, sizeof(wifi_sta_config.sta.password))); + ESP_EXCEPTIONS( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_sta_config) ); + } + MP_THREAD_GIL_EXIT(); + ESP_EXCEPTIONS( esp_wifi_connect() ); + MP_THREAD_GIL_ENTER(); + wifi_sta_connected = true; + + return mp_const_none; +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp_connect_obj, 1, 7, esp_connect); + +STATIC mp_obj_t esp_disconnect(mp_obj_t self_in) { + wifi_sta_connected = false; + ESP_EXCEPTIONS( esp_wifi_disconnect() ); + return mp_const_none; +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp_disconnect_obj, esp_disconnect); + +STATIC mp_obj_t esp_status(mp_obj_t self_in) { + return mp_const_none; +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp_status_obj, esp_status); + +STATIC mp_obj_t esp_scan(mp_obj_t self_in) { + // check that STA mode is active + wifi_mode_t mode; + ESP_EXCEPTIONS(esp_wifi_get_mode(&mode)); + if ((mode & WIFI_MODE_STA) == 0) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "STA must be active")); + } + + mp_obj_t list = mp_obj_new_list(0, NULL); + wifi_scan_config_t config = { 0 }; + // XXX how do we scan hidden APs (and if we can scan them, are they really hidden?) + MP_THREAD_GIL_EXIT(); + esp_err_t status = esp_wifi_scan_start(&config, 1); + MP_THREAD_GIL_ENTER(); + if (status == 0) { + uint16_t count = 0; + ESP_EXCEPTIONS( esp_wifi_scan_get_ap_num(&count) ); + wifi_ap_record_t *wifi_ap_records = calloc(count, sizeof(wifi_ap_record_t)); + ESP_EXCEPTIONS( esp_wifi_scan_get_ap_records(&count, wifi_ap_records) ); + for (uint16_t i = 0; i < count; i++) { + mp_obj_tuple_t *t = mp_obj_new_tuple(6, NULL); + uint8_t *x = memchr(wifi_ap_records[i].ssid, 0, sizeof(wifi_ap_records[i].ssid)); + int ssid_len = x ? x - wifi_ap_records[i].ssid : sizeof(wifi_ap_records[i].ssid); + t->items[0] = mp_obj_new_bytes(wifi_ap_records[i].ssid, ssid_len); + t->items[1] = mp_obj_new_bytes(wifi_ap_records[i].bssid, sizeof(wifi_ap_records[i].bssid)); + t->items[2] = MP_OBJ_NEW_SMALL_INT(wifi_ap_records[i].primary); + t->items[3] = MP_OBJ_NEW_SMALL_INT(wifi_ap_records[i].rssi); + t->items[4] = MP_OBJ_NEW_SMALL_INT(wifi_ap_records[i].authmode); + t->items[5] = mp_const_false; // XXX hidden? + mp_obj_list_append(list, MP_OBJ_FROM_PTR(t)); + } + free(wifi_ap_records); + } + return list; +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp_scan_obj, esp_scan); + +STATIC mp_obj_t esp_isconnected(mp_obj_t self_in) { + wlan_if_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (self->if_id == WIFI_IF_STA) { + tcpip_adapter_ip_info_t info; + tcpip_adapter_get_ip_info(WIFI_IF_STA, &info); + return mp_obj_new_bool(info.ip.addr != 0); + } else { + wifi_sta_list_t sta; + esp_wifi_ap_get_sta_list(&sta); + return mp_obj_new_bool(sta.num != 0); + } +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp_isconnected_obj, esp_isconnected); + +STATIC mp_obj_t esp_ifconfig(size_t n_args, const mp_obj_t *args) { + wlan_if_obj_t *self = MP_OBJ_TO_PTR(args[0]); + tcpip_adapter_ip_info_t info; + ip_addr_t dns_addr; + tcpip_adapter_get_ip_info(self->if_id, &info); + if (n_args == 1) { + // get + dns_addr = dns_getserver(0); + mp_obj_t tuple[4] = { + netutils_format_ipv4_addr((uint8_t*)&info.ip, NETUTILS_BIG), + netutils_format_ipv4_addr((uint8_t*)&info.netmask, NETUTILS_BIG), + netutils_format_ipv4_addr((uint8_t*)&info.gw, NETUTILS_BIG), + netutils_format_ipv4_addr((uint8_t*)&dns_addr, NETUTILS_BIG), + }; + return mp_obj_new_tuple(4, tuple); + } else { + // set + mp_obj_t *items; + mp_obj_get_array_fixed_n(args[1], 4, &items); + netutils_parse_ipv4_addr(items[0], (void*)&info.ip, NETUTILS_BIG); + if (mp_obj_is_integer(items[1])) { + // allow numeric netmask, i.e.: + // 24 -> 255.255.255.0 + // 16 -> 255.255.0.0 + // etc... + uint32_t* m = (uint32_t*)&info.netmask; + *m = htonl(0xffffffff << (32 - mp_obj_get_int(items[1]))); + } else { + netutils_parse_ipv4_addr(items[1], (void*)&info.netmask, NETUTILS_BIG); + } + netutils_parse_ipv4_addr(items[2], (void*)&info.gw, NETUTILS_BIG); + netutils_parse_ipv4_addr(items[3], (void*)&dns_addr, NETUTILS_BIG); + // To set a static IP we have to disable DHCP first + if (self->if_id == WIFI_IF_STA) { + esp_err_t e = tcpip_adapter_dhcpc_stop(WIFI_IF_STA); + if (e != ESP_OK && e != ESP_ERR_TCPIP_ADAPTER_DHCP_ALREADY_STOPPED) _esp_exceptions(e); + ESP_EXCEPTIONS(tcpip_adapter_set_ip_info(WIFI_IF_STA, &info)); + } else if (self->if_id == WIFI_IF_AP) { + esp_err_t e = tcpip_adapter_dhcps_stop(WIFI_IF_AP); + if (e != ESP_OK && e != ESP_ERR_TCPIP_ADAPTER_DHCP_ALREADY_STOPPED) _esp_exceptions(e); + ESP_EXCEPTIONS(tcpip_adapter_set_ip_info(WIFI_IF_AP, &info)); + ESP_EXCEPTIONS(tcpip_adapter_dhcps_start(WIFI_IF_AP)); + } + return mp_const_none; + } +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp_ifconfig_obj, 1, 2, esp_ifconfig); + +STATIC mp_obj_t esp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { + if (n_args != 1 && kwargs->used != 0) { + mp_raise_TypeError("either pos or kw args are allowed"); + } + + wlan_if_obj_t *self = MP_OBJ_TO_PTR(args[0]); + + // get the config for the interface + wifi_config_t cfg; + ESP_EXCEPTIONS(esp_wifi_get_config(self->if_id, &cfg)); + + if (kwargs->used != 0) { + + for (size_t i = 0; i < kwargs->alloc; i++) { + if (MP_MAP_SLOT_IS_FILLED(kwargs, i)) { + int req_if = -1; + + #define QS(x) (uintptr_t)MP_OBJ_NEW_QSTR(x) + switch ((uintptr_t)kwargs->table[i].key) { + case QS(MP_QSTR_mac): { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(kwargs->table[i].value, &bufinfo, MP_BUFFER_READ); + if (bufinfo.len != 6) { + mp_raise_ValueError("invalid buffer length"); + } + ESP_EXCEPTIONS(esp_wifi_set_mac(self->if_id, bufinfo.buf)); + break; + } + case QS(MP_QSTR_essid): { + req_if = WIFI_IF_AP; + mp_uint_t len; + const char *s = mp_obj_str_get_data(kwargs->table[i].value, &len); + len = MIN(len, sizeof(cfg.ap.ssid)); + memcpy(cfg.ap.ssid, s, len); + cfg.ap.ssid_len = len; + break; + } + case QS(MP_QSTR_hidden): { + req_if = WIFI_IF_AP; + cfg.ap.ssid_hidden = mp_obj_is_true(kwargs->table[i].value); + break; + } + case QS(MP_QSTR_authmode): { + req_if = WIFI_IF_AP; + cfg.ap.authmode = mp_obj_get_int(kwargs->table[i].value); + break; + } + case QS(MP_QSTR_password): { + req_if = WIFI_IF_AP; + mp_uint_t len; + const char *s = mp_obj_str_get_data(kwargs->table[i].value, &len); + len = MIN(len, sizeof(cfg.ap.password) - 1); + memcpy(cfg.ap.password, s, len); + cfg.ap.password[len] = 0; + break; + } + case QS(MP_QSTR_channel): { + req_if = WIFI_IF_AP; + cfg.ap.channel = mp_obj_get_int(kwargs->table[i].value); + break; + } + default: + goto unknown; + } + #undef QS + + // We post-check interface requirements to save on code size + if (req_if >= 0) { + require_if(args[0], req_if); + } + } + } + + ESP_EXCEPTIONS(esp_wifi_set_config(self->if_id, &cfg)); + + return mp_const_none; + } + + // Get config + + if (n_args != 2) { + mp_raise_TypeError("can query only one param"); + } + + int req_if = -1; + mp_obj_t val; + + #define QS(x) (uintptr_t)MP_OBJ_NEW_QSTR(x) + switch ((uintptr_t)args[1]) { + case QS(MP_QSTR_mac): { + uint8_t mac[6]; + ESP_EXCEPTIONS(esp_wifi_get_mac(self->if_id, mac)); + return mp_obj_new_bytes(mac, sizeof(mac)); + } + case QS(MP_QSTR_essid): + req_if = WIFI_IF_AP; + val = mp_obj_new_str((char*)cfg.ap.ssid, cfg.ap.ssid_len); + break; + case QS(MP_QSTR_hidden): + req_if = WIFI_IF_AP; + val = mp_obj_new_bool(cfg.ap.ssid_hidden); + break; + case QS(MP_QSTR_authmode): + req_if = WIFI_IF_AP; + val = MP_OBJ_NEW_SMALL_INT(cfg.ap.authmode); + break; + case QS(MP_QSTR_channel): + req_if = WIFI_IF_AP; + val = MP_OBJ_NEW_SMALL_INT(cfg.ap.channel); + break; + default: + goto unknown; + } + #undef QS + + // We post-check interface requirements to save on code size + if (req_if >= 0) { + require_if(args[0], req_if); + } + + return val; + +unknown: + mp_raise_ValueError("unknown config param"); +} + +STATIC MP_DEFINE_CONST_FUN_OBJ_KW(esp_config_obj, 1, esp_config); + +STATIC const mp_map_elem_t wlan_if_locals_dict_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR_active), (mp_obj_t)&esp_active_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_connect), (mp_obj_t)&esp_connect_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_disconnect), (mp_obj_t)&esp_disconnect_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_status), (mp_obj_t)&esp_status_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_scan), (mp_obj_t)&esp_scan_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_isconnected), (mp_obj_t)&esp_isconnected_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_config), (mp_obj_t)&esp_config_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_ifconfig), (mp_obj_t)&esp_ifconfig_obj }, +}; + +STATIC MP_DEFINE_CONST_DICT(wlan_if_locals_dict, wlan_if_locals_dict_table); + +const mp_obj_type_t wlan_if_type = { + { &mp_type_type }, + .name = MP_QSTR_WLAN, + .locals_dict = (mp_obj_t)&wlan_if_locals_dict, +}; + +STATIC mp_obj_t esp_phy_mode(size_t n_args, const mp_obj_t *args) { + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp_phy_mode_obj, 0, 1, esp_phy_mode); + + +STATIC const mp_map_elem_t mp_module_network_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_network) }, + { MP_OBJ_NEW_QSTR(MP_QSTR___init__), (mp_obj_t)&esp_initialize_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_WLAN), (mp_obj_t)&get_wlan_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_phy_mode), (mp_obj_t)&esp_phy_mode_obj }, + +#if MODNETWORK_INCLUDE_CONSTANTS + { MP_OBJ_NEW_QSTR(MP_QSTR_STA_IF), + MP_OBJ_NEW_SMALL_INT(WIFI_IF_STA)}, + { MP_OBJ_NEW_QSTR(MP_QSTR_AP_IF), + MP_OBJ_NEW_SMALL_INT(WIFI_IF_AP)}, + + { MP_OBJ_NEW_QSTR(MP_QSTR_MODE_11B), + MP_OBJ_NEW_SMALL_INT(WIFI_PROTOCOL_11B) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_MODE_11G), + MP_OBJ_NEW_SMALL_INT(WIFI_PROTOCOL_11G) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_MODE_11N), + MP_OBJ_NEW_SMALL_INT(WIFI_PROTOCOL_11N) }, + + { MP_OBJ_NEW_QSTR(MP_QSTR_AUTH_OPEN), + MP_OBJ_NEW_SMALL_INT(WIFI_AUTH_OPEN) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_AUTH_WEP), + MP_OBJ_NEW_SMALL_INT(WIFI_AUTH_WEP) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_AUTH_WPA_PSK), + MP_OBJ_NEW_SMALL_INT(WIFI_AUTH_WPA_PSK) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_AUTH_WPA2_PSK), + MP_OBJ_NEW_SMALL_INT(WIFI_AUTH_WPA2_PSK) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_AUTH_WPA_WPA2_PSK), + MP_OBJ_NEW_SMALL_INT(WIFI_AUTH_WPA_WPA2_PSK) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_AUTH_MAX), + MP_OBJ_NEW_SMALL_INT(WIFI_AUTH_MAX) }, +#endif +}; + +STATIC MP_DEFINE_CONST_DICT(mp_module_network_globals, mp_module_network_globals_table); + +const mp_obj_module_t mp_module_network = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mp_module_network_globals, +}; diff --git a/ports/esp32/modsocket.c b/ports/esp32/modsocket.c new file mode 100644 index 0000000000..6ba61e874f --- /dev/null +++ b/ports/esp32/modsocket.c @@ -0,0 +1,570 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * Development of the code in this file was sponsored by Microbric Pty Ltd + * and Mnemote Pty Ltd + * + * The MIT License (MIT) + * + * Copyright (c) 2016, 2017 Nick Moore @mnemote + * + * Based on extmod/modlwip.c + * Copyright (c) 2013, 2014 Damien P. George + * Copyright (c) 2015 Galen Hazelwood + * + * 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 +#include +#include +#include + +#include "py/runtime0.h" +#include "py/nlr.h" +#include "py/objlist.h" +#include "py/objstr.h" +#include "py/runtime.h" +#include "py/mperrno.h" +#include "py/mphal.h" +#include "py/stream.h" +#include "py/mperrno.h" +#include "lib/netutils/netutils.h" +#include "tcpip_adapter.h" + +#include "lwip/sockets.h" +#include "lwip/netdb.h" +#include "lwip/ip4.h" +#include "esp_log.h" + +#define SOCKET_POLL_US (100000) + +typedef struct _socket_obj_t { + mp_obj_base_t base; + int fd; + uint8_t domain; + uint8_t type; + uint8_t proto; + unsigned int retries; +} socket_obj_t; + +void _socket_settimeout(socket_obj_t *sock, uint64_t timeout_ms); + +NORETURN static void exception_from_errno(int _errno) { + // Here we need to convert from lwip errno values to MicroPython's standard ones + if (_errno == EINPROGRESS) { + _errno = MP_EINPROGRESS; + } + mp_raise_OSError(_errno); +} + +void check_for_exceptions() { + mp_obj_t exc = MP_STATE_VM(mp_pending_exception); + if (exc != MP_OBJ_NULL) { + MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL; + nlr_raise(exc); + } +} + +STATIC mp_obj_t socket_close(const mp_obj_t arg0) { + socket_obj_t *self = MP_OBJ_TO_PTR(arg0); + if (self->fd >= 0) { + int ret = lwip_close_r(self->fd); + if (ret != 0) { + exception_from_errno(errno); + } + self->fd = -1; + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(socket_close_obj, socket_close); + +static int _socket_getaddrinfo2(const mp_obj_t host, const mp_obj_t portx, struct addrinfo **resp) { + const struct addrinfo hints = { + .ai_family = AF_INET, + .ai_socktype = SOCK_STREAM, + }; + + mp_obj_t port = portx; + if (MP_OBJ_IS_SMALL_INT(port)) { + // This is perverse, because lwip_getaddrinfo promptly converts it back to an int, but + // that's the API we have to work with ... + port = mp_obj_str_binary_op(MP_BINARY_OP_MODULO, mp_obj_new_str_via_qstr("%s", 2), port); + } + + const char *host_str = mp_obj_str_get_str(host); + const char *port_str = mp_obj_str_get_str(port); + + if (host_str[0] == '\0') { + // a host of "" is equivalent to the default/all-local IP address + host_str = "0.0.0.0"; + } + + MP_THREAD_GIL_EXIT(); + int res = lwip_getaddrinfo(host_str, port_str, &hints, resp); + MP_THREAD_GIL_ENTER(); + + return res; +} + +int _socket_getaddrinfo(const mp_obj_t addrtuple, struct addrinfo **resp) { + mp_uint_t len = 0; + mp_obj_t *elem; + mp_obj_get_array(addrtuple, &len, &elem); + if (len != 2) return -1; + return _socket_getaddrinfo2(elem[0], elem[1], resp); +} + +STATIC mp_obj_t socket_bind(const mp_obj_t arg0, const mp_obj_t arg1) { + socket_obj_t *self = MP_OBJ_TO_PTR(arg0); + struct addrinfo *res; + _socket_getaddrinfo(arg1, &res); + int r = lwip_bind_r(self->fd, res->ai_addr, res->ai_addrlen); + lwip_freeaddrinfo(res); + if (r < 0) exception_from_errno(errno); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_bind_obj, socket_bind); + +STATIC mp_obj_t socket_listen(const mp_obj_t arg0, const mp_obj_t arg1) { + socket_obj_t *self = MP_OBJ_TO_PTR(arg0); + int backlog = mp_obj_get_int(arg1); + int r = lwip_listen_r(self->fd, backlog); + if (r < 0) exception_from_errno(errno); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_listen_obj, socket_listen); + +STATIC mp_obj_t socket_accept(const mp_obj_t arg0) { + socket_obj_t *self = MP_OBJ_TO_PTR(arg0); + + struct sockaddr addr; + socklen_t addr_len = sizeof(addr); + + int new_fd = -1; + for (int i=0; i<=self->retries; i++) { + MP_THREAD_GIL_EXIT(); + new_fd = lwip_accept_r(self->fd, &addr, &addr_len); + MP_THREAD_GIL_ENTER(); + if (new_fd >= 0) break; + if (errno != EAGAIN) exception_from_errno(errno); + check_for_exceptions(); + } + if (new_fd < 0) mp_raise_OSError(MP_ETIMEDOUT); + + // create new socket object + socket_obj_t *sock = m_new_obj_with_finaliser(socket_obj_t); + sock->base.type = self->base.type; + sock->fd = new_fd; + sock->domain = self->domain; + sock->type = self->type; + sock->proto = self->proto; + _socket_settimeout(sock, UINT64_MAX); + + // make the return value + uint8_t *ip = (uint8_t*)&((struct sockaddr_in*)&addr)->sin_addr; + mp_uint_t port = lwip_ntohs(((struct sockaddr_in*)&addr)->sin_port); + mp_obj_tuple_t *client = mp_obj_new_tuple(2, NULL); + client->items[0] = sock; + client->items[1] = netutils_format_inet_addr(ip, port, NETUTILS_BIG); + + return client; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(socket_accept_obj, socket_accept); + +STATIC mp_obj_t socket_connect(const mp_obj_t arg0, const mp_obj_t arg1) { + socket_obj_t *self = MP_OBJ_TO_PTR(arg0); + struct addrinfo *res; + _socket_getaddrinfo(arg1, &res); + MP_THREAD_GIL_EXIT(); + int r = lwip_connect_r(self->fd, res->ai_addr, res->ai_addrlen); + MP_THREAD_GIL_ENTER(); + lwip_freeaddrinfo(res); + if (r != 0) { + exception_from_errno(errno); + } + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_connect_obj, socket_connect); + +STATIC mp_obj_t socket_setsockopt(size_t n_args, const mp_obj_t *args) { + (void)n_args; // always 4 + socket_obj_t *self = MP_OBJ_TO_PTR(args[0]); + + int level = mp_obj_get_int(args[1]); + if (level != SOL_SOCKET) { + mp_raise_ValueError("unsupported level"); + } + + // only "int" arguments are supported at the moment + int opt = mp_obj_get_int(args[2]); + int val = mp_obj_get_int(args[3]); + int ret = lwip_setsockopt_r(self->fd, SOL_SOCKET, opt, &val, sizeof(int)); + + if (ret != 0) { + exception_from_errno(errno); + } + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_setsockopt_obj, 4, 4, socket_setsockopt); + +void _socket_settimeout(socket_obj_t *sock, uint64_t timeout_ms) { + // Rather than waiting for the entire timeout specified, we wait sock->retries times + // for SOCKET_POLL_US each, checking for a MicroPython interrupt between timeouts. + // with SOCKET_POLL_MS == 100ms, sock->retries allows for timeouts up to 13 years. + // if timeout_ms == UINT64_MAX, wait forever. + sock->retries = (timeout_ms == UINT64_MAX) ? UINT_MAX : timeout_ms * 1000 / SOCKET_POLL_US; + + struct timeval timeout = { + .tv_sec = 0, + .tv_usec = timeout_ms ? SOCKET_POLL_US : 0 + }; + lwip_setsockopt_r(sock->fd, SOL_SOCKET, SO_SNDTIMEO, (const void *)&timeout, sizeof(timeout)); + lwip_setsockopt_r(sock->fd, SOL_SOCKET, SO_RCVTIMEO, (const void *)&timeout, sizeof(timeout)); + lwip_fcntl_r(sock->fd, F_SETFL, timeout_ms ? 0 : O_NONBLOCK); +} + +STATIC mp_obj_t socket_settimeout(const mp_obj_t arg0, const mp_obj_t arg1) { + socket_obj_t *self = MP_OBJ_TO_PTR(arg0); + if (arg1 == mp_const_none) _socket_settimeout(self, UINT64_MAX); + else _socket_settimeout(self, mp_obj_get_float(arg1) * 1000L); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_settimeout_obj, socket_settimeout); + +STATIC mp_obj_t socket_setblocking(const mp_obj_t arg0, const mp_obj_t arg1) { + socket_obj_t *self = MP_OBJ_TO_PTR(arg0); + if (mp_obj_is_true(arg1)) _socket_settimeout(self, UINT64_MAX); + else _socket_settimeout(self, 0); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_setblocking_obj, socket_setblocking); + +mp_obj_t _socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in, + struct sockaddr *from, socklen_t *from_len) { + socket_obj_t *sock = MP_OBJ_TO_PTR(self_in); + size_t len = mp_obj_get_int(len_in); + vstr_t vstr; + vstr_init_len(&vstr, len); + + // XXX Would be nicer to use RTC to handle timeouts + for (int i=0; i<=sock->retries; i++) { + MP_THREAD_GIL_EXIT(); + int r = lwip_recvfrom_r(sock->fd, vstr.buf, len, 0, from, from_len); + MP_THREAD_GIL_ENTER(); + if (r >= 0) { vstr.len = r; return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); } + if (errno != EWOULDBLOCK) exception_from_errno(errno); + check_for_exceptions(); + } + mp_raise_OSError(MP_ETIMEDOUT); +} + +STATIC mp_obj_t socket_recv(mp_obj_t self_in, mp_obj_t len_in) { + return _socket_recvfrom(self_in, len_in, NULL, NULL); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_recv_obj, socket_recv); + +STATIC mp_obj_t socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in) { + struct sockaddr from; + socklen_t fromlen = sizeof(from); + + mp_obj_t tuple[2]; + tuple[0] = _socket_recvfrom(self_in, len_in, &from, &fromlen); + + uint8_t *ip = (uint8_t*)&((struct sockaddr_in*)&from)->sin_addr; + mp_uint_t port = lwip_ntohs(((struct sockaddr_in*)&from)->sin_port); + tuple[1] = netutils_format_inet_addr(ip, port, NETUTILS_BIG); + + return mp_obj_new_tuple(2, tuple); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_recvfrom_obj, socket_recvfrom); + +int _socket_send(socket_obj_t *sock, const char *data, size_t datalen) { + int sentlen = 0; + for (int i=0; i<=sock->retries && sentlen < datalen; i++) { + MP_THREAD_GIL_EXIT(); + int r = lwip_write_r(sock->fd, data+sentlen, datalen-sentlen); + MP_THREAD_GIL_ENTER(); + if (r < 0 && errno != EWOULDBLOCK) exception_from_errno(errno); + if (r > 0) sentlen += r; + check_for_exceptions(); + } + if (sentlen == 0) mp_raise_OSError(MP_ETIMEDOUT); + return sentlen; +} + +STATIC mp_obj_t socket_send(const mp_obj_t arg0, const mp_obj_t arg1) { + socket_obj_t *sock = MP_OBJ_TO_PTR(arg0); + mp_uint_t datalen; + const char *data = mp_obj_str_get_data(arg1, &datalen); + int r = _socket_send(sock, data, datalen); + return mp_obj_new_int(r); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_send_obj, socket_send); + +STATIC mp_obj_t socket_sendall(const mp_obj_t arg0, const mp_obj_t arg1) { + // XXX behaviour when nonblocking (see extmod/modlwip.c) + // XXX also timeout behaviour. + socket_obj_t *sock = MP_OBJ_TO_PTR(arg0); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(arg1, &bufinfo, MP_BUFFER_READ); + int r = _socket_send(sock, bufinfo.buf, bufinfo.len); + if (r < bufinfo.len) mp_raise_OSError(MP_ETIMEDOUT); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_sendall_obj, socket_sendall); + +STATIC mp_obj_t socket_sendto(mp_obj_t self_in, mp_obj_t data_in, mp_obj_t addr_in) { + socket_obj_t *self = MP_OBJ_TO_PTR(self_in); + + // get the buffer to send + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(data_in, &bufinfo, MP_BUFFER_READ); + + // create the destination address + struct sockaddr_in to; + to.sin_len = sizeof(to); + to.sin_family = AF_INET; + to.sin_port = lwip_htons(netutils_parse_inet_addr(addr_in, (uint8_t*)&to.sin_addr, NETUTILS_BIG)); + + // send the data + for (int i=0; i<=self->retries; i++) { + MP_THREAD_GIL_EXIT(); + int ret = lwip_sendto_r(self->fd, bufinfo.buf, bufinfo.len, 0, (struct sockaddr*)&to, sizeof(to)); + MP_THREAD_GIL_ENTER(); + if (ret > 0) return mp_obj_new_int_from_uint(ret); + if (ret == -1 && errno != EWOULDBLOCK) { + exception_from_errno(errno); + } + check_for_exceptions(); + } + mp_raise_OSError(MP_ETIMEDOUT); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_3(socket_sendto_obj, socket_sendto); + +STATIC mp_obj_t socket_fileno(const mp_obj_t arg0) { + socket_obj_t *self = MP_OBJ_TO_PTR(arg0); + return mp_obj_new_int(self->fd); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(socket_fileno_obj, socket_fileno); + +STATIC mp_obj_t socket_makefile(size_t n_args, const mp_obj_t *args) { + (void)n_args; + return args[0]; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_makefile_obj, 1, 3, socket_makefile); + + +// XXX this can end up waiting a very long time if the content is dribbled in one character +// at a time, as the timeout resets each time a recvfrom succeeds ... this is probably not +// good behaviour. + +STATIC mp_uint_t socket_stream_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) { + socket_obj_t *sock = self_in; + + // XXX Would be nicer to use RTC to handle timeouts + for (int i=0; i<=sock->retries; i++) { + MP_THREAD_GIL_EXIT(); + int r = lwip_recvfrom_r(sock->fd, buf, size, 0, NULL, NULL); + MP_THREAD_GIL_ENTER(); + if (r >= 0) return r; + if (r < 0 && errno != EWOULDBLOCK) { *errcode = errno; return MP_STREAM_ERROR; } + check_for_exceptions(); + } + *errcode = sock->retries == 0 ? MP_EWOULDBLOCK : MP_ETIMEDOUT; + return MP_STREAM_ERROR; +} + +STATIC mp_uint_t socket_stream_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) { + socket_obj_t *sock = self_in; + for (int i=0; i<=sock->retries; i++) { + MP_THREAD_GIL_EXIT(); + int r = lwip_write_r(sock->fd, buf, size); + MP_THREAD_GIL_ENTER(); + if (r > 0) return r; + if (r < 0 && errno != EWOULDBLOCK) { *errcode = errno; return MP_STREAM_ERROR; } + check_for_exceptions(); + } + *errcode = sock->retries == 0 ? MP_EWOULDBLOCK : MP_ETIMEDOUT; + return MP_STREAM_ERROR; +} + +STATIC mp_uint_t socket_stream_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) { + socket_obj_t * socket = self_in; + if (request == MP_STREAM_POLL) { + + fd_set rfds; FD_ZERO(&rfds); + fd_set wfds; FD_ZERO(&wfds); + fd_set efds; FD_ZERO(&efds); + struct timeval timeout = { .tv_sec = 0, .tv_usec = 0 }; + if (arg & MP_STREAM_POLL_RD) FD_SET(socket->fd, &rfds); + if (arg & MP_STREAM_POLL_WR) FD_SET(socket->fd, &wfds); + if (arg & MP_STREAM_POLL_HUP) FD_SET(socket->fd, &efds); + + int r = select((socket->fd)+1, &rfds, &wfds, &efds, &timeout); + if (r < 0) { + *errcode = MP_EIO; + return MP_STREAM_ERROR; + } + + mp_uint_t ret = 0; + if (FD_ISSET(socket->fd, &rfds)) ret |= MP_STREAM_POLL_RD; + if (FD_ISSET(socket->fd, &wfds)) ret |= MP_STREAM_POLL_WR; + if (FD_ISSET(socket->fd, &efds)) ret |= MP_STREAM_POLL_HUP; + return ret; + } + + *errcode = MP_EINVAL; + return MP_STREAM_ERROR; +} + +STATIC const mp_map_elem_t socket_locals_dict_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___del__), (mp_obj_t)&socket_close_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_close), (mp_obj_t)&socket_close_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_bind), (mp_obj_t)&socket_bind_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_listen), (mp_obj_t)&socket_listen_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_accept), (mp_obj_t)&socket_accept_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_connect), (mp_obj_t)&socket_connect_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_send), (mp_obj_t)&socket_send_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_sendall), (mp_obj_t)&socket_sendall_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_sendto), (mp_obj_t)&socket_sendto_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_recv), (mp_obj_t)&socket_recv_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_recvfrom), (mp_obj_t)&socket_recvfrom_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_setsockopt), (mp_obj_t)&socket_setsockopt_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_settimeout), (mp_obj_t)&socket_settimeout_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_setblocking), (mp_obj_t)&socket_setblocking_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_makefile), (mp_obj_t)&socket_makefile_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_fileno), (mp_obj_t)&socket_fileno_obj }, + + { MP_OBJ_NEW_QSTR(MP_QSTR_read), (mp_obj_t)&mp_stream_read_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_readinto), (mp_obj_t)&mp_stream_readinto_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_readline), (mp_obj_t)&mp_stream_unbuffered_readline_obj}, + { MP_OBJ_NEW_QSTR(MP_QSTR_write), (mp_obj_t)&mp_stream_write_obj }, +}; +STATIC MP_DEFINE_CONST_DICT(socket_locals_dict, socket_locals_dict_table); + +STATIC const mp_stream_p_t socket_stream_p = { + .read = socket_stream_read, + .write = socket_stream_write, + .ioctl = socket_stream_ioctl +}; + +STATIC const mp_obj_type_t socket_type = { + { &mp_type_type }, + .name = MP_QSTR_socket, + .protocol = &socket_stream_p, + .locals_dict = (mp_obj_t)&socket_locals_dict, +}; + +STATIC mp_obj_t get_socket(size_t n_args, const mp_obj_t *args) { + socket_obj_t *sock = m_new_obj_with_finaliser(socket_obj_t); + sock->base.type = &socket_type; + sock->domain = AF_INET; + sock->type = SOCK_STREAM; + sock->proto = 0; + if (n_args > 0) { + sock->domain = mp_obj_get_int(args[0]); + if (n_args > 1) { + sock->type = mp_obj_get_int(args[1]); + if (n_args > 2) { + sock->proto = mp_obj_get_int(args[2]); + } + } + } + + sock->fd = lwip_socket(sock->domain, sock->type, sock->proto); + if (sock->fd < 0) { + exception_from_errno(errno); + } + _socket_settimeout(sock, UINT64_MAX); + + return MP_OBJ_FROM_PTR(sock); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(get_socket_obj, 0, 3, get_socket); + +STATIC mp_obj_t esp_socket_getaddrinfo(const mp_obj_t host, const mp_obj_t port) { + struct addrinfo *res = NULL; + _socket_getaddrinfo2(host, port, &res); + mp_obj_t ret_list = mp_obj_new_list(0, NULL); + + for (struct addrinfo *resi = res; resi; resi = resi->ai_next) { + mp_obj_t addrinfo_objs[5] = { + mp_obj_new_int(resi->ai_family), + mp_obj_new_int(resi->ai_socktype), + mp_obj_new_int(resi->ai_protocol), + mp_obj_new_str(resi->ai_canonname, strlen(resi->ai_canonname)), + mp_const_none + }; + + if (resi->ai_family == AF_INET) { + struct sockaddr_in *addr = (struct sockaddr_in *)resi->ai_addr; + // This looks odd, but it's really just a u32_t + ip4_addr_t ip4_addr = { .addr = addr->sin_addr.s_addr }; + char buf[16]; + ip4addr_ntoa_r(&ip4_addr, buf, sizeof(buf)); + mp_obj_t inaddr_objs[2] = { + mp_obj_new_str(buf, strlen(buf)), + mp_obj_new_int(ntohs(addr->sin_port)) + }; + addrinfo_objs[4] = mp_obj_new_tuple(2, inaddr_objs); + } + mp_obj_list_append(ret_list, mp_obj_new_tuple(5, addrinfo_objs)); + } + + if (res) lwip_freeaddrinfo(res); + return ret_list; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(esp_socket_getaddrinfo_obj, esp_socket_getaddrinfo); + +STATIC mp_obj_t esp_socket_initialize() { + static int initialized = 0; + if (!initialized) { + ESP_LOGI("modsocket", "Initializing"); + tcpip_adapter_init(); + initialized = 1; + } + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(esp_socket_initialize_obj, esp_socket_initialize); + +STATIC const mp_map_elem_t mp_module_socket_globals_table[] = { + { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_usocket) }, + { MP_OBJ_NEW_QSTR(MP_QSTR___init__), (mp_obj_t)&esp_socket_initialize_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_socket), (mp_obj_t)&get_socket_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_getaddrinfo), (mp_obj_t)&esp_socket_getaddrinfo_obj }, + + { MP_OBJ_NEW_QSTR(MP_QSTR_AF_INET), MP_OBJ_NEW_SMALL_INT(AF_INET) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_AF_INET6), MP_OBJ_NEW_SMALL_INT(AF_INET6) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_SOCK_STREAM), MP_OBJ_NEW_SMALL_INT(SOCK_STREAM) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_SOCK_DGRAM), MP_OBJ_NEW_SMALL_INT(SOCK_DGRAM) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_SOCK_RAW), MP_OBJ_NEW_SMALL_INT(SOCK_RAW) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_IPPROTO_TCP), MP_OBJ_NEW_SMALL_INT(IPPROTO_TCP) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_IPPROTO_UDP), MP_OBJ_NEW_SMALL_INT(IPPROTO_UDP) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_SOL_SOCKET), MP_OBJ_NEW_SMALL_INT(SOL_SOCKET) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_SO_REUSEADDR), MP_OBJ_NEW_SMALL_INT(SO_REUSEADDR) }, +}; + +STATIC MP_DEFINE_CONST_DICT(mp_module_socket_globals, mp_module_socket_globals_table); + +const mp_obj_module_t mp_module_usocket = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mp_module_socket_globals, +}; diff --git a/ports/esp32/moduhashlib.c b/ports/esp32/moduhashlib.c new file mode 100644 index 0000000000..6f67aa7d9d --- /dev/null +++ b/ports/esp32/moduhashlib.c @@ -0,0 +1,142 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014 Paul Sokolovsky + * + * 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/runtime.h" + +#include "mbedtls/sha256.h" +#include "mbedtls/sha1.h" + +union sha_ctxs { + mbedtls_sha256_context sha256; + mbedtls_sha1_context sha1; +}; +typedef struct _mp_obj_hash_t { + mp_obj_base_t base; + union sha_ctxs state; +} mp_obj_hash_t; + +STATIC mp_obj_t sha256_update(mp_obj_t self_in, mp_obj_t arg); +STATIC mp_obj_t sha1_update(mp_obj_t self_in, mp_obj_t arg); + +STATIC mp_obj_t sha256_make_new(const mp_obj_type_t *type, + size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 0, 1, false); + mp_obj_hash_t *o = m_new_obj_var(mp_obj_hash_t, char, sizeof(union sha_ctxs)); + o->base.type = type; + mbedtls_sha256_init(&o->state.sha256); + if (n_args == 1) { + sha256_update(MP_OBJ_FROM_PTR(o), args[0]); + } + return MP_OBJ_FROM_PTR(o); +} + +STATIC mp_obj_t sha1_make_new(const mp_obj_type_t *type, + size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 0, 1, false); + mp_obj_hash_t *o = m_new_obj_var(mp_obj_hash_t, char, sizeof(union sha_ctxs)); + o->base.type = type; + mbedtls_sha1_init(&o->state.sha1); + if (n_args == 1) { + sha1_update(MP_OBJ_FROM_PTR(o), args[0]); + } + return MP_OBJ_FROM_PTR(o); +} + +STATIC mp_obj_t sha256_update(mp_obj_t self_in, mp_obj_t arg) { + mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_READ); + mbedtls_sha256_update(&self->state.sha256, bufinfo.buf, bufinfo.len); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(sha256_update_obj, sha256_update); + +STATIC mp_obj_t sha1_update(mp_obj_t self_in, mp_obj_t arg) { + mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in); + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_READ); + mbedtls_sha1_update(&self->state.sha1, bufinfo.buf, bufinfo.len); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(sha1_update_obj, sha1_update); + +STATIC mp_obj_t sha256_digest(mp_obj_t self_in) { + mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in); + vstr_t vstr; + vstr_init_len(&vstr, 32); + mbedtls_sha256_finish(&self->state.sha256, (unsigned char *)vstr.buf); + return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); +} +MP_DEFINE_CONST_FUN_OBJ_1(sha256_digest_obj, sha256_digest); + +STATIC mp_obj_t sha1_digest(mp_obj_t self_in) { + mp_obj_hash_t *self = MP_OBJ_TO_PTR(self_in); + vstr_t vstr; + vstr_init_len(&vstr, 20); + mbedtls_sha1_finish(&self->state.sha1, (unsigned char *)vstr.buf); + return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); +} +MP_DEFINE_CONST_FUN_OBJ_1(sha1_digest_obj, sha1_digest); + +STATIC const mp_rom_map_elem_t sha256_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&sha256_update_obj) }, + { MP_ROM_QSTR(MP_QSTR_digest), MP_ROM_PTR(&sha256_digest_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(sha256_locals_dict, sha256_locals_dict_table); + +STATIC const mp_obj_type_t sha256_type = { + { &mp_type_type }, + .name = MP_QSTR_sha256, + .make_new = sha256_make_new, + .locals_dict = (void*)&sha256_locals_dict, +}; + +STATIC const mp_rom_map_elem_t sha1_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&sha1_update_obj) }, + { MP_ROM_QSTR(MP_QSTR_digest), MP_ROM_PTR(&sha1_digest_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(sha1_locals_dict, sha1_locals_dict_table); + +STATIC const mp_obj_type_t sha1_type = { + { &mp_type_type }, + .name = MP_QSTR_sha1, + .make_new = sha1_make_new, + .locals_dict = (void*)&sha1_locals_dict, +}; + +STATIC const mp_rom_map_elem_t mp_module_hashlib_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uhashlib) }, + { MP_ROM_QSTR(MP_QSTR_sha256), MP_ROM_PTR(&sha256_type) }, + { MP_ROM_QSTR(MP_QSTR_sha1), MP_ROM_PTR(&sha1_type) }, +}; + +STATIC MP_DEFINE_CONST_DICT(mp_module_hashlib_globals, + mp_module_hashlib_globals_table); + +const mp_obj_module_t mp_module_uhashlib = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mp_module_hashlib_globals, +}; diff --git a/ports/esp32/modules/_boot.py b/ports/esp32/modules/_boot.py new file mode 100644 index 0000000000..bf40ea3a9f --- /dev/null +++ b/ports/esp32/modules/_boot.py @@ -0,0 +1,12 @@ +import gc +import uos +from flashbdev import bdev + +try: + if bdev: + uos.mount(bdev, '/') +except OSError: + import inisetup + vfs = inisetup.setup() + +gc.collect() diff --git a/ports/esp32/modules/apa106.py b/ports/esp32/modules/apa106.py new file mode 100644 index 0000000000..ef971d78ba --- /dev/null +++ b/ports/esp32/modules/apa106.py @@ -0,0 +1,8 @@ +# APA106driver for MicroPython on ESP32 +# MIT license; Copyright (c) 2016 Damien P. George + +from neopixel import NeoPixel + + +class APA106(NeoPixel): + ORDER = (0, 1, 2, 3) diff --git a/ports/esp32/modules/dht.py b/ports/esp32/modules/dht.py new file mode 120000 index 0000000000..b6acf2853e --- /dev/null +++ b/ports/esp32/modules/dht.py @@ -0,0 +1 @@ +../../esp8266/modules/dht.py \ No newline at end of file diff --git a/ports/esp32/modules/ds18x20.py b/ports/esp32/modules/ds18x20.py new file mode 120000 index 0000000000..9721929a3f --- /dev/null +++ b/ports/esp32/modules/ds18x20.py @@ -0,0 +1 @@ +../../esp8266/modules/ds18x20.py \ No newline at end of file diff --git a/ports/esp32/modules/flashbdev.py b/ports/esp32/modules/flashbdev.py new file mode 100644 index 0000000000..935f5342fc --- /dev/null +++ b/ports/esp32/modules/flashbdev.py @@ -0,0 +1,34 @@ +import esp + +class FlashBdev: + + SEC_SIZE = 4096 + START_SEC = esp.flash_user_start() // SEC_SIZE + + def __init__(self, blocks): + self.blocks = blocks + + def readblocks(self, n, buf): + #print("readblocks(%s, %x(%d))" % (n, id(buf), len(buf))) + esp.flash_read((n + self.START_SEC) * self.SEC_SIZE, buf) + + def writeblocks(self, n, buf): + #print("writeblocks(%s, %x(%d))" % (n, id(buf), len(buf))) + #assert len(buf) <= self.SEC_SIZE, len(buf) + esp.flash_erase(n + self.START_SEC) + esp.flash_write((n + self.START_SEC) * self.SEC_SIZE, buf) + + def ioctl(self, op, arg): + #print("ioctl(%d, %r)" % (op, arg)) + if op == 4: # BP_IOCTL_SEC_COUNT + return self.blocks + if op == 5: # BP_IOCTL_SEC_SIZE + return self.SEC_SIZE + +size = esp.flash_size() +if size < 1024*1024: + # flash too small for a filesystem + bdev = None +else: + # for now we use a fixed size for the filesystem + bdev = FlashBdev(2048 * 1024 // FlashBdev.SEC_SIZE) diff --git a/ports/esp32/modules/inisetup.py b/ports/esp32/modules/inisetup.py new file mode 100644 index 0000000000..45bce82cc2 --- /dev/null +++ b/ports/esp32/modules/inisetup.py @@ -0,0 +1,38 @@ +import uos +from flashbdev import bdev + +def check_bootsec(): + buf = bytearray(bdev.SEC_SIZE) + bdev.readblocks(0, buf) + empty = True + for b in buf: + if b != 0xff: + empty = False + break + if empty: + return True + fs_corrupted() + +def fs_corrupted(): + import time + while 1: + print("""\ +FAT filesystem appears to be corrupted. If you had important data there, you +may want to make a flash snapshot to try to recover it. Otherwise, perform +factory reprogramming of MicroPython firmware (completely erase flash, followed +by firmware programming). +""") + time.sleep(3) + +def setup(): + check_bootsec() + print("Performing initial setup") + uos.VfsFat.mkfs(bdev) + vfs = uos.VfsFat(bdev) + uos.mount(vfs, '/flash') + uos.chdir('/flash') + with open("boot.py", "w") as f: + f.write("""\ +# This file is executed on every boot (including wake-boot from deepsleep) +""") + return vfs diff --git a/ports/esp32/modules/neopixel.py b/ports/esp32/modules/neopixel.py new file mode 100644 index 0000000000..86c1586cdd --- /dev/null +++ b/ports/esp32/modules/neopixel.py @@ -0,0 +1,33 @@ +# NeoPixel driver for MicroPython on ESP32 +# MIT license; Copyright (c) 2016 Damien P. George + +from esp import neopixel_write + + +class NeoPixel: + ORDER = (1, 0, 2, 3) + + def __init__(self, pin, n, bpp=3, timing=0): + self.pin = pin + self.n = n + self.bpp = bpp + self.buf = bytearray(n * bpp) + self.pin.init(pin.OUT) + self.timing = timing + + def __setitem__(self, index, val): + offset = index * self.bpp + for i in range(self.bpp): + self.buf[offset + self.ORDER[i]] = val[i] + + def __getitem__(self, index): + offset = index * self.bpp + return tuple(self.buf[offset + self.ORDER[i]] + for i in range(self.bpp)) + + def fill(self, color): + for i in range(self.n): + self[i] = color + + def write(self): + neopixel_write(self.pin, self.buf, self.timing) diff --git a/ports/esp32/modules/onewire.py b/ports/esp32/modules/onewire.py new file mode 120000 index 0000000000..091629488e --- /dev/null +++ b/ports/esp32/modules/onewire.py @@ -0,0 +1 @@ +../../esp8266/modules/onewire.py \ No newline at end of file diff --git a/ports/esp32/modules/upip.py b/ports/esp32/modules/upip.py new file mode 120000 index 0000000000..130eb69016 --- /dev/null +++ b/ports/esp32/modules/upip.py @@ -0,0 +1 @@ +../../../tools/upip.py \ No newline at end of file diff --git a/ports/esp32/modules/upip_utarfile.py b/ports/esp32/modules/upip_utarfile.py new file mode 120000 index 0000000000..d9653d6a60 --- /dev/null +++ b/ports/esp32/modules/upip_utarfile.py @@ -0,0 +1 @@ +../../../tools/upip_utarfile.py \ No newline at end of file diff --git a/ports/esp32/modules/upysh.py b/ports/esp32/modules/upysh.py new file mode 120000 index 0000000000..12d100c29c --- /dev/null +++ b/ports/esp32/modules/upysh.py @@ -0,0 +1 @@ +../../../../micropython-lib/upysh/upysh.py \ No newline at end of file diff --git a/ports/esp32/modules/urequests.py b/ports/esp32/modules/urequests.py new file mode 120000 index 0000000000..76661112e1 --- /dev/null +++ b/ports/esp32/modules/urequests.py @@ -0,0 +1 @@ +../../../../micropython-lib/urequests/urequests.py \ No newline at end of file diff --git a/ports/esp32/moduos.c b/ports/esp32/moduos.c new file mode 100644 index 0000000000..9f0e291a68 --- /dev/null +++ b/ports/esp32/moduos.c @@ -0,0 +1,128 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * Development of the code in this file was sponsored by Microbric Pty Ltd + * + * The MIT License (MIT) + * + * Copyright (c) 2015 Josef Gajdusek + * Copyright (c) 2016 Damien P. George + * + * 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 + +#include "esp_system.h" + +#include "py/mpconfig.h" +#include "py/obj.h" +#include "py/objstr.h" +#include "py/runtime.h" +#include "py/mperrno.h" +#include "extmod/vfs.h" +#include "extmod/vfs_fat.h" +#include "genhdr/mpversion.h" + +extern const mp_obj_type_t mp_fat_vfs_type; + +STATIC const qstr os_uname_info_fields[] = { + MP_QSTR_sysname, MP_QSTR_nodename, + MP_QSTR_release, MP_QSTR_version, MP_QSTR_machine +}; +STATIC const MP_DEFINE_STR_OBJ(os_uname_info_sysname_obj, MICROPY_PY_SYS_PLATFORM); +STATIC const MP_DEFINE_STR_OBJ(os_uname_info_nodename_obj, MICROPY_PY_SYS_PLATFORM); +STATIC const MP_DEFINE_STR_OBJ(os_uname_info_release_obj, MICROPY_VERSION_STRING); +STATIC const MP_DEFINE_STR_OBJ(os_uname_info_version_obj, MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE); +STATIC const MP_DEFINE_STR_OBJ(os_uname_info_machine_obj, MICROPY_HW_BOARD_NAME " with " MICROPY_HW_MCU_NAME); + +STATIC MP_DEFINE_ATTRTUPLE( + os_uname_info_obj, + os_uname_info_fields, + 5, + (mp_obj_t)&os_uname_info_sysname_obj, + (mp_obj_t)&os_uname_info_nodename_obj, + (mp_obj_t)&os_uname_info_release_obj, + (mp_obj_t)&os_uname_info_version_obj, + (mp_obj_t)&os_uname_info_machine_obj +); + +STATIC mp_obj_t os_uname(void) { + return (mp_obj_t)&os_uname_info_obj; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(os_uname_obj, os_uname); + +STATIC mp_obj_t os_urandom(mp_obj_t num) { + mp_int_t n = mp_obj_get_int(num); + vstr_t vstr; + vstr_init_len(&vstr, n); + uint32_t r = 0; + for (int i = 0; i < n; i++) { + if ((i & 3) == 0) { + r = esp_random(); // returns 32-bit hardware random number + } + vstr.buf[i] = r; + r >>= 8; + } + return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_urandom_obj, os_urandom); + +#if MICROPY_PY_OS_DUPTERM +STATIC mp_obj_t os_dupterm_notify(mp_obj_t obj_in) { + (void)obj_in; + mp_hal_signal_dupterm_input(); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(os_dupterm_notify_obj, os_dupterm_notify); +#endif + +STATIC const mp_rom_map_elem_t os_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uos) }, + { MP_ROM_QSTR(MP_QSTR_uname), MP_ROM_PTR(&os_uname_obj) }, + { MP_ROM_QSTR(MP_QSTR_urandom), MP_ROM_PTR(&os_urandom_obj) }, + #if MICROPY_PY_OS_DUPTERM + { MP_ROM_QSTR(MP_QSTR_dupterm), MP_ROM_PTR(&mp_uos_dupterm_obj) }, + { MP_ROM_QSTR(MP_QSTR_dupterm_notify), MP_ROM_PTR(&os_dupterm_notify_obj) }, + #endif + #if MICROPY_VFS + { MP_ROM_QSTR(MP_QSTR_ilistdir), MP_ROM_PTR(&mp_vfs_ilistdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_listdir), MP_ROM_PTR(&mp_vfs_listdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_mkdir), MP_ROM_PTR(&mp_vfs_mkdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_rmdir), MP_ROM_PTR(&mp_vfs_rmdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_chdir), MP_ROM_PTR(&mp_vfs_chdir_obj) }, + { MP_ROM_QSTR(MP_QSTR_getcwd), MP_ROM_PTR(&mp_vfs_getcwd_obj) }, + { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&mp_vfs_remove_obj) }, + { MP_ROM_QSTR(MP_QSTR_rename), MP_ROM_PTR(&mp_vfs_rename_obj) }, + { MP_ROM_QSTR(MP_QSTR_stat), MP_ROM_PTR(&mp_vfs_stat_obj) }, + { MP_ROM_QSTR(MP_QSTR_statvfs), MP_ROM_PTR(&mp_vfs_statvfs_obj) }, + { MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&mp_vfs_mount_obj) }, + { MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&mp_vfs_umount_obj) }, + #if MICROPY_VFS_FAT + { MP_ROM_QSTR(MP_QSTR_VfsFat), MP_ROM_PTR(&mp_fat_vfs_type) }, + #endif + #endif +}; + +STATIC MP_DEFINE_CONST_DICT(os_module_globals, os_module_globals_table); + +const mp_obj_module_t uos_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&os_module_globals, +}; diff --git a/ports/esp32/modutime.c b/ports/esp32/modutime.c new file mode 100644 index 0000000000..f037faad95 --- /dev/null +++ b/ports/esp32/modutime.c @@ -0,0 +1,61 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * Development of the code in this file was sponsored by Microbric Pty Ltd + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Damien P. George + * + * 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 +#include +#include + +#include "extmod/utime_mphal.h" + +STATIC mp_obj_t time_time(void) { + struct timeval tv; + gettimeofday(&tv, NULL); + return mp_obj_new_int(tv.tv_sec); +} +MP_DEFINE_CONST_FUN_OBJ_0(time_time_obj, time_time); + +STATIC const mp_rom_map_elem_t time_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_utime) }, + + { MP_ROM_QSTR(MP_QSTR_time), MP_ROM_PTR(&time_time_obj) }, + { MP_ROM_QSTR(MP_QSTR_sleep), MP_ROM_PTR(&mp_utime_sleep_obj) }, + { MP_ROM_QSTR(MP_QSTR_sleep_ms), MP_ROM_PTR(&mp_utime_sleep_ms_obj) }, + { MP_ROM_QSTR(MP_QSTR_sleep_us), MP_ROM_PTR(&mp_utime_sleep_us_obj) }, + { MP_ROM_QSTR(MP_QSTR_ticks_ms), MP_ROM_PTR(&mp_utime_ticks_ms_obj) }, + { MP_ROM_QSTR(MP_QSTR_ticks_us), MP_ROM_PTR(&mp_utime_ticks_us_obj) }, + { MP_ROM_QSTR(MP_QSTR_ticks_cpu), MP_ROM_PTR(&mp_utime_ticks_cpu_obj) }, + { MP_ROM_QSTR(MP_QSTR_ticks_add), MP_ROM_PTR(&mp_utime_ticks_add_obj) }, + { MP_ROM_QSTR(MP_QSTR_ticks_diff), MP_ROM_PTR(&mp_utime_ticks_diff_obj) }, +}; + +STATIC MP_DEFINE_CONST_DICT(time_module_globals, time_module_globals_table); + +const mp_obj_module_t utime_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&time_module_globals, +}; diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h new file mode 100644 index 0000000000..2c32593244 --- /dev/null +++ b/ports/esp32/mpconfigport.h @@ -0,0 +1,248 @@ +// Options to control how MicroPython is built for this port, +// overriding defaults in py/mpconfig.h. + +#include +#include +#include "rom/ets_sys.h" + +// object representation and NLR handling +#define MICROPY_OBJ_REPR (MICROPY_OBJ_REPR_A) +#define MICROPY_NLR_SETJMP (1) + +// memory allocation policies +#define MICROPY_ALLOC_PATH_MAX (128) + +// emitters +#define MICROPY_PERSISTENT_CODE_LOAD (1) + +// compiler configuration +#define MICROPY_COMP_MODULE_CONST (1) +#define MICROPY_COMP_TRIPLE_TUPLE_ASSIGN (1) + +// optimisations +#define MICROPY_OPT_COMPUTED_GOTO (1) +#define MICROPY_OPT_MPZ_BITWISE (1) + +// Python internal features +#define MICROPY_READER_VFS (1) +#define MICROPY_ENABLE_GC (1) +#define MICROPY_ENABLE_FINALISER (1) +#define MICROPY_STACK_CHECK (1) +#define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1) +#define MICROPY_KBD_EXCEPTION (1) +#define MICROPY_HELPER_REPL (1) +#define MICROPY_REPL_EMACS_KEYS (1) +#define MICROPY_REPL_AUTO_INDENT (1) +#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) +#define MICROPY_ENABLE_SOURCE_LINE (1) +#define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_NORMAL) +#define MICROPY_WARNINGS (1) +#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT) +#define MICROPY_PY_BUILTINS_COMPLEX (1) +#define MICROPY_CPYTHON_COMPAT (1) +#define MICROPY_STREAMS_NON_BLOCK (1) +#define MICROPY_STREAMS_POSIX_API (1) +#define MICROPY_MODULE_BUILTIN_INIT (1) +#define MICROPY_MODULE_WEAK_LINKS (1) +#define MICROPY_MODULE_FROZEN_STR (0) +#define MICROPY_MODULE_FROZEN_MPY (1) +#define MICROPY_QSTR_EXTRA_POOL mp_qstr_frozen_const_pool +#define MICROPY_CAN_OVERRIDE_BUILTINS (1) +#define MICROPY_USE_INTERNAL_ERRNO (1) +#define MICROPY_USE_INTERNAL_PRINTF (0) // ESP32 SDK requires its own printf +#define MICROPY_ENABLE_SCHEDULER (1) +#define MICROPY_SCHEDULER_DEPTH (8) +#define MICROPY_VFS (1) +#define MICROPY_VFS_FAT (1) + +// control over Python builtins +#define MICROPY_PY_FUNCTION_ATTRS (1) +#define MICROPY_PY_STR_BYTES_CMP_WARN (1) +#define MICROPY_PY_BUILTINS_STR_UNICODE (1) +#define MICROPY_PY_BUILTINS_STR_CENTER (1) +#define MICROPY_PY_BUILTINS_STR_PARTITION (1) +#define MICROPY_PY_BUILTINS_STR_SPLITLINES (1) +#define MICROPY_PY_BUILTINS_BYTEARRAY (1) +#define MICROPY_PY_BUILTINS_MEMORYVIEW (1) +#define MICROPY_PY_BUILTINS_SET (1) +#define MICROPY_PY_BUILTINS_SLICE (1) +#define MICROPY_PY_BUILTINS_SLICE_ATTRS (1) +#define MICROPY_PY_BUILTINS_FROZENSET (1) +#define MICROPY_PY_BUILTINS_PROPERTY (1) +#define MICROPY_PY_BUILTINS_RANGE_ATTRS (1) +#define MICROPY_PY_BUILTINS_TIMEOUTERROR (1) +#define MICROPY_PY_ALL_SPECIAL_METHODS (1) +#define MICROPY_PY_BUILTINS_COMPILE (1) +#define MICROPY_PY_BUILTINS_ENUMERATE (1) +#define MICROPY_PY_BUILTINS_EXECFILE (1) +#define MICROPY_PY_BUILTINS_FILTER (1) +#define MICROPY_PY_BUILTINS_REVERSED (1) +#define MICROPY_PY_BUILTINS_NOTIMPLEMENTED (1) +#define MICROPY_PY_BUILTINS_INPUT (1) +#define MICROPY_PY_BUILTINS_MIN_MAX (1) +#define MICROPY_PY_BUILTINS_POW3 (1) +#define MICROPY_PY_BUILTINS_HELP (1) +#define MICROPY_PY_BUILTINS_HELP_TEXT esp32_help_text +#define MICROPY_PY_BUILTINS_HELP_MODULES (1) +#define MICROPY_PY___FILE__ (1) +#define MICROPY_PY_MICROPYTHON_MEM_INFO (1) +#define MICROPY_PY_ARRAY (1) +#define MICROPY_PY_ARRAY_SLICE_ASSIGN (1) +#define MICROPY_PY_ATTRTUPLE (1) +#define MICROPY_PY_COLLECTIONS (1) +#define MICROPY_PY_COLLECTIONS_ORDEREDDICT (1) +#define MICROPY_PY_MATH (1) +#define MICROPY_PY_MATH_SPECIAL_FUNCTIONS (1) +#define MICROPY_PY_CMATH (1) +#define MICROPY_PY_GC (1) +#define MICROPY_PY_IO (1) +#define MICROPY_PY_IO_FILEIO (1) +#define MICROPY_PY_IO_BYTESIO (1) +#define MICROPY_PY_IO_BUFFEREDWRITER (1) +#define MICROPY_PY_STRUCT (1) +#define MICROPY_PY_SYS (1) +#define MICROPY_PY_SYS_MAXSIZE (1) +#define MICROPY_PY_SYS_MODULES (1) +#define MICROPY_PY_SYS_EXIT (1) +#define MICROPY_PY_SYS_STDFILES (1) +#define MICROPY_PY_SYS_STDIO_BUFFER (1) +#define MICROPY_PY_UERRNO (1) +#define MICROPY_PY_USELECT (1) +#define MICROPY_PY_UTIME_MP_HAL (1) +#define MICROPY_PY_THREAD (1) +#define MICROPY_PY_THREAD_GIL (1) +#define MICROPY_PY_THREAD_GIL_VM_DIVISOR (32) + +// extended modules +#define MICROPY_PY_UCTYPES (1) +#define MICROPY_PY_UZLIB (1) +#define MICROPY_PY_UJSON (1) +#define MICROPY_PY_URE (1) +#define MICROPY_PY_UHEAPQ (1) +#define MICROPY_PY_UTIMEQ (1) +#define MICROPY_PY_UHASHLIB (0) // We use the ESP32 version +#define MICROPY_PY_UHASHLIB_SHA1 (MICROPY_PY_USSL && MICROPY_SSL_AXTLS) +#define MICROPY_PY_UBINASCII (1) +#define MICROPY_PY_UBINASCII_CRC32 (1) +#define MICROPY_PY_URANDOM (1) +#define MICROPY_PY_URANDOM_EXTRA_FUNCS (1) +#define MICROPY_PY_MACHINE (1) +#define MICROPY_PY_MACHINE_PIN_MAKE_NEW mp_pin_make_new +#define MICROPY_PY_MACHINE_PULSE (1) +#define MICROPY_PY_MACHINE_I2C (1) +#define MICROPY_PY_MACHINE_SPI (1) +#define MICROPY_PY_MACHINE_SPI_MSB (0) +#define MICROPY_PY_MACHINE_SPI_LSB (1) +#define MICROPY_PY_MACHINE_SPI_MAKE_NEW machine_hw_spi_make_new +#define MICROPY_PY_MACHINE_SPI_MIN_DELAY (0) +#define MICROPY_PY_MACHINE_SPI_MAX_BAUDRATE (ets_get_cpu_frequency() * 1000000 / 200) // roughly +#define MICROPY_PY_USSL (1) +#define MICROPY_SSL_MBEDTLS (1) +#define MICROPY_PY_WEBSOCKET (0) +#define MICROPY_PY_FRAMEBUF (1) + +// fatfs configuration +#define MICROPY_FATFS_ENABLE_LFN (1) +#define MICROPY_FATFS_RPATH (2) +#define MICROPY_FATFS_MAX_SS (4096) +#define MICROPY_FATFS_LFN_CODE_PAGE (437) /* 1=SFN/ANSI 437=LFN/U.S.(OEM) */ +#define mp_type_fileio fatfs_type_fileio +#define mp_type_textio fatfs_type_textio + +// 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 +#define mp_builtin_open_obj mp_vfs_open_obj + +// extra built in names to add to the global namespace +#define MICROPY_PORT_BUILTINS \ + { MP_OBJ_NEW_QSTR(MP_QSTR_input), (mp_obj_t)&mp_builtin_input_obj }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_open), (mp_obj_t)&mp_builtin_open_obj }, + +// extra built in modules to add to the list of known ones +extern const struct _mp_obj_module_t esp_module; +extern const struct _mp_obj_module_t utime_module; +extern const struct _mp_obj_module_t uos_module; +extern const struct _mp_obj_module_t mp_module_usocket; +extern const struct _mp_obj_module_t mp_module_machine; +extern const struct _mp_obj_module_t mp_module_network; +extern const struct _mp_obj_module_t mp_module_onewire; + +#define MICROPY_PORT_BUILTIN_MODULES \ + { MP_OBJ_NEW_QSTR(MP_QSTR_esp), (mp_obj_t)&esp_module }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_utime), (mp_obj_t)&utime_module }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_uos), (mp_obj_t)&uos_module }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_usocket), (mp_obj_t)&mp_module_usocket }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_machine), (mp_obj_t)&mp_module_machine }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_network), (mp_obj_t)&mp_module_network }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR__onewire), (mp_obj_t)&mp_module_onewire }, \ + +#define MICROPY_PORT_BUILTIN_MODULE_WEAK_LINKS \ + { MP_OBJ_NEW_QSTR(MP_QSTR_binascii), (mp_obj_t)&mp_module_ubinascii }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_collections), (mp_obj_t)&mp_module_collections }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_errno), (mp_obj_t)&mp_module_uerrno }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_hashlib), (mp_obj_t)&mp_module_uhashlib }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_heapq), (mp_obj_t)&mp_module_uheapq }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_io), (mp_obj_t)&mp_module_io }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_json), (mp_obj_t)&mp_module_ujson }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_os), (mp_obj_t)&uos_module }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_random), (mp_obj_t)&mp_module_urandom }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_re), (mp_obj_t)&mp_module_ure }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_select), (mp_obj_t)&mp_module_uselect }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_socket), (mp_obj_t)&mp_module_usocket }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_ssl), (mp_obj_t)&mp_module_ussl }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_struct), (mp_obj_t)&mp_module_ustruct }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_time), (mp_obj_t)&utime_module }, \ + { MP_OBJ_NEW_QSTR(MP_QSTR_zlib), (mp_obj_t)&mp_module_uzlib }, \ + +#define MP_STATE_PORT MP_STATE_VM + +#define MICROPY_PORT_ROOT_POINTERS \ + const char *readline_hist[8]; \ + mp_obj_t machine_pin_irq_handler[40]; \ + +// type definitions for the specific machine + +#define BYTES_PER_WORD (4) +#define MICROPY_MAKE_POINTER_CALLABLE(p) ((void*)((mp_uint_t)(p))) +#define MP_PLAT_PRINT_STRN(str, len) mp_hal_stdout_tx_strn_cooked(str, len) +#define MP_SSIZE_MAX (0x7fffffff) + +// Note: these "critical nested" macros do not ensure cross-CPU exclusion, +// the only disable interrupts on the current CPU. To full manage exclusion +// one should use portENTER_CRITICAL/portEXIT_CRITICAL instead. +#include "freertos/FreeRTOS.h" +#define MICROPY_BEGIN_ATOMIC_SECTION() portENTER_CRITICAL_NESTED() +#define MICROPY_END_ATOMIC_SECTION(state) portEXIT_CRITICAL_NESTED(state) + +#if MICROPY_PY_THREAD +#define MICROPY_EVENT_POLL_HOOK \ + do { \ + extern void mp_handle_pending(void); \ + mp_handle_pending(); \ + MP_THREAD_GIL_EXIT(); \ + MP_THREAD_GIL_ENTER(); \ + } while (0); +#else +#define MICROPY_EVENT_POLL_HOOK \ + do { \ + extern void mp_handle_pending(void); \ + mp_handle_pending(); \ + asm("waiti 0"); \ + } while (0); +#endif + +#define UINT_FMT "%u" +#define INT_FMT "%d" + +typedef int32_t mp_int_t; // must be pointer size +typedef uint32_t mp_uint_t; // must be pointer size +typedef long mp_off_t; +// ssize_t, off_t as required by POSIX-signatured functions in stream.h +#include + +// board specifics + +#define MICROPY_HW_BOARD_NAME "ESP32 module" +#define MICROPY_HW_MCU_NAME "ESP32" +#define MICROPY_PY_SYS_PLATFORM "esp32" diff --git a/ports/esp32/mphalport.c b/ports/esp32/mphalport.c new file mode 100644 index 0000000000..e588fc65a1 --- /dev/null +++ b/ports/esp32/mphalport.c @@ -0,0 +1,139 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * Development of the code in this file was sponsored by Microbric Pty Ltd + * + * The MIT License (MIT) + * + * Copyright (c) 2014 Damien P. George + * + * 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 +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "rom/uart.h" + +#include "py/obj.h" +#include "py/mpstate.h" +#include "py/mphal.h" +#include "extmod/misc.h" +#include "lib/utils/pyexec.h" + +STATIC uint8_t stdin_ringbuf_array[256]; +ringbuf_t stdin_ringbuf = {stdin_ringbuf_array, sizeof(stdin_ringbuf_array)}; + +int mp_hal_stdin_rx_chr(void) { + for (;;) { + int c = ringbuf_get(&stdin_ringbuf); + if (c != -1) { + return c; + } + MICROPY_EVENT_POLL_HOOK + vTaskDelay(1); + } +} + +void mp_hal_stdout_tx_char(char c) { + uart_tx_one_char(c); + //mp_uos_dupterm_tx_strn(&c, 1); +} + +void mp_hal_stdout_tx_str(const char *str) { + MP_THREAD_GIL_EXIT(); + while (*str) { + mp_hal_stdout_tx_char(*str++); + } + MP_THREAD_GIL_ENTER(); +} + +void mp_hal_stdout_tx_strn(const char *str, uint32_t len) { + MP_THREAD_GIL_EXIT(); + while (len--) { + mp_hal_stdout_tx_char(*str++); + } + MP_THREAD_GIL_ENTER(); +} + +void mp_hal_stdout_tx_strn_cooked(const char *str, uint32_t len) { + MP_THREAD_GIL_EXIT(); + while (len--) { + if (*str == '\n') { + mp_hal_stdout_tx_char('\r'); + } + mp_hal_stdout_tx_char(*str++); + } + MP_THREAD_GIL_ENTER(); +} + +uint32_t mp_hal_ticks_ms(void) { + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec * 1000 + tv.tv_usec / 1000; +} + +uint32_t mp_hal_ticks_us(void) { + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec * 1000000 + tv.tv_usec; +} + +void mp_hal_delay_ms(uint32_t ms) { + struct timeval tv_start; + struct timeval tv_end; + uint64_t dt; + gettimeofday(&tv_start, NULL); + for (;;) { + gettimeofday(&tv_end, NULL); + dt = (tv_end.tv_sec - tv_start.tv_sec) * 1000 + (tv_end.tv_usec - tv_start.tv_usec) / 1000; + if (dt + portTICK_PERIOD_MS >= ms) { + // doing a vTaskDelay would take us beyound requested delay time + break; + } + MICROPY_EVENT_POLL_HOOK + vTaskDelay(1); + } + if (dt < ms) { + // do the remaining delay accurately + ets_delay_us((ms - dt) * 1000); + } +} + +void mp_hal_delay_us(uint32_t us) { + ets_delay_us(us); +} + +// this function could do with improvements (eg use ets_delay_us) +void mp_hal_delay_us_fast(uint32_t us) { + uint32_t delay = ets_get_cpu_frequency() / 19; + while (--us) { + for (volatile uint32_t i = delay; i; --i) { + } + } +} + +/* +extern int mp_stream_errno; +int *__errno() { + return &mp_stream_errno; +} +*/ diff --git a/ports/esp32/mphalport.h b/ports/esp32/mphalport.h new file mode 100644 index 0000000000..8a0f1dc8b0 --- /dev/null +++ b/ports/esp32/mphalport.h @@ -0,0 +1,83 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * Development of the code in this file was sponsored by Microbric Pty Ltd + * + * The MIT License (MIT) + * + * Copyright (c) 2014 Damien P. George + * + * 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 INCLUDED_MPHALPORT_H +#define INCLUDED_MPHALPORT_H + +#include "py/ringbuf.h" +#include "lib/utils/interrupt_char.h" + +extern ringbuf_t stdin_ringbuf; + +uint32_t mp_hal_ticks_us(void); +__attribute__((always_inline)) static inline uint32_t mp_hal_ticks_cpu(void) { + uint32_t ccount; + __asm__ __volatile__("rsr %0,ccount":"=a" (ccount)); + return ccount; +} + +void mp_hal_delay_us(uint32_t); +void mp_hal_delay_us_fast(uint32_t); +void mp_hal_set_interrupt_char(int c); +uint32_t mp_hal_get_cpu_freq(void); + +#define mp_hal_quiet_timing_enter() MICROPY_BEGIN_ATOMIC_SECTION() +#define mp_hal_quiet_timing_exit(irq_state) MICROPY_END_ATOMIC_SECTION(irq_state) + +// C-level pin HAL +#include "py/obj.h" +#include "driver/gpio.h" +#define MP_HAL_PIN_FMT "%u" +#define mp_hal_pin_obj_t gpio_num_t +mp_hal_pin_obj_t machine_pin_get_id(mp_obj_t pin_in); +#define mp_hal_get_pin_obj(o) machine_pin_get_id(o) +#define mp_obj_get_pin(o) machine_pin_get_id(o) // legacy name; only to support esp8266/modonewire +#define mp_hal_pin_name(p) (p) +static inline void mp_hal_pin_input(mp_hal_pin_obj_t pin) { + gpio_set_direction(pin, GPIO_MODE_INPUT); +} +static inline void mp_hal_pin_output(mp_hal_pin_obj_t pin) { + gpio_set_direction(pin, GPIO_MODE_INPUT_OUTPUT); +} +static inline void mp_hal_pin_open_drain(mp_hal_pin_obj_t pin) { + gpio_set_direction(pin, GPIO_MODE_INPUT_OUTPUT_OD); +} +static inline void mp_hal_pin_od_low(mp_hal_pin_obj_t pin) { + gpio_set_level(pin, 0); +} +static inline void mp_hal_pin_od_high(mp_hal_pin_obj_t pin) { + gpio_set_level(pin, 1); +} +static inline int mp_hal_pin_read(mp_hal_pin_obj_t pin) { + return gpio_get_level(pin); +} +static inline void mp_hal_pin_write(mp_hal_pin_obj_t pin, int v) { + gpio_set_level(pin, v); +} + +#endif // INCLUDED_MPHALPORT_H diff --git a/ports/esp32/mpthreadport.c b/ports/esp32/mpthreadport.c new file mode 100644 index 0000000000..76d9431c03 --- /dev/null +++ b/ports/esp32/mpthreadport.c @@ -0,0 +1,226 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd + * Copyright (c) 2017 Pycom Limited + * + * 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 "stdio.h" + +#include "py/mpconfig.h" +#include "py/mpstate.h" +#include "py/gc.h" +#include "py/mpthread.h" +#include "mpthreadport.h" + +#include "esp_task.h" + +#if MICROPY_PY_THREAD + +#define MP_THREAD_MIN_STACK_SIZE (4 * 1024) +#define MP_THREAD_DEFAULT_STACK_SIZE (MP_THREAD_MIN_STACK_SIZE + 1024) +#define MP_THREAD_PRIORITY (ESP_TASK_PRIO_MIN + 1) + +// this structure forms a linked list, one node per active thread +typedef struct _thread_t { + TaskHandle_t id; // system id of thread + int ready; // whether the thread is ready and running + void *arg; // thread Python args, a GC root pointer + void *stack; // pointer to the stack + StaticTask_t *tcb; // pointer to the Task Control Block + size_t stack_len; // number of words in the stack + struct _thread_t *next; +} thread_t; + +// the mutex controls access to the linked list +STATIC mp_thread_mutex_t thread_mutex; +STATIC thread_t thread_entry0; +STATIC thread_t *thread; // root pointer, handled by mp_thread_gc_others + +void mp_thread_init(void *stack, uint32_t stack_len) { + mp_thread_set_state(&mp_state_ctx.thread); + // create the first entry in the linked list of all threads + thread = &thread_entry0; + thread->id = xTaskGetCurrentTaskHandle(); + thread->ready = 1; + thread->arg = NULL; + thread->stack = stack; + thread->stack_len = stack_len; + thread->next = NULL; + mp_thread_mutex_init(&thread_mutex); +} + +void mp_thread_gc_others(void) { + mp_thread_mutex_lock(&thread_mutex, 1); + for (thread_t *th = thread; th != NULL; th = th->next) { + gc_collect_root((void**)&th, 1); + gc_collect_root(&th->arg, 1); // probably not needed + if (th->id == xTaskGetCurrentTaskHandle()) { + continue; + } + if (!th->ready) { + continue; + } + gc_collect_root(th->stack, th->stack_len); // probably not needed + } + mp_thread_mutex_unlock(&thread_mutex); +} + +mp_state_thread_t *mp_thread_get_state(void) { + return pvTaskGetThreadLocalStoragePointer(NULL, 1); +} + +void mp_thread_set_state(void *state) { + vTaskSetThreadLocalStoragePointer(NULL, 1, state); +} + +void mp_thread_start(void) { + mp_thread_mutex_lock(&thread_mutex, 1); + for (thread_t *th = thread; th != NULL; th = th->next) { + if (th->id == xTaskGetCurrentTaskHandle()) { + th->ready = 1; + break; + } + } + mp_thread_mutex_unlock(&thread_mutex); +} + +STATIC void *(*ext_thread_entry)(void*) = NULL; + +STATIC void freertos_entry(void *arg) { + if (ext_thread_entry) { + ext_thread_entry(arg); + } + vTaskDelete(NULL); + for (;;); +} + +void mp_thread_create_ex(void *(*entry)(void*), void *arg, size_t *stack_size, int priority, char *name) { + // store thread entry function into a global variable so we can access it + ext_thread_entry = entry; + + if (*stack_size == 0) { + *stack_size = MP_THREAD_DEFAULT_STACK_SIZE; // default stack size + } else if (*stack_size < MP_THREAD_MIN_STACK_SIZE) { + *stack_size = MP_THREAD_MIN_STACK_SIZE; // minimum stack size + } + + // allocate TCB, stack and linked-list node (must be outside thread_mutex lock) + StaticTask_t *tcb = m_new(StaticTask_t, 1); + StackType_t *stack = m_new(StackType_t, *stack_size / sizeof(StackType_t)); + thread_t *th = m_new_obj(thread_t); + + mp_thread_mutex_lock(&thread_mutex, 1); + + // create thread + TaskHandle_t id = xTaskCreateStaticPinnedToCore(freertos_entry, name, *stack_size / sizeof(StackType_t), arg, priority, stack, tcb, 0); + if (id == NULL) { + mp_thread_mutex_unlock(&thread_mutex); + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "can't create thread")); + } + + // adjust the stack_size to provide room to recover from hitting the limit + *stack_size -= 1024; + + // add thread to linked list of all threads + th->id = id; + th->ready = 0; + th->arg = arg; + th->stack = stack; + th->tcb = tcb; + th->stack_len = *stack_size / sizeof(StackType_t); + th->next = thread; + thread = th; + + mp_thread_mutex_unlock(&thread_mutex); +} + +void mp_thread_create(void *(*entry)(void*), void *arg, size_t *stack_size) { + mp_thread_create_ex(entry, arg, stack_size, MP_THREAD_PRIORITY, "mp_thread"); +} + +void mp_thread_finish(void) { + mp_thread_mutex_lock(&thread_mutex, 1); + for (thread_t *th = thread; th != NULL; th = th->next) { + if (th->id == xTaskGetCurrentTaskHandle()) { + th->ready = 0; + break; + } + } + mp_thread_mutex_unlock(&thread_mutex); +} + +void vPortCleanUpTCB(void *tcb) { + thread_t *prev = NULL; + mp_thread_mutex_lock(&thread_mutex, 1); + for (thread_t *th = thread; th != NULL; prev = th, th = th->next) { + // unlink the node from the list + if (th->tcb == tcb) { + if (prev != NULL) { + prev->next = th->next; + } else { + // move the start pointer + thread = th->next; + } + // explicitly release all its memory + m_del(StaticTask_t, th->tcb, 1); + m_del(StackType_t, th->stack, th->stack_len); + m_del(thread_t, th, 1); + break; + } + } + mp_thread_mutex_unlock(&thread_mutex); +} + +void mp_thread_mutex_init(mp_thread_mutex_t *mutex) { + mutex->handle = xSemaphoreCreateMutexStatic(&mutex->buffer); +} + +int mp_thread_mutex_lock(mp_thread_mutex_t *mutex, int wait) { + return (pdTRUE == xSemaphoreTake(mutex->handle, wait ? portMAX_DELAY : 0)); +} + +void mp_thread_mutex_unlock(mp_thread_mutex_t *mutex) { + xSemaphoreGive(mutex->handle); +} + +void mp_thread_deinit(void) { + mp_thread_mutex_lock(&thread_mutex, 1); + for (thread_t *th = thread; th != NULL; th = th->next) { + // don't delete the current task + if (th->id == xTaskGetCurrentTaskHandle()) { + continue; + } + vTaskDelete(th->id); + } + mp_thread_mutex_unlock(&thread_mutex); + // allow FreeRTOS to clean-up the threads + vTaskDelay(2); +} + +#else + +void vPortCleanUpTCB(void *tcb) { +} + +#endif // MICROPY_PY_THREAD diff --git a/ports/esp32/mpthreadport.h b/ports/esp32/mpthreadport.h new file mode 100644 index 0000000000..54e35d61c3 --- /dev/null +++ b/ports/esp32/mpthreadport.h @@ -0,0 +1,45 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd + * Copyright (c) 2017 Pycom Limited + * + * 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_ESP32_MPTHREADPORT_H +#define MICROPY_INCLUDED_ESP32_MPTHREADPORT_H + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/queue.h" + +typedef struct _mp_thread_mutex_t { + SemaphoreHandle_t handle; + StaticSemaphore_t buffer; +} mp_thread_mutex_t; + +void mp_thread_init(void *stack, uint32_t stack_len); +void mp_thread_gc_others(void); +void mp_thread_deinit(void); + +#endif // MICROPY_INCLUDED_ESP32_MPTHREADPORT_H diff --git a/ports/esp32/qstrdefsport.h b/ports/esp32/qstrdefsport.h new file mode 100644 index 0000000000..ff6c2cc426 --- /dev/null +++ b/ports/esp32/qstrdefsport.h @@ -0,0 +1,30 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014 Damien P. George + * + * 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. + */ + +// qstrs specific to this port, only needed if they aren't auto-generated + +// Entries for sys.path +Q(/lib) diff --git a/ports/esp32/sdkconfig.h b/ports/esp32/sdkconfig.h new file mode 100644 index 0000000000..ce254c0e6d --- /dev/null +++ b/ports/esp32/sdkconfig.h @@ -0,0 +1,162 @@ +#define CONFIG_TRACEMEM_RESERVE_DRAM 0x0 +#define CONFIG_BT_RESERVE_DRAM 0x0 +#define CONFIG_ULP_COPROC_RESERVE_MEM 0 +#define CONFIG_PHY_DATA_OFFSET 0xf000 +#define CONFIG_APP_OFFSET 0x10000 + +#define CONFIG_SPI_FLASH_ROM_DRIVER_PATCH 1 +#define CONFIG_FOUR_UNIVERSAL_MAC_ADDRESS 1 +#define CONFIG_NUMBER_OF_UNIVERSAL_MAC_ADDRESS 4 + +#define CONFIG_TCPIP_TASK_STACK_SIZE 2560 + +#define CONFIG_ESP32_APPTRACE_DEST_NONE 1 +#define CONFIG_ESP32_PHY_MAX_TX_POWER 20 +#define CONFIG_ESP32_PANIC_PRINT_REBOOT 1 +#define CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC 1 +#define CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1 1 +#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_ENABLE_COREDUMP_TO_NONE 1 +#define CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE 1 +#define CONFIG_ESP32_WIFI_AMPDU_ENABLED 1 +#define CONFIG_ESP32_WIFI_NVS_ENABLED 1 +#define CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM 10 +#define CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM 0 +#define CONFIG_ESP32_WIFI_TX_BUFFER_TYPE 1 +#define CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER 1 +#define CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM 32 +#define CONFIG_ESP32_WIFI_RX_BA_WIN 6 +#define CONFIG_ESP32_WIFI_TX_BA_WIN 6 +#define CONFIG_ESP32_XTAL_FREQ_AUTO 1 +#define CONFIG_ESP32_XTAL_FREQ 0 +#define CONFIG_ESP32_RTC_CLK_CAL_CYCLES 1024 +#define CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT 5 +#define CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT 2048 + +#define CONFIG_FOUR_MAC_ADDRESS_FROM_EFUSE 1 +#define CONFIG_DMA_RX_BUF_NUM 10 +#define CONFIG_DMA_TX_BUF_NUM 10 +#define CONFIG_EMAC_TASK_PRIORITY 20 + +#define CONFIG_INT_WDT 1 +#define CONFIG_INT_WDT_TIMEOUT_MS 300 +#define CONFIG_INT_WDT_CHECK_CPU1 0 +#define CONFIG_TASK_WDT 1 +#define CONFIG_TASK_WDT_TIMEOUT_S 5 +#define CONFIG_TASK_WDT_CHECK_IDLE_TASK 0 +#define CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1 0 + +#define CONFIG_FREERTOS_UNICORE 1 +#define CONFIG_FREERTOS_CORETIMER_0 1 +#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_THREAD_LOCAL_STORAGE_POINTERS 2 +#define CONFIG_FREERTOS_IDLE_TASK_STACKSIZE 1024 +#define CONFIG_FREERTOS_ISR_STACKSIZE 1536 +#define CONFIG_FREERTOS_BREAK_ON_SCHEDULER_START_JTAG 1 +#define CONFIG_FREERTOS_MAX_TASK_NAME_LEN 16 +#define CONFIG_SUPPORT_STATIC_ALLOCATION 1 +#define CONFIG_ENABLE_STATIC_TASK_CLEAN_UP_HOOK 1 + +#define CONFIG_MAIN_TASK_STACK_SIZE 4096 +#define CONFIG_IPC_TASK_STACK_SIZE 1024 +#define CONFIG_BTC_TASK_STACK_SIZE 3072 +#define CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE 4096 +#define CONFIG_SYSTEM_EVENT_QUEUE_SIZE 32 +#define CONFIG_TIMER_TASK_STACK_SIZE 4096 +#define CONFIG_TIMER_TASK_PRIORITY 1 +#define CONFIG_TIMER_TASK_STACK_DEPTH 2048 +#define CONFIG_TIMER_QUEUE_LENGTH 10 + +#define CONFIG_NEWLIB_STDIN_LINE_ENDING_CR 1 +#define CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF 1 +#define CONFIG_PHY_ENABLED 1 +#define CONFIG_WIFI_ENABLED 1 +#define CONFIG_OPTIMIZATION_LEVEL_DEBUG 1 +#define CONFIG_MEMMAP_SMP 1 + +#define CONFIG_PARTITION_TABLE_SINGLE_APP 1 +#define CONFIG_PARTITION_TABLE_FILENAME "partitions_singleapp.csv" +#define CONFIG_PARTITION_TABLE_CUSTOM_APP_BIN_OFFSET 0x10000 +#define CONFIG_PARTITION_TABLE_CUSTOM_FILENAME "partitions.csv" + +#define CONFIG_CONSOLE_UART_BAUDRATE 115200 +#define CONFIG_CONSOLE_UART_NUM 0 +#define CONFIG_CONSOLE_UART_DEFAULT 1 + +#define CONFIG_LOG_DEFAULT_LEVEL_INFO 1 +#define CONFIG_LOG_BOOTLOADER_LEVEL_WARN 1 +#define CONFIG_LOG_DEFAULT_LEVEL 3 +#define CONFIG_LOG_COLORS 1 +#define CONFIG_LOG_BOOTLOADER_LEVEL 2 + +#define CONFIG_LWIP_THREAD_LOCAL_STORAGE_INDEX 0 +#define CONFIG_LWIP_DHCP_DOES_ARP_CHECK 1 +#define CONFIG_LWIP_DHCP_MAX_NTP_SERVERS 1 +#define CONFIG_LWIP_MAX_SOCKETS 8 +#define CONFIG_LWIP_SO_REUSE 1 +#define CONFIG_IP_LOST_TIMER_INTERVAL 120 +#define CONFIG_UDP_RECVMBOX_SIZE 6 +#define CONFIG_TCP_MAXRTX 12 +#define CONFIG_TCP_SYNMAXRTX 6 +#define CONFIG_TCP_MSL 60000 +#define CONFIG_TCP_MSS 1436 +#define CONFIG_TCP_SND_BUF_DEFAULT 5744 +#define CONFIG_TCP_WND_DEFAULT 5744 +#define CONFIG_TCP_QUEUE_OOSEQ 1 +#define CONFIG_TCP_OVERSIZE_MSS 1 +#define CONFIG_TCP_RECVMBOX_SIZE 6 + +#define CONFIG_MBEDTLS_AES_C 1 +#define CONFIG_MBEDTLS_CCM_C 1 +#define CONFIG_MBEDTLS_ECDH_C 1 +#define CONFIG_MBEDTLS_ECDSA_C 1 +#define CONFIG_MBEDTLS_ECP_C 1 +#define CONFIG_MBEDTLS_ECP_DP_BP256R1_ENABLED 1 +#define CONFIG_MBEDTLS_ECP_DP_BP384R1_ENABLED 1 +#define CONFIG_MBEDTLS_ECP_DP_BP512R1_ENABLED 1 +#define CONFIG_MBEDTLS_ECP_DP_CURVE25519_ENABLED 1 +#define CONFIG_MBEDTLS_ECP_DP_SECP192K1_ENABLED 1 +#define CONFIG_MBEDTLS_ECP_DP_SECP192R1_ENABLED 1 +#define CONFIG_MBEDTLS_ECP_DP_SECP224K1_ENABLED 1 +#define CONFIG_MBEDTLS_ECP_DP_SECP224R1_ENABLED 1 +#define CONFIG_MBEDTLS_ECP_DP_SECP256K1_ENABLED 1 +#define CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED 1 +#define CONFIG_MBEDTLS_ECP_DP_SECP384R1_ENABLED 1 +#define CONFIG_MBEDTLS_ECP_DP_SECP521R1_ENABLED 1 +#define CONFIG_MBEDTLS_ECP_NIST_OPTIM 1 +#define CONFIG_MBEDTLS_GCM_C 1 +#define CONFIG_MBEDTLS_HARDWARE_AES 1 +#define CONFIG_MBEDTLS_HAVE_TIME 1 +#define CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_RSA 1 +#define CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA 1 +#define CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA 1 +#define CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_RSA 1 +#define CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_RSA 1 +#define CONFIG_MBEDTLS_KEY_EXCHANGE_ELLIPTIC_CURVE 1 +#define CONFIG_MBEDTLS_KEY_EXCHANGE_RSA 1 +#define CONFIG_MBEDTLS_PEM_PARSE_C 1 +#define CONFIG_MBEDTLS_PEM_WRITE_C 1 +#define CONFIG_MBEDTLS_RC4_DISABLED 1 +#define CONFIG_MBEDTLS_SSL_ALPN 1 +#define CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN 16384 +#define CONFIG_MBEDTLS_SSL_PROTO_TLS1 1 +#define CONFIG_MBEDTLS_SSL_PROTO_TLS1_1 1 +#define CONFIG_MBEDTLS_SSL_PROTO_TLS1_2 1 +#define CONFIG_MBEDTLS_SSL_RENEGOTIATION 1 +#define CONFIG_MBEDTLS_SSL_SESSION_TICKETS 1 +#define CONFIG_MBEDTLS_TLS_CLIENT 1 +#define CONFIG_MBEDTLS_TLS_ENABLED 1 +#define CONFIG_MBEDTLS_TLS_SERVER 1 +#define CONFIG_MBEDTLS_TLS_SERVER_AND_CLIENT 1 +#define CONFIG_MBEDTLS_X509_CRL_PARSE_C 1 +#define CONFIG_MBEDTLS_X509_CSR_PARSE_C 1 + +#define CONFIG_MAKE_WARN_UNDEFINED_VARIABLES 1 +#define CONFIG_TOOLPREFIX "xtensa-esp32-elf-" +#define CONFIG_PYTHON "python2" diff --git a/ports/esp32/uart.c b/ports/esp32/uart.c new file mode 100644 index 0000000000..10a4ba462e --- /dev/null +++ b/ports/esp32/uart.c @@ -0,0 +1,65 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * Development of the code in this file was sponsored by Microbric Pty Ltd + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Damien P. George + * + * 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 + +#include "driver/uart.h" + +#include "py/mpstate.h" +#include "py/mphal.h" + +STATIC void uart_irq_handler(void *arg); + +void uart_init(void) { + uart_isr_handle_t handle; + uart_isr_register(UART_NUM_0, uart_irq_handler, NULL, ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM, &handle); + uart_enable_rx_intr(UART_NUM_0); +} + +// all code executed in ISR must be in IRAM, and any const data must be in DRAM +STATIC void IRAM_ATTR uart_irq_handler(void *arg) { + volatile uart_dev_t *uart = &UART0; + uart->int_clr.rxfifo_full = 1; + uart->int_clr.frm_err = 1; + uart->int_clr.rxfifo_tout = 1; + while (uart->status.rxfifo_cnt) { + uint8_t c = uart->fifo.rw_byte; + if (c == mp_interrupt_char) { + // inline version of mp_keyboard_interrupt(); + MP_STATE_VM(mp_pending_exception) = MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_kbd_exception)); + #if MICROPY_ENABLE_SCHEDULER + if (MP_STATE_VM(sched_state) == MP_SCHED_IDLE) { + MP_STATE_VM(sched_state) = MP_SCHED_PENDING; + } + #endif + } else { + // this is an inline function so will be in IRAM + ringbuf_put(&stdin_ringbuf, c); + } + } +} diff --git a/ports/esp32/uart.h b/ports/esp32/uart.h new file mode 100644 index 0000000000..264c8b8949 --- /dev/null +++ b/ports/esp32/uart.h @@ -0,0 +1,33 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * Development of the code in this file was sponsored by Microbric Pty Ltd + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Damien P. George + * + * 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_ESP32_UART_H +#define MICROPY_INCLUDED_ESP32_UART_H + +void uart_init(void); + +#endif // MICROPY_INCLUDED_ESP32_UART_H