From bc08c884a2e7d1d3a4476c224f230b90ee2bf1e3 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 6 Dec 2016 12:20:10 +1100 Subject: [PATCH] esp32: Add new port to Espressif ESP32 SoC. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit is a combination of 216 commits from the initial stages of development of this port, up to and including the point where the code was moved to the ports/esp32 directory. These commits were mostly concerned with setting up the build system and getting a reliable port working with basic features. The following is a digest of the original commits in their original order (most recent listed first), grouped where possible per author. The list is here to give credit for the work and provide some level of traceability and accountability. For the full history of development please consult the original repository. All code is MIT licensed and the relevant copyright holders are listed in the comment-header of each file. Damien George esp32: Update to latest ESP IDF. esp32: Update module symlinks now that code lives under the ports dir. esp32: Update to compile with new ports/esp32 directory structure. esp32: Move it to the ports/ directory. esp32/machine_uart: Don't save baudrate but compute it instead. esp32/modsocket: Add socket.readinto() method. esp32/modesp: Add esp.gpio_matrix_in and esp.gpio_matrix_out functions. esp32/machine_uart: Wait for all data to be tx'd before changing config. NyxCode esp32: Add note to README.md about updating the submodules of ESP IDF. Anthony Briggs esp32: Update README.md installation and flashing instructions. Javier Candeira esp32: Raise error when setting input-only pin to output. With help from Adrian Smith (fon@thefon.net) Javier Candeira esp32: Replace exception raising with corresponding mp_raise_XXX funcs. Tisham Dhar esp32: Add some specific notes about building on Windows using WSL. Ben Gamari esp32: Provide machine.Signal class. Damien George esp32/modnetwork: Implement AP version of network.isconnected(). Eric Poulsen esp32/README.md: Add note about btree submodule initialization. Damien George esp32: Make firmware.bin start at 0x1000 to allow flash size autodetect. esp32: Changes to follow latest version of upstream uPy. esp32/Makefile: Separate ESP-specific inc dirs to avoid header clashes. esp32: Enable "btree" database module. esp32: Update to latest ESP IDF. Roosted7 esp32: Update to latest ESP-IDF. Alex King esp32/machine_timer: Add support for esp32 hardware timer. Code lineage: Timer() is based loosely on the version in esp8266, although the implementation is differs significantly because of the change in the underlying platform. Damien George esp32/machine_uart: Increase UART TX buffer size to 64. esp32/modules: Update dht symlink. esp32/mpconfigport.h: Enable utimeq module, needed for uasyncio. esp32: Changes to follow latest version of upstream uPy. esp32: Update to latest ESP-IDF. esp32/machine_uart: Add uart.any() method. esp32/machine_uart: Uninstall the UART driver before installing it. Thomas Roos esp32: Update to latest ESP-IDF. Eric Poulsen esp32/modsocket: Make read/write return None when in non-blocking mode. esp32/modsocket.c: Fix send/sendto/write for non-blocking sockets. Odd Stråbø esp32: Initial working implementation of machine.UART. Code lineage (as seen by previous commits): I copied the ESP8266 code, renamed pyb -> machine, and used esp-idf as a reference while implementing minimal functionality. I provide all of my changes under the MIT license. Odd Stråbø esp32/machine_uart: Rename pyb to machine. esp32: Copy machine_uart.c from esp8266 port. Damien George esp32/moduos: Add uos.ilistdir() function. esp32: Mount filesystem at the root of the VFS, following esp8266. Andy Valencia esp32: Add hardware SHA1/SHA256 support via mbedtls API. Code lineage: a copy of extmod/moduhashlib with the API invocation details edited. Total derivative work. Andy Valencia esp32: Add PWM support via machine.PWM class. Code lineage: I started by copying the esp8266 machine_pwm.c. I used information from the ESP32 Technical Reference Manual, the esp-idf documentation, and the SDK's sample ledc example code (but I did not copy that code, just studied it to understand the SDK's API for PWM). So aside from the code copied from the esp8266 PWM support, everything else you see is just new code I wrote. I wasn't an employee of anybody when I wrote it, and I wrote it with the understanding and intention that it's simply a derivative work of the existing micropython code. I freely and willingly contribute it to the project and intend that it not change the legal status of the micropython code base in any way, even if it is included in that base in whole or part. Damien George esp32/modules: Add symlinks for upysh and upip. Eric Poulsen esp32/modmachine: Add unique_id() function to machine module. Damien George esp32: Change dac_out_voltage to dac_output_voltage for new IDF API. esp32: Update esp32.custom_common.ld to align with changes in ESP IDF. Eric Poulsen esp32: Update to latest ESP IDF. Damien George esp32/modsocket: When resolving IP addr handle the case of host=''. esp32: Update to latest ESP IDF. Eric Poulsen esp32/Makefile: Change default FLASH_MODE to dio for WROOM-32 module. esp32: Move FAT FS to start at 0x200000 and increase size to 2MiB. Damien George esp32: Remove enable_irq/disable_irq and use ATOMIC_SECTION instead. esp32/mpconfigport.h: Provide ATOMIC_SECTION macros. esp32/main: Restart the MCU if there is a failed NLR jump. Daniel Campora esp32: Enable threading; be sure to exit GIL when a thread will block. esp32: Trace the registers when doing a gc collect. Also make it thread ready. esp32: Add threading implementation, disabled for the time being. Damien George esp32: Update to latest ESP IDF. esp32/uart: Use high-level function to install UART0 RX ISR handler. esp32/Makefile: Make FreeRTOS private include dir really private. Eric Poulsen esp32: Add support for hardware SPI peripheral (block 1 and 2). Sergio Conde Gómez esp32/modules/inisetup.py: Mount filesystem at /flash like ESP8266 Damien George esp32: Convert to use core-provided KeyboardInterrupt exception. esp32: Pump the event loop while waiting for rx-chr or delay_ms. esp32: Implement Pin.irq() using "soft" scheduled interrupts. esp32: Update to latest ESP IDF version. Eric Poulsen esp32/README: Add troubleshooting section to the end. tyggerjai esp32: Add support for WS2812 and APA106 RGB LEDs. Damien George esp32: Add makeimg.py script to build full firmware; use it in Makefile. esp32/modsocket: Make socket.read return when socket closes. esp32/modsocket: Initialise the timeout on an accepted socket. esp32/mphalport: Provide proper implementations of disable_/enable_irq. esp32/modmachine: Add disable_irq/enable_irq functions. Nick Moore esp32/modsocket.c: add comment explaining timeout behaviour esp32/modsocket.c: clean up send methods for retries too esp32/modsocket.c: sockets always nonblocking, accept timeout in modsocket esp32: Update to latest ESP IDF version. esp32/modsocket.c: remove MSG_PEEK workaround on select ioctl. esp32/modsocket.c: Initialize tcp when modsocket loads. Damien George esp32/main: Bump heap size from 64k to 96k. esp32/modutime: Add time.time() function. esp32/modsocket: Convert lwip errnos to uPy ones. esp32/modules: Provide symlink to ds18x20 module. esp32: Add support for onewire protocol via OneWire module. esp32: Add support for DHT11 and DHT22 sensors. esp32/mphalport: Improve delay and ticks functions. esp32: Update to latest ESP IDF. esp32/modules: Provide symlink to urequests from micropython-lib. esp32: Populate sys.path. Nick Moore esp32/machine_dac.c: implement DAC pins as well esp32/machine_adc.c: also machine.ADC esp32/machine_touchpad.c: add support for touchpad Damien George esp32/README: Add hint about using GNUmakefile on case-insensitive FS. esp32/mpconfigport.h: Enable maximum speed software SPI. esp32: Provide improved version of mp_hal_delay_us_fast. esp32/mpconfigport.h: Enable MICROPY_PY_BUILTINS_POW3 option. esp32: Update to latest ESP IDF. esp32: Convert to use new oofatfs library and generic VFS sub-system. esp32: Enable help('modules') to list builtin modules. esp32: Convert to use new builtin help function. Aaron Kelly esp32/README: Add comment about ESP-IDF version Damien George esp32: Consistently use size_t instead of mp_uint_t. esp32: Change "Micro Python" to "MicroPython" in license comments. esp32/Makefile: Use -C argument to git instead of cd'ing. esp32/help: Add section to help about using the network module. esp32/README: Add section about configuring and using an ESP32 board. esp32/README: Remove paragraph about buggy toolchain, it's now fixed. esp32/modnetwork: Change network init logging from info to debug. esp32/modnetwork: Don't start AP automatically when init'ing wifi. esp32/modsocket: Implement socket.setsockopt, to support SO_REUSEADDR. esp32: Update to latest ESP IDF. esp32/sdkconfig.h: Remove unused CONFIG_ESPTOOLPY_xxx config settings. esp32/modsocket: Add support for DGRAM and RAW, and sendto/recvfrom. esp32/modsocket: Fix return value of "port" in socket.accept. esp32/modsocket: Make socket.recv take exactly 2 args. esp32: Enable ussl module, using mbedtls component from ESP IDF. esp32/modsocket: Rename "socket" module to "usocket". esp32/sdkconfig: Increase max number of open sockets from 4 to 8. esp32/modsocket: Add error checking for creating and closing sockets. esp32/modsocket: Use _r (re-entrant) versions of LWIP socket API funcs. esp32/modsocket: Raise an exception if socket.connect did not succeed. esp32/modsocket: Make socket.accept return a tuple: (client, addr). esp32/modsocket: Use m_new_obj_with_finaliser instead of calloc. esp32/Makefile: Add check for IDF version, and warn if not supported. esp32/esp32.custom_common.ld: Update to follow changes in IDF. esp32: Update to latest ESP IDF. nubcore esp32: add #define CONFIG_ESP32_WIFI_RX_BUFFER_NUM 25 Nick Moore esp32/modsocket.c: add in sendall and makefile methods #10 esp32/modsocket.c: fixups for #10 esp32/modsocket.c: fix copyright, socket_recv gets param and exception esp32/modnetwork.c: fix copyright, network.active param to bool Damien George esp32/modnetwork: Implement wlan.isconnected() method. esp32/modnetwork: Add initial implementation of wlan.config(). esp32/modnetwork: Simplify event_handler messages. Nick Moore esp32/modsocket.c: support for ioctl, settimeout, setblocking, getaddrinfo Damien George esp32/README: Add comment about FLASH_MODE being dio. esp32: Update to latest ESP IDF. esp32/modnetwork: Remove unnecessary indirection variable for scan list. esp32/modnetwork: Check that STA is active before trying to scan. esp32/mphalport: Replace portTICK_RATE_MS with portTICK_PERIOD_MS. esp32/README: Add comment about using $(HOME) in makefile. esp32/modnetwork: Use memset instead of bzero, the latter is deprecated. esp32/modnetwork: Improve error handling when STA is connecting to AP. esp32/Makefile: Use tab instead of spaces, and use shorter variable. Nick Moore esp32/modsocket.c: AF_*, SOCK_* and IPPROTO_* constants esp32/modsocket.c: socket.settimeout implementation Damien George esp32/Makefile: Update to latest ESP IDF. Nick Moore esp32/modsocket.c: use mp streams for sockets esp32: network.WLAN.ifconfig based on esp8266 version esp32: Fix up exception handling esp32: sketchy modsocket ... revisit this once modnetwork is sorted esp32: First cut at modnetwork, manually rebased from prev. version Damien George esp32/help: Update help text. esp32: Add info about Microbric Pty Ltd being the sponsor of this port. esp32: Add README.md file. esp32/mpconfigport.h: Add weak links to many of the builtin modules. esp32: Enable soft implementation of machine.SPI class. esp32/Makefile: Simplify APP_LD_ARGS by using OBJ variable. esp32/Makefile: Reorganise Makefile and add some comments. esp32/Makefile: Clean up CFLAGS for ESP IDF components. esp32/Makefile: Tidy up names of ESP IDF components, to match dir name. esp32/Makefile: Define and use ESPCOMP variable. esp32: Update to latest ESP IDF. esp32/main: Enable filesystem support. esp32: Use custom ld script to ensure correct code get placed in iram. esp32: Update to latest ESP IDF. esp32/main: Pin the uPy task to core 0. esp32: Update to use latest ESP IDF. esp32: Disable boot-up scripts, spi_flash_erase_sector no longer works. esp32: Add scripts to init and mount filesystem. esp32: Enable frozen bytecode, with scripts stored in "modules/" dir. esp32/modesp: Increase flash_user_start position to 1Mbyte. esp32/Makefile: Add "erase" target for convenient erasure. esp32/sdkconfig: Reorder config settings to put common things together. esp32/sdkconfig: Change to use single core only. esp32: Add esp module. esp32/uart.c: Make sure uart ISR handler is all in iram. esp32/main.c: Use ESP_TASK_PRIO_MIN + 1 for mp_task's priority. esp32/Makefile: Use only bare-minimum flags when compiling .S files. esp32/Makefile: Rename "firmware" to "application". esp32: Update ESP IDF version. esp32/Makefile: Add declarations to build bootloader and partitions. esp32/Makefile: When deploying, write the application last. esp32/Makefile: Use $(INC) variable instead of listing include dirs. esp32/Makefile: Use locally built versions of freertos and newlib libs. esp32: Add low-level uart handler with ISR and ringbuf for stdin. esp32: Add machine.idle() function. esp32: Add machine.I2C class. esp32: Enable machine.time_pulse_us. esp32: Add initial implementation of machine.Pin class. esp32: Prepare main.c for using xTaskCreateStatic. esp32: Clean up mphalport.h. esp32: Add initial uos module. esp32: Clean up mpconfigport.h, enable more features. esp32: Use new reset function. esp32: Update to latest ESP IDF. esp32: Add idf-version target to Makefile, to track IDF commit. esp32: Initial port to ESP32. --- ports/esp32/Makefile | 719 +++++++++++++++++++++++++++ ports/esp32/README.md | 199 ++++++++ ports/esp32/esp32.custom_common.ld | 215 ++++++++ ports/esp32/espneopixel.c | 53 ++ ports/esp32/esponewire.c | 80 +++ ports/esp32/fatfs_port.c | 46 ++ ports/esp32/gccollect.c | 66 +++ ports/esp32/gccollect.h | 42 ++ ports/esp32/help.c | 65 +++ ports/esp32/machine_adc.c | 132 +++++ ports/esp32/machine_dac.c | 97 ++++ ports/esp32/machine_hw_spi.c | 370 ++++++++++++++ ports/esp32/machine_pin.c | 375 ++++++++++++++ ports/esp32/machine_pwm.c | 277 +++++++++++ ports/esp32/machine_timer.c | 192 +++++++ ports/esp32/machine_touchpad.c | 110 ++++ ports/esp32/machine_uart.c | 359 +++++++++++++ ports/esp32/main.c | 131 +++++ ports/esp32/makeimg.py | 25 + ports/esp32/memory.h | 2 + ports/esp32/modesp.c | 129 +++++ ports/esp32/modesp.h | 1 + ports/esp32/modmachine.c | 135 +++++ ports/esp32/modmachine.h | 18 + ports/esp32/modnetwork.c | 561 +++++++++++++++++++++ ports/esp32/modsocket.c | 570 +++++++++++++++++++++ ports/esp32/moduhashlib.c | 142 ++++++ ports/esp32/modules/_boot.py | 12 + ports/esp32/modules/apa106.py | 8 + ports/esp32/modules/dht.py | 1 + ports/esp32/modules/ds18x20.py | 1 + ports/esp32/modules/flashbdev.py | 34 ++ ports/esp32/modules/inisetup.py | 38 ++ ports/esp32/modules/neopixel.py | 33 ++ ports/esp32/modules/onewire.py | 1 + ports/esp32/modules/upip.py | 1 + ports/esp32/modules/upip_utarfile.py | 1 + ports/esp32/modules/upysh.py | 1 + ports/esp32/modules/urequests.py | 1 + ports/esp32/moduos.c | 128 +++++ ports/esp32/modutime.c | 61 +++ ports/esp32/mpconfigport.h | 248 +++++++++ ports/esp32/mphalport.c | 139 ++++++ ports/esp32/mphalport.h | 83 ++++ ports/esp32/mpthreadport.c | 226 +++++++++ ports/esp32/mpthreadport.h | 45 ++ ports/esp32/qstrdefsport.h | 30 ++ ports/esp32/sdkconfig.h | 162 ++++++ ports/esp32/uart.c | 65 +++ ports/esp32/uart.h | 33 ++ 50 files changed, 6463 insertions(+) create mode 100644 ports/esp32/Makefile create mode 100644 ports/esp32/README.md create mode 100644 ports/esp32/esp32.custom_common.ld create mode 100644 ports/esp32/espneopixel.c create mode 100644 ports/esp32/esponewire.c create mode 100644 ports/esp32/fatfs_port.c create mode 100644 ports/esp32/gccollect.c create mode 100644 ports/esp32/gccollect.h create mode 100644 ports/esp32/help.c create mode 100644 ports/esp32/machine_adc.c create mode 100644 ports/esp32/machine_dac.c create mode 100644 ports/esp32/machine_hw_spi.c create mode 100644 ports/esp32/machine_pin.c create mode 100644 ports/esp32/machine_pwm.c create mode 100644 ports/esp32/machine_timer.c create mode 100644 ports/esp32/machine_touchpad.c create mode 100644 ports/esp32/machine_uart.c create mode 100644 ports/esp32/main.c create mode 100644 ports/esp32/makeimg.py create mode 100644 ports/esp32/memory.h create mode 100644 ports/esp32/modesp.c create mode 100644 ports/esp32/modesp.h create mode 100644 ports/esp32/modmachine.c create mode 100644 ports/esp32/modmachine.h create mode 100644 ports/esp32/modnetwork.c create mode 100644 ports/esp32/modsocket.c create mode 100644 ports/esp32/moduhashlib.c create mode 100644 ports/esp32/modules/_boot.py create mode 100644 ports/esp32/modules/apa106.py create mode 120000 ports/esp32/modules/dht.py create mode 120000 ports/esp32/modules/ds18x20.py create mode 100644 ports/esp32/modules/flashbdev.py create mode 100644 ports/esp32/modules/inisetup.py create mode 100644 ports/esp32/modules/neopixel.py create mode 120000 ports/esp32/modules/onewire.py create mode 120000 ports/esp32/modules/upip.py create mode 120000 ports/esp32/modules/upip_utarfile.py create mode 120000 ports/esp32/modules/upysh.py create mode 120000 ports/esp32/modules/urequests.py create mode 100644 ports/esp32/moduos.c create mode 100644 ports/esp32/modutime.c create mode 100644 ports/esp32/mpconfigport.h create mode 100644 ports/esp32/mphalport.c create mode 100644 ports/esp32/mphalport.h create mode 100644 ports/esp32/mpthreadport.c create mode 100644 ports/esp32/mpthreadport.h create mode 100644 ports/esp32/qstrdefsport.h create mode 100644 ports/esp32/sdkconfig.h create mode 100644 ports/esp32/uart.c create mode 100644 ports/esp32/uart.h diff --git a/ports/esp32/Makefile b/ports/esp32/Makefile new file mode 100644 index 000000000..2eae802be --- /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 000000000..7591f040a --- /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 000000000..45242f9af --- /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 000000000..829c8b1c8 --- /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 000000000..781616cbe --- /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 000000000..b3690a01f --- /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 000000000..9843cef00 --- /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 000000000..fe02cc62b --- /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 000000000..95d115c56 --- /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 000000000..d62f362e9 --- /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 000000000..bd0804ec4 --- /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 000000000..437b620f5 --- /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 000000000..493dff3f5 --- /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 000000000..489833e7c --- /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 000000000..d75efb8fc --- /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 000000000..96de1a2a1 --- /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 000000000..0b303d424 --- /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 000000000..091cbddc9 --- /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 000000000..aeedbff7e --- /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 000000000..f3777b0e3 --- /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 000000000..caa055164 --- /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 000000000..a822c02ec --- /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 000000000..51038f314 --- /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 000000000..4909235b4 --- /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 000000000..73a471861 --- /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 000000000..6ba61e874 --- /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 000000000..6f67aa7d9 --- /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 000000000..bf40ea3a9 --- /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 000000000..ef971d78b --- /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 000000000..b6acf2853 --- /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 000000000..9721929a3 --- /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 000000000..935f5342f --- /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 000000000..45bce82cc --- /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 000000000..86c1586cd --- /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 000000000..091629488 --- /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 000000000..130eb6901 --- /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 000000000..d9653d6a6 --- /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 000000000..12d100c29 --- /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 000000000..76661112e --- /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 000000000..9f0e291a6 --- /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 000000000..f037faad9 --- /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 000000000..2c3259324 --- /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 000000000..e588fc65a --- /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 000000000..8a0f1dc8b --- /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 000000000..76d9431c0 --- /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 000000000..54e35d61c --- /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 000000000..ff6c2cc42 --- /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 000000000..ce254c0e6 --- /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 000000000..10a4ba462 --- /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 000000000..264c8b894 --- /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