diff --git a/MicroPython_BUILD/BUILD.sh b/MicroPython_BUILD/BUILD.sh index 3ec9f81..a16bdf6 100755 --- a/MicroPython_BUILD/BUILD.sh +++ b/MicroPython_BUILD/BUILD.sh @@ -48,7 +48,7 @@ #======================= -TOOLS_VER=ver20180408.id +TOOLS_VER=ver20180412.id #======================= # ----------------------------- diff --git a/MicroPython_BUILD/components/micropython/esp32/libs/ftp.c b/MicroPython_BUILD/components/micropython/esp32/libs/ftp.c index 81daea2..e5050fc 100644 --- a/MicroPython_BUILD/components/micropython/esp32/libs/ftp.c +++ b/MicroPython_BUILD/components/micropython/esp32/libs/ftp.c @@ -1394,6 +1394,16 @@ bool ftp_terminate (void) { return res; } +//------------------------- +bool ftp_stop_requested() { + if ((FtpTaskHandle == NULL) || (ftp_mutex == NULL)) return false; + if (xSemaphoreTake(ftp_mutex, FTP_MUTEX_TIMEOUT_MS / portTICK_PERIOD_MS) !=pdTRUE) return false; + + bool res = (ftp_stop == 1); + xSemaphoreGive(ftp_mutex); + return res; +} + //------------------------------- int32_t ftp_get_maxstack (void) { if ((FtpTaskHandle == NULL) || (ftp_mutex == NULL)) return -1; diff --git a/MicroPython_BUILD/components/micropython/esp32/libs/ftp.h b/MicroPython_BUILD/components/micropython/esp32/libs/ftp.h index 78aeee6..4836df1 100644 --- a/MicroPython_BUILD/components/micropython/esp32/libs/ftp.h +++ b/MicroPython_BUILD/components/micropython/esp32/libs/ftp.h @@ -79,6 +79,7 @@ bool ftp_disable (void); bool ftp_reset (void); int ftp_getstate(); bool ftp_terminate (void); +bool ftp_stop_requested(); int32_t ftp_get_maxstack (void); #endif diff --git a/MicroPython_BUILD/components/micropython/esp32/libs/telnet.c b/MicroPython_BUILD/components/micropython/esp32/libs/telnet.c index f0abb6c..cb764c7 100644 --- a/MicroPython_BUILD/components/micropython/esp32/libs/telnet.c +++ b/MicroPython_BUILD/components/micropython/esp32/libs/telnet.c @@ -723,6 +723,16 @@ bool telnet_terminate (void) { return true; } +//---------------------------- +bool telnet_stop_requested() { + if ((TelnetTaskHandle == NULL) || (telnet_mutex == NULL)) return false; + if (xSemaphoreTake(telnet_mutex, TELNET_MUTEX_TIMEOUT_MS / portTICK_PERIOD_MS) !=pdTRUE) return false; + + bool res = (telnet_stop == 1); + xSemaphoreGive(telnet_mutex); + return res; +} + //---------------------------------- int32_t telnet_get_maxstack (void) { if ((TelnetTaskHandle == NULL) || (telnet_mutex == NULL)) return -1; diff --git a/MicroPython_BUILD/components/micropython/esp32/libs/telnet.h b/MicroPython_BUILD/components/micropython/esp32/libs/telnet.h index a39850d..d4f64b9 100644 --- a/MicroPython_BUILD/components/micropython/esp32/libs/telnet.h +++ b/MicroPython_BUILD/components/micropython/esp32/libs/telnet.h @@ -79,6 +79,7 @@ bool telnet_isenabled (void); bool telnet_reset (void); int telnet_getstate(); bool telnet_terminate (void); +bool telnet_stop_requested(); int32_t telnet_get_maxstack (void); #endif diff --git a/MicroPython_BUILD/components/micropython/esp32/machine_adc.c b/MicroPython_BUILD/components/micropython/esp32/machine_adc.c index dd158e3..c59e600 100644 --- a/MicroPython_BUILD/components/micropython/esp32/machine_adc.c +++ b/MicroPython_BUILD/components/micropython/esp32/machine_adc.c @@ -39,68 +39,185 @@ #include "py/runtime.h" #include "py/mphal.h" #include "modmachine.h" +#include "machine_pin.h" #define ADC1_CHANNEL_HALL ADC1_CHANNEL_MAX typedef struct _madc_obj_t { mp_obj_base_t base; int gpio_id; - adc1_channel_t adc1_id; + adc_unit_t adc_num; + adc1_channel_t adc_chan; adc_atten_t atten; adc_bits_width_t width; } madc_obj_t; -static uint16_t adc_chan_used = 0; +static uint16_t adc1_chan_used = 0; +static uint16_t adc2_chan_used = 0; static int8_t adc_width = -1; +static int8_t last_adc_width = -1; +static int8_t last_adc_num = -1; static uint32_t adc_vref = 1100; +static uint32_t last_adc_vref = 0; +static adc_atten_t last_atten = ADC_ATTEN_MAX; +static adc_atten_t last_atten2 = ADC_ATTEN_MAX; +static esp_adc_cal_characteristics_t characteristics; +static const uint8_t adc1_gpios[ADC1_CHANNEL_MAX] = {36, 37, 38, 39, 32, 33, 34, 35}; +static const uint8_t adc2_gpios[ADC2_CHANNEL_MAX] = {4, 0, 2, 15, 13, 12, 14, 27, 25, 26}; + +//------------------------------------- +static void set_width(madc_obj_t *self) +{ + if (adc_width != self->width) { + if (self->adc_num == ADC_UNIT_1) { + esp_err_t err = adc1_config_width(self->width); + if (err != ESP_OK) mp_raise_ValueError("Set width Error"); + } + else { + adc_set_data_width(self->adc_num, self->width); + } + adc_width = self->width; + } +} + +//----------------------------------------------------- +static int get_adc_channel(adc_unit_t adc_num, int pin) +{ + int channel = -1; + if (adc_num == ADC_UNIT_1) { + for (int i=0; i < ADC1_CHANNEL_MAX; i++) { + if (adc1_gpios[i] == pin) { + channel = i; + break; + } + } + } + else { + for (int i=0; i < ADC2_CHANNEL_MAX; i++) { + if (adc2_gpios[i] == pin) { + channel = i; + break; + } + } + } + return channel; +} + +//------------------------------------------------------------------------------------------------------------ +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 *all_args) +{ + enum { ARG_pin, ARG_unit }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_pin, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = mp_const_none}}, + { MP_QSTR_unit, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = ADC_UNIT_1}}, + }; + // parse arguments + 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); -//---------------------------------------------------------------------------------------------------------- -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) { esp_err_t err = 0; mp_arg_check_num(n_args, n_kw, 1, 1, true); int pin_id = 0; - if (MP_OBJ_IS_INT(args[0])) pin_id = mp_obj_get_int(args[0]); - else pin_id = machine_pin_get_id(args[0]); + pin_id = machine_pin_get_gpio(args[ARG_pin].u_obj); madc_obj_t *self = m_new_obj(madc_obj_t);; self->base.type = &machine_adc_type; - if ((pin_id != ADC1_CHANNEL_HALL) && ((pin_id < 32) || (pin_id > 39))) mp_raise_ValueError("invalid Pin for ADC"); - - self->atten = ADC_ATTEN_0db; + self->adc_num = args[ARG_unit].u_int; + if ((self->adc_num != ADC_UNIT_1) && (self->adc_num != ADC_UNIT_2)) { + mp_raise_ValueError("invalid ADC unit (1 and 2 allowed)"); + } + self->atten = ADC_ATTEN_DB_0; self->width = ADC_WIDTH_BIT_12; - if (pin_id != ADC1_CHANNEL_HALL) { - if (pin_id > 35) self->adc1_id = ADC1_CHANNEL_0 + (pin_id-36); - else self->adc1_id = ADC1_CHANNEL_4 + (pin_id-32); - if ((adc_chan_used & 0x0100) && ((self->adc1_id == 36) || (self->adc1_id == 39))) mp_raise_ValueError("hall used, cannot use pins 36 & 39"); - adc_chan_used |= (1 << self->adc1_id); - self->gpio_id = pin_id; - self->gpio_id = pin_id; - err = adc1_config_channel_atten(self->adc1_id, ADC_ATTEN_0db); - if (err != ESP_OK) mp_raise_ValueError("Parameter Error"); + if (pin_id != ADC1_CHANNEL_HALL) { + int channel = get_adc_channel(self->adc_num, pin_id); + if (channel < 0) mp_raise_ValueError("invalid Pin for ADC"); + self->adc_chan = channel; + self->gpio_id = pin_id; + + if (self->adc_num == ADC_UNIT_1) { + if ((adc1_chan_used & 0x0100) && ((pin_id == 36) || (pin_id == 39))) mp_raise_ValueError("hall used, cannot use pins 36 & 39"); + if (adc1_chan_used & (1 << self->adc_chan)) mp_raise_ValueError("pin already used for adc"); + adc1_chan_used |= (1 << self->adc_chan); + + err = adc_gpio_init(self->adc_num, self->adc_chan); + if (err != ESP_OK) mp_raise_ValueError("Error configuring ADC gpio"); + err = adc1_config_channel_atten(self->adc_chan, ADC_ATTEN_DB_0); + if (err != ESP_OK) mp_raise_ValueError("Error configuring attenuation"); + } + else { + if (adc2_chan_used & (1 << self->adc_chan)) mp_raise_ValueError("pin already used for adc"); + adc2_chan_used |= (1 << self->adc_chan); + + gpio_pad_select_gpio(self->gpio_id); + gpio_set_direction(self->gpio_id, GPIO_MODE_DISABLE); + gpio_set_pull_mode(self->gpio_id, GPIO_FLOATING); + + adc_gpio_init(self->adc_num, self->adc_chan); + if (err != ESP_OK) mp_raise_ValueError("Error configuring ADC gpio"); + if (last_atten2 != self->atten) { + adc2_config_channel_atten(self->adc_chan, self->atten); + last_atten2 = self->atten; + } + } } else { - if (adc_chan_used & 0x09) mp_raise_ValueError("adc on gpio 36 or 39 used"); - self->adc1_id = ADC1_CHANNEL_HALL; + self->adc_num = ADC_UNIT_1; + if (adc1_chan_used & 0x09) mp_raise_ValueError("adc on gpio 36 or 39 used"); + if (adc1_chan_used & 0x0100) mp_raise_ValueError("hall already used"); + adc1_chan_used |= 0x0100; + self->adc_chan = ADC1_CHANNEL_HALL; self->gpio_id = GPIO_NUM_MAX; } - if (adc_width != self->width) { - err = adc1_config_width(self->width); - if (err != ESP_OK) mp_raise_ValueError("Set width Error"); - adc_width = self->width; - } + set_width(self); return MP_OBJ_FROM_PTR(self); } +//------------------------------------------- +STATIC mp_obj_t madc_deinit(mp_obj_t self_in) +{ + madc_obj_t *self = self_in; + if (self->gpio_id < 0) return mp_const_none; + + if (self->adc_num == ADC_UNIT_1) { + if (self->adc_chan == ADC1_CHANNEL_HALL) { + adc1_chan_used &= 0x00FF; + gpio_pad_select_gpio(36); + gpio_pad_select_gpio(39); + } + else { + adc1_chan_used &= (~(1 << self->adc_chan) & 0x1FF); + gpio_pad_select_gpio(self->gpio_id); + gpio_set_direction(self->gpio_id, GPIO_MODE_INPUT); + gpio_set_pull_mode(self->gpio_id, GPIO_FLOATING); + } + } + else { + adc2_chan_used &= (~(1 << self->adc_chan) & 0x3FF); + gpio_pad_select_gpio(self->gpio_id); + gpio_set_direction(self->gpio_id, GPIO_MODE_INPUT); + gpio_set_pull_mode(self->gpio_id, GPIO_FLOATING); + } + self->gpio_id = -1; + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(madc_deinit_obj, madc_deinit); + //--------------------------------------------------------------------------------------- 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; + + if (self->gpio_id < 0) { + mp_printf(print, "ADC( deinitialized )"); + return; + } + char satten[16]; char spin[8]; if (self->atten == ADC_ATTEN_DB_0) sprintf(satten, "0dB (1.1V)"); @@ -112,22 +229,32 @@ STATIC void madc_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_ if (self->gpio_id == GPIO_NUM_MAX) sprintf(spin, "HALL"); else sprintf(spin, "Pin(%u)", self->gpio_id); - mp_printf(print, "ADC(%s: width=%u bits, atten=%s, Vref=%u mV)", spin, self->width+9, satten, adc_vref); + mp_printf(print, "ADC(%s: unit=ADC%d, chan=%d, width=%u bits, atten=%s, Vref=%u mV)", spin, self->adc_num, self->adc_chan, self->width+9, satten, adc_vref); } //---------------------------------------------- STATIC mp_obj_t madc_readraw(mp_obj_t self_in) { madc_obj_t *self = self_in; - if (adc_width != self->width) { - esp_err_t err = adc1_config_width(self->width); - if (err != ESP_OK) mp_raise_ValueError("Set width Error"); - adc_width = self->width; - } + if (self->gpio_id < 0) { + mp_raise_ValueError("Not initialized"); + } - int val = 0; - if (self->gpio_id == GPIO_NUM_MAX) val= hall_sensor_read(); - else val = adc1_get_raw(self->adc1_id); - if (val == -1) mp_raise_ValueError("Parameter Error"); + int val = 0; + if (self->adc_num == ADC_UNIT_1) { + set_width(self); + + if (self->gpio_id == GPIO_NUM_MAX) val= hall_sensor_read(); + else val = adc1_get_raw(self->adc_chan); + if (val == -1) mp_raise_ValueError("Parameter Error (ADC raw read)"); + } + else { + if (last_atten2 != self->atten) { + adc2_config_channel_atten(self->adc_chan, self->atten); + last_atten2 = self->atten; + } + esp_err_t err = adc2_get_raw(self->adc_chan, self->atten, &val); + if (err != ESP_OK) mp_raise_ValueError("Cannot read, ADC2 used by Wi-Fi"); + } return MP_OBJ_NEW_SMALL_INT(val); } @@ -136,21 +263,34 @@ MP_DEFINE_CONST_FUN_OBJ_1(madc_readraw_obj, madc_readraw); //------------------------------------------- STATIC mp_obj_t madc_read(mp_obj_t self_in) { madc_obj_t *self = self_in; - esp_adc_cal_characteristics_t characteristics; - if (adc_width != self->width) { - esp_err_t err = adc1_config_width(self->width); - if (err != ESP_OK) mp_raise_ValueError("Set width Error"); - } + if (self->gpio_id < 0) { + mp_raise_ValueError("Not initialized"); + } + + set_width(self); int adc_val = 0; if (self->gpio_id == GPIO_NUM_MAX) adc_val= hall_sensor_read(); else { - // Deprecated - //esp_adc_cal_get_characteristics(adc_vref, self->atten, self->width, &characteristics); - //adc_val = adc1_to_voltage(self->adc1_id, &characteristics); + if ((last_adc_num != self->adc_num) || (last_adc_vref != adc_vref) || (last_atten != self->atten) || (last_adc_width != self->width)) { + // New characterization needed + esp_adc_cal_value_t cal_val = esp_adc_cal_characterize(self->adc_num, self->atten, self->width, adc_vref, &characteristics); + last_adc_vref = adc_vref; + last_atten = self->atten; + last_adc_width = self->width; + last_adc_num = self->adc_num; - esp_adc_cal_value_t adc_val = esp_adc_cal_characterize(ADC_UNIT_1, self->atten, self->width, adc_vref, &characteristics); - esp_adc_cal_get_voltage(self->adc1_id, &characteristics, &adc_val); + ESP_LOGD("MOD_ADC", "Characterize ADC_UNIT_%d: Vref used: %s", self->adc_num, (cal_val == 0) ? "eFuse" : (cal_val == 1) ? "Two point" : "Default"); + } + if ((self->adc_num == ADC_UNIT_2) && (last_atten2 != self->atten)) { + adc2_config_channel_atten(self->adc_chan, self->atten); + last_atten2 = self->atten; + } + esp_err_t err = esp_adc_cal_get_voltage(self->adc_chan, &characteristics, (uint32_t *)&adc_val); + if (err != ESP_OK) { + if (self->adc_num == ADC_UNIT_2) mp_raise_ValueError("Cannot read, ADC2 used by Wi-Fi"); + else mp_raise_ValueError("Error reading"); + } } return MP_OBJ_NEW_SMALL_INT(adc_val); } @@ -159,13 +299,26 @@ 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; - if (self->gpio_id == GPIO_NUM_MAX) return mp_const_none; + if (self->gpio_id < 0) { + mp_raise_ValueError("Not initialized"); + } + + if (self->gpio_id == GPIO_NUM_MAX) return mp_const_none; adc_atten_t atten = mp_obj_get_int(atten_in); if ((atten < ADC_ATTEN_DB_0) || (atten > ADC_ATTEN_DB_11)) mp_raise_ValueError("Unsupported atten value"); - esp_err_t err = adc1_config_channel_atten(self->adc1_id, atten); - if (err != ESP_OK) mp_raise_ValueError("Parameter Error"); + esp_err_t err; + if (self->adc_num == ADC_UNIT_1) { + err = adc1_config_channel_atten(self->adc_chan, atten); + if (err != ESP_OK) mp_raise_ValueError("Parameter Error (config attenuation)"); + } + else { + if (last_atten2 != atten) { + adc2_config_channel_atten(self->adc_chan, atten); + last_atten2 = self->atten; + } + } self->atten = atten; return mp_const_none; } @@ -174,7 +327,11 @@ MP_DEFINE_CONST_FUN_OBJ_2(madc_atten_obj, madc_atten); //--------------------------------------------------------------- STATIC mp_obj_t madc_width(mp_obj_t self_in, mp_obj_t width_in) { madc_obj_t *self = self_in; - if (self->gpio_id == GPIO_NUM_MAX) return mp_const_none; + if (self->gpio_id < 0) { + mp_raise_ValueError("Not initialized"); + } + + if (self->gpio_id == GPIO_NUM_MAX) return mp_const_none; adc_bits_width_t width = mp_obj_get_int(width_in); if ((width < ADC_WIDTH_9Bit) || (width > ADC_WIDTH_12Bit)) mp_raise_ValueError("Unsupported width value"); @@ -224,6 +381,7 @@ STATIC const mp_rom_map_elem_t madc_locals_dict_table[] = { { 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_vref), MP_ROM_PTR(&madc_vref_togpio_obj) }, + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&madc_deinit_obj) }, { MP_ROM_QSTR(MP_QSTR_HALL), MP_ROM_INT(ADC1_CHANNEL_MAX) }, diff --git a/MicroPython_BUILD/components/micropython/esp32/machine_pin.c b/MicroPython_BUILD/components/micropython/esp32/machine_pin.c index c51541c..b10b4b9 100644 --- a/MicroPython_BUILD/components/micropython/esp32/machine_pin.c +++ b/MicroPython_BUILD/components/micropython/esp32/machine_pin.c @@ -165,7 +165,7 @@ STATIC mp_obj_t machine_pin_obj_init_helper(const machine_pin_obj_t *self, size_ // configure the pin for gpio if (rtc_gpio_is_valid_gpio(self->id)) rtc_gpio_deinit(self->id); - if (self->id < 28) gpio_pad_select_gpio(self->id); + gpio_pad_select_gpio(self->id); // set initial value (do this before configuring mode/pull) if (args[ARG_value].u_obj != MP_OBJ_NULL) { diff --git a/MicroPython_BUILD/components/micropython/esp32/main.c b/MicroPython_BUILD/components/micropython/esp32/main.c index 090e7d3..252f45f 100644 --- a/MicroPython_BUILD/components/micropython/esp32/main.c +++ b/MicroPython_BUILD/components/micropython/esp32/main.c @@ -271,10 +271,19 @@ void micropython_entry(void) { // === Set esp32 log levels while running MicroPython === if (CONFIG_MICRO_PY_LOG_LEVEL < CONFIG_LOG_DEFAULT_LEVEL) esp_log_level_set("*", CONFIG_MICRO_PY_LOG_LEVEL); - esp_log_level_set("wifi", ESP_LOG_ERROR); - esp_log_level_set("rmt", ESP_LOG_ERROR); + if ((CONFIG_LOG_DEFAULT_LEVEL > ESP_LOG_WARN) && (CONFIG_MICRO_PY_LOG_LEVEL > ESP_LOG_WARN)){ + esp_log_level_set("wifi", ESP_LOG_WARN); + esp_log_level_set("rmt", ESP_LOG_WARN); + esp_log_level_set("tcpip_adapter", ESP_LOG_WARN); + esp_log_level_set("event", ESP_LOG_WARN); + esp_log_level_set("nvs", ESP_LOG_WARN); + esp_log_level_set("phy_init", ESP_LOG_WARN); + esp_log_level_set("wl_flash", ESP_LOG_WARN); + esp_log_level_set("RTC_MODULE", ESP_LOG_WARN); + } #ifdef CONFIG_MICROPY_USE_OTA - esp_log_level_set("OTA_UPDATE", ESP_LOG_DEBUG); + if (CONFIG_LOG_DEFAULT_LEVEL >= ESP_LOG_DEBUG) esp_log_level_set("OTA_UPDATE", ESP_LOG_DEBUG); + else esp_log_level_set("OTA_UPDATE", CONFIG_LOG_DEFAULT_LEVEL); #endif nvs_flash_init(); diff --git a/MicroPython_BUILD/components/micropython/esp32/modmachine.c b/MicroPython_BUILD/components/micropython/esp32/modmachine.c index 0b897f9..af282dc 100644 --- a/MicroPython_BUILD/components/micropython/esp32/modmachine.c +++ b/MicroPython_BUILD/components/micropython/esp32/modmachine.c @@ -52,6 +52,7 @@ #include "esp_log.h" #include "esp_err.h" #include "esp_pm.h" +#include "esp_wifi.h" #include "driver/uart.h" #include "py/obj.h" @@ -64,6 +65,7 @@ #include "mpsleep.h" #include "machine_rtc.h" #include "uart.h" +#include "modnetwork.h" #if MICROPY_PY_MACHINE @@ -176,7 +178,7 @@ static void RTC_IRAM_ATTR wake_stub() if ((machine_rtc_config.deepsleep_time) && (machine_rtc_config.deepsleep_interval)) { // == Set the out pin to active level if configured if (machine_rtc_config.stub_outpin >= 0) { - if (machine_rtc_config.stub_outpin < 28) gpio_pad_select_gpio(machine_rtc_config.stub_outpin); + gpio_pad_select_gpio(machine_rtc_config.stub_outpin); if (machine_rtc_config.stub_outpin < 32) gpio_output_set(machine_rtc_config.stub_outpin_level << machine_rtc_config.stub_outpin, (machine_rtc_config.stub_outpin_level ? 0 : 1) << machine_rtc_config.stub_outpin, @@ -278,10 +280,18 @@ void prepareSleepReset(uint8_t hrst, char *msg) internalUmount(); if (!hrst) { - mp_thread_deinit(); - if (msg) mp_hal_stdout_tx_str(msg); - + // stop and deinitialize WiFi + if (wifi_network_state == WIFI_STATE_STARTED) { + wifi_network_state = WIFI_STATE_STOPPED; + wifi_sta_isconnected = false; + wifi_sta_has_ipaddress = false; + wifi_sta_changed_ipaddress = false; + wifi_ap_isconnected = false; + wifi_ap_sta_isconnected = false; + esp_wifi_stop(); + esp_wifi_deinit(); + } // deinitialise peripherals //ToDo: deinitialize other peripherals, threads, services, ... machine_pins_deinit(); @@ -462,7 +472,7 @@ STATIC mp_obj_t machine_deepsleep(size_t n_args, const mp_obj_t *pos_args, mp_ma } if (machine_rtc_config.ext0_pin >= 0) { - printf("EXT0=%d\n", machine_rtc_config.ext0_pin); + ESP_LOGD("DEEP SLEEP", "EXT0=%d\n", machine_rtc_config.ext0_pin); esp_sleep_enable_ext0_wakeup((gpio_num_t)machine_rtc_config.ext0_pin, machine_rtc_config.ext0_level ? 1 : 0); esp_set_deep_sleep_wake_stub(&wake_stub); } @@ -475,7 +485,7 @@ STATIC mp_obj_t machine_deepsleep(size_t n_args, const mp_obj_t *pos_args, mp_ma } } if (ext1_pins != 0) { - printf("EXT1 = [%llx]\n", ext1_pins); + ESP_LOGD("DEEP SLEEP", "EXT1 = [%llx]\n", ext1_pins); //esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON); uint8_t ext1_level = machine_rtc_config.ext1_level; if (machine_rtc_config.ext1_level == EXT1_WAKEUP_ALL_HIGH) ext1_level = ESP_EXT1_WAKEUP_ANY_HIGH; @@ -487,9 +497,9 @@ STATIC mp_obj_t machine_deepsleep(size_t n_args, const mp_obj_t *pos_args, mp_ma esp_sleep_enable_touchpad_wakeup(); } - printf("Sleep time: time=%d, interval=%d, pin=%d, level=%d, wait=%llu\n", + ESP_LOGD("DEEP SLEEP", "Sleep time: time=%d, interval=%d, pin=%d, level=%d, wait=%llu\n", sleep_time, stub_sleep, led_pin, args[ARG_stub_ledlevel].u_bool, wait_in_stub); - prepareSleepReset(0, "ESP32: DEEP SLEEP\n"); + prepareSleepReset(0, NULL); if ((stub_sleep) || (led_pin >= 0)) { if (led_pin >= 0) { diff --git a/MicroPython_BUILD/components/micropython/esp32/modnetwork.c b/MicroPython_BUILD/components/micropython/esp32/modnetwork.c index 1aa46b5..781f376 100644 --- a/MicroPython_BUILD/components/micropython/esp32/modnetwork.c +++ b/MicroPython_BUILD/components/micropython/esp32/modnetwork.c @@ -45,10 +45,8 @@ #include "py/runtime.h" #include "py/mphal.h" #include "py/mperrno.h" -//#include "py/obj.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" @@ -61,6 +59,10 @@ #include "modnetwork.h" #define MODNETWORK_INCLUDE_CONSTANTS (1) +//#define MPY_WIFI_USED_STORAGE WIFI_STORAGE_FLASH +#define MPY_WIFI_USED_STORAGE WIFI_STORAGE_RAM + +static const char *MODNETTWORK_TAG = "[modnetwork]"; NORETURN void _esp_exceptions(esp_err_t e) { switch (e) { @@ -147,6 +149,7 @@ static const char* const wifi_events[] = { "Ethernet got IP from connected AP", }; +/* static const char* const wifi_cyphers[] = { "NONE", "WEP40", @@ -156,6 +159,7 @@ static const char* const wifi_cyphers[] = { "TKIP_CCMP", "UNKNOWN", }; +*/ static const char* const wifi_auth_modes[] = { "OPEN", @@ -173,7 +177,7 @@ static inline void esp_exceptions(esp_err_t e) { #define ESP_EXCEPTIONS(x) do { esp_exceptions(x); } while (0); // global variables -int wifi_network_state = -1; +int wifi_network_state = WIFI_STATE_NOTINIT; bool wifi_sta_isconnected = false; bool wifi_sta_has_ipaddress = false; bool wifi_sta_changed_ipaddress = false; @@ -181,17 +185,15 @@ bool wifi_ap_isconnected = false; bool wifi_ap_sta_isconnected = false; const mp_obj_type_t wlan_if_type; -const wlan_if_obj_t wlan_sta_obj = {{&wlan_if_type}, WIFI_IF_STA}; -const wlan_if_obj_t wlan_ap_obj = {{&wlan_if_type}, WIFI_IF_AP}; +const wlan_if_obj_t wlan_sta_obj = {{&wlan_if_type}, WIFI_IF_STA, WIFI_MODE_STA}; +const wlan_if_obj_t wlan_ap_obj = {{&wlan_if_type}, WIFI_IF_AP, WIFI_MODE_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 reconnect. -static bool wifi_sta_connected = false; - -static uint8_t _isConnected = 0; +// Set to "true" if the STA interface is requested to be automatically reconnected. +static bool wifi_sta_reconnect = false; +static bool sta_isStarted = false; static mp_obj_t event_callback = NULL; static mp_obj_t probereq_callback = NULL; @@ -220,7 +222,6 @@ end: //------------------------------------------------------ static void processEvent_callback(system_event_t *event) { - if (wifi_network_state < 2) return; if (event->event_id >= SYSTEM_EVENT_MAX) return; mp_sched_carg_t *carg = NULL; @@ -361,62 +362,60 @@ static void processEvent_callback(system_event_t *event) } } +//------------------------ +static void tryReconnect() +{ + if (wifi_sta_reconnect) { + wifi_mode_t mode; + if (esp_wifi_get_mode(&mode) == ESP_OK) { + if (mode & WIFI_MODE_STA) { + if (sta_isStarted) { + // STA is active and started, attempt to reconnect. + esp_err_t res = esp_wifi_connect(); + if (res != ESP_OK) { + ESP_LOGD(MODNETTWORK_TAG, "error attempting to reconnect: (%d)", res-ESP_ERR_WIFI_BASE); + } + } + } + } + } +} // 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) { - if (wifi_network_state < 2) return ESP_OK; - if (wifi_mutex) xSemaphoreTake(wifi_mutex, 1000); - switch(event->event_id) { - case SYSTEM_EVENT_STA_START: - ESP_LOGI("wifi", "STA_START"); - break; - case SYSTEM_EVENT_STA_GOT_IP: - ESP_LOGI("network", "GOT_IP"); - _isConnected = 1; - break; - case SYSTEM_EVENT_STA_DISCONNECTED: { - // This is a workaround as ESP32 WiFi libs don't currently - // auto-reassociate. - _isConnected = 0; - 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_BEACON_TIMEOUT: - ESP_LOGD("wifi", "beacon timeout"); - // AP has dropped out; try to reconnect. - break; - case WIFI_REASON_NO_AP_FOUND: - ESP_LOGD("wifi", "no AP found"); - // AP may not exist, or it may have momentarily dropped out; try to reconnect. - break; - case WIFI_REASON_AUTH_FAIL: - ESP_LOGD("wifi", "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) { - ESP_LOGD("wifi", "error attempting to reconnect: 0x%04x", e); - } - } + + if (wifi_network_state == WIFI_STATE_STARTED) { + switch(event->event_id) { + case SYSTEM_EVENT_STA_START: + sta_isStarted = true; + tryReconnect(); + break; + case SYSTEM_EVENT_STA_STOP: + sta_isStarted = false; + break; + case SYSTEM_EVENT_STA_DISCONNECTED: { + // This is a workaround as ESP32 WiFi library doesn't currently auto-reconnect. + system_event_sta_disconnected_t *disconn = &event->event_info.disconnected; + switch (disconn->reason) { + case WIFI_REASON_AUTH_FAIL: + wifi_sta_reconnect = false; + break; + case WIFI_REASON_ASSOC_LEAVE: + sta_isStarted = false; + break; + default: + // Let other errors through and try to reconnect. + break; } + tryReconnect(); + break; + } + default: + break; } - break; - } - default: - ESP_LOGD("network", "event %d", event->event_id); - break; } #ifdef CONFIG_MICROPY_USE_MDNS @@ -424,19 +423,12 @@ static esp_err_t event_handler(void *ctx, system_event_t *event) #endif // === Handle events callbacks === - processEvent_callback(event); + if (wifi_network_state == WIFI_STATE_STARTED) processEvent_callback(event); if (wifi_mutex) xSemaphoreGive(wifi_mutex); 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); @@ -445,70 +437,15 @@ STATIC void require_if(mp_obj_t wlan_if, int if_no) { } } -//-------------------------------------- -static void _wifi_init(wifi_mode_t mode) -{ - esp_err_t ret = 0; - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - - ESP_LOGD("modnetwork", "Initializing WiFi"); - ret = esp_wifi_init(&cfg); - if (ret != ESP_OK) { - ESP_LOGE("modnetwork", "Error initializing WiFi (%d)", ret); - mp_raise_OSError(ret); - } - ret = esp_wifi_set_storage(WIFI_STORAGE_FLASH); - if (ret != ESP_OK) { - ESP_LOGE("modnetwork", "Error initializing WiFi storage (%d)", ret); - mp_raise_OSError(ret); - } - ESP_LOGD("modnetwork", "Initialized"); - esp_wifi_set_mode(mode); - if (ret != ESP_OK) { - ESP_LOGE("modnetwork", "Error setting WiFi mode (%d)", ret); - mp_raise_OSError(ret); - } - ret = esp_wifi_start(); - if (ret != ESP_OK) { - ESP_LOGE("modnetwork", "Error starting WiFi(%d)", ret); - mp_raise_OSError(ret); - } - ESP_LOGD("modnetwork", "Started"); -} - -//------------------------------------------------------------- -STATIC mp_obj_t get_wlan(size_t n_args, const mp_obj_t *args) { - if (wifi_network_state < 0) { - mp_raise_ValueError("TCT/IP Adapter not initialized"); - } - - // Get required WiFi mode - int if_id = (n_args > 0) ? mp_obj_get_int(args[0]) : WIFI_IF_STA; - if ((if_id != WIFI_IF_STA) && (if_id != WIFI_IF_AP)) { - mp_raise_ValueError("invalid WLAN interface identifier"); - } - - if (wifi_network_state < 2) { - wifi_mode_t mode; - if (if_id == WIFI_IF_STA) mode = WIFI_MODE_STA; - else mode = WIFI_MODE_AP; - _wifi_init(mode); - wifi_network_state = 2; - } - - if (if_id == WIFI_IF_STA) return MP_OBJ_FROM_PTR(&wlan_sta_obj); - else return MP_OBJ_FROM_PTR(&wlan_ap_obj); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(get_wlan_obj, 0, 1, get_wlan); - //-------------------------------- STATIC mp_obj_t esp_initialize() { - if (wifi_network_state < 0) { - ESP_LOGD("modnetwork", "Initializing TCP/IP"); + if (wifi_network_state < WIFI_STATE_INIT) { + // This is executed only once + ESP_LOGD(MODNETTWORK_TAG, "Initializing TCP/IP"); tcpip_adapter_init(); - ESP_LOGD("modnetwork", "Initializing Event Loop"); + ESP_LOGD(MODNETTWORK_TAG, "Initializing Event Loop"); ESP_EXCEPTIONS( esp_event_loop_init(event_handler, NULL) ); - ESP_LOGD("modnetwork", "esp_event_loop_init done"); + ESP_LOGD(MODNETTWORK_TAG, "Event loop initialized"); // create mutex's if (wifi_mutex == NULL) wifi_mutex = xSemaphoreCreateMutex(); @@ -516,7 +453,7 @@ STATIC mp_obj_t esp_initialize() { // add probe requests handler esp_wifi_set_sta_rx_probe_req(processPROBEREQRECVED); - wifi_network_state = 0; + wifi_network_state = WIFI_STATE_INIT; } return mp_const_none; } @@ -526,76 +463,251 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_0(esp_initialize_obj, esp_initialize); #error WIFI_MODE_STA and WIFI_MODE_AP are supposed to be bitfields! #endif + +// Return WLAN object for given WiFi mode (default: STA) +// Does not start WiFi if not started ! +//------------------------------------------------------------- +STATIC mp_obj_t get_wlan(size_t n_args, const mp_obj_t *args) { + if (wifi_network_state < WIFI_STATE_INIT) { + mp_raise_ValueError("TCT/IP Adapter not initialized"); + } + + // Default mode + int if_id = WIFI_IF_STA; + + if (n_args > 0) { + // Get required WiFi mode + if_id = mp_obj_get_int(args[0]); + if ((if_id != WIFI_IF_STA) && (if_id != WIFI_IF_AP)) { + mp_raise_ValueError("invalid WLAN interface identifier"); + } + } + + // Return the WLAN object + if (if_id == WIFI_IF_STA) return MP_OBJ_FROM_PTR(&wlan_sta_obj); + return MP_OBJ_FROM_PTR(&wlan_ap_obj); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(get_wlan_obj, 0, 1, get_wlan); + + +//---------------------- +static void _init_wifi() +{ + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + esp_err_t ret = esp_wifi_init(&cfg); + if (ret != ESP_OK) { + ESP_LOGE(MODNETTWORK_TAG, "Error initializing WiFi (%d)", ret); + mp_raise_OSError(ret); + } + ret = esp_wifi_set_storage(MPY_WIFI_USED_STORAGE); + if (ret != ESP_OK) { + ESP_LOGE(MODNETTWORK_TAG, "Error initializing WiFi storage (%d)", ret); + mp_raise_OSError(ret); + } + ESP_LOGD(MODNETTWORK_TAG, "WiFi Initialized"); + wifi_network_state = WIFI_STATE_STOPPED; +} + +// Initialize WiFi if needed, set the requested mode and start WiFi +//------------------------------------------------------ +static void _wifi_init(wifi_mode_t mode, bool reconnect) +{ + if (wifi_network_state < WIFI_STATE_STOPPED) _init_wifi(); + + esp_err_t ret = 0; + if (wifi_network_state == WIFI_STATE_STARTED) { + // Stop WiFi + wifi_network_state = WIFI_STATE_STOPPED; + wifi_sta_isconnected = false; + wifi_sta_has_ipaddress = false; + wifi_sta_changed_ipaddress = false; + wifi_ap_isconnected = false; + wifi_ap_sta_isconnected = false; + ret = esp_wifi_stop(); + if (ret != ESP_OK) { + ESP_LOGE(MODNETTWORK_TAG, "Error stopping WiFi (%d)", ret); + goto exit_error; + } + ESP_LOGD(MODNETTWORK_TAG, "WiFi Stopped"); + } + + // Set WiFi mode + esp_wifi_set_mode(mode); + if (ret != ESP_OK) { + ESP_LOGE(MODNETTWORK_TAG, "Error setting WiFi mode (%d)", ret); + goto exit_error; + } + // Start WiFi + ret = esp_wifi_start(); + if (ret != ESP_OK) { + ESP_LOGE(MODNETTWORK_TAG, "Error starting WiFi (%d)", ret); + goto exit_error; + } + wifi_sta_reconnect = reconnect; + + wifi_network_state = WIFI_STATE_STARTED; + ESP_LOGD(MODNETTWORK_TAG, "WiFi Started, mode %d", mode); + return; + +exit_error: + wifi_network_state = WIFI_STATE_INIT; + ret = esp_wifi_stop(); + if (ret != ESP_OK) { + ESP_LOGE(MODNETTWORK_TAG, "Error stopping WiFi (%d)", ret); + } + vTaskDelay(5 / portTICK_PERIOD_MS); + esp_wifi_deinit(); + if (ret != ESP_OK) { + ESP_LOGE(MODNETTWORK_TAG, "Error deinitializing WiFi (%d)", ret); + } + vTaskDelay(5 / portTICK_PERIOD_MS); + mp_raise_OSError(ret); +} + +// Activate (start) or deactivate (stop) Wifi //------------------------------------------------------------- STATIC mp_obj_t esp_active(size_t n_args, const mp_obj_t *args) { - if (wifi_network_state < 1) { - ESP_LOGW("modnetwork", "WiFi not initialized"); + if (wifi_network_state < WIFI_STATE_INIT) { + ESP_LOGW(MODNETTWORK_TAG, "WiFi not initialized"); return mp_const_false; } - if (n_args < 2) { - if (wifi_network_state == 2) return mp_const_true; - else return mp_const_false; - } wlan_if_obj_t *self = MP_OBJ_TO_PTR(args[0]); + wifi_mode_t mode; + if (n_args < 2) goto exit; + + // Get requested action bool active = mp_obj_is_true(args[1]); - if ((!active) && (wifi_network_state == 2)) { - // was active, Deactivate WiFi - wifi_network_state = 1; - wifi_sta_isconnected = false; - wifi_sta_has_ipaddress = false; - wifi_sta_changed_ipaddress = false; - wifi_ap_isconnected = false; - wifi_ap_sta_isconnected = false; - esp_wifi_stop(); - esp_wifi_deinit(); + + if (active) { + // === WiFi activation requested === + if (wifi_network_state == WIFI_STATE_STARTED) { + // WiFi already started, check mode + esp_err_t ret = esp_wifi_get_mode(&mode); + if (ret != ESP_OK) { + ESP_LOGE(MODNETTWORK_TAG, "Error getting WiFi mode (%d)", ret); + return mp_const_false; + } + // If requested mode is already started, just return + if (self->wifi_mode & mode) return mp_const_true; + // Restart WiFi adding a new mode + _wifi_init(mode | self->wifi_mode, (mode & WIFI_MODE_STA) & wifi_sta_reconnect); + } + else { + // Start WiFi + _wifi_init(self->wifi_mode, false); + } } - else if ((active) && (wifi_network_state == 1)) { - // Was inactive, Activate WiFi - wifi_mode_t mode; - if (self->if_id == WIFI_IF_STA) mode = WIFI_MODE_STA; - else mode = WIFI_MODE_AP; - _wifi_init(mode); - wifi_network_state = 2; + else { + // === WiFi deactivation requested === + if (wifi_network_state == WIFI_STATE_STARTED) { + // Get current mode + esp_err_t ret = esp_wifi_get_mode(&mode); + if (ret != ESP_OK) { + ESP_LOGE(MODNETTWORK_TAG, "Error getting WiFi mode (%d)", ret); + return mp_const_false; + } + if (self->wifi_mode & mode) { + wifi_mode_t new_mode = mode & ~self->wifi_mode; + if (new_mode == 0) { + // No mode is active, stop and deinitialize WiFi + wifi_network_state = WIFI_STATE_INIT; + wifi_sta_isconnected = false; + wifi_sta_has_ipaddress = false; + wifi_sta_changed_ipaddress = false; + wifi_ap_isconnected = false; + wifi_ap_sta_isconnected = false; + ret = esp_wifi_stop(); + if (ret != ESP_OK) { + ESP_LOGE(MODNETTWORK_TAG, "Error stopping WiFi (%d)", ret); + } + vTaskDelay(5 / portTICK_PERIOD_MS); + esp_wifi_deinit(); + if (ret != ESP_OK) { + ESP_LOGE(MODNETTWORK_TAG, "Error deinitializing WiFi (%d)", ret); + } + vTaskDelay(5 / portTICK_PERIOD_MS); + ESP_LOGI(MODNETTWORK_TAG, "WiFi Stopped"); + } + else { + _wifi_init(new_mode, (new_mode & WIFI_MODE_STA) & wifi_sta_reconnect); + } + } + } } - if (wifi_network_state == 2) return mp_const_true; - else return mp_const_false; + +exit: + // === Return wifi status (started/not started) === + if (wifi_network_state != WIFI_STATE_STARTED) return mp_const_false; + // Get current mode + esp_err_t ret = esp_wifi_get_mode(&mode); + if (ret != ESP_OK) { + ESP_LOGE(MODNETTWORK_TAG, "Error getting WiFi mode (%d)", ret); + return mp_const_false; + } + if (self->wifi_mode & mode) return mp_const_true; + return mp_const_false; } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp_active_obj, 1, 2, esp_active); +//------------------------------------------ +static bool _check_wifi_started(bool except) +{ + if (wifi_network_state < WIFI_STATE_STARTED) { + if (except) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "WiFi not started")); + } + else { + ESP_LOGW(MODNETTWORK_TAG, "WiFi not started"); + return false; + } + } + return true; +} + +// Connect to access point (only in STA mode) //---------------------------------------------------------------- STATIC mp_obj_t esp_connect(size_t n_args, const mp_obj_t *args) { - if (wifi_network_state < 2) { - ESP_LOGW("modnetwork", "WiFi not started"); - return mp_const_none; + if (!_check_wifi_started(false)) return mp_const_none; + + wlan_if_obj_t *self = MP_OBJ_TO_PTR(args[0]); + if (self->wifi_mode != WIFI_MODE_STA) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "Not supported in AP mode")); } - wifi_mode_t mode; + + wifi_mode_t mode; esp_err_t ret = esp_wifi_get_mode(&mode); if (ret != ESP_OK) { - ESP_LOGE("modnetwork", "Error getting WiFi mode (%d)", ret); + ESP_LOGE(MODNETTWORK_TAG, "Error getting WiFi mode (%d)", ret); return mp_const_none; } - if ((mode & WIFI_MODE_STA) == 0) return mp_const_none; + // Only connect if in STA mode + if ((mode & WIFI_MODE_STA) == 0) { + ESP_LOGE(MODNETTWORK_TAG, "STA mode not started"); + return mp_const_none; + } mp_uint_t len; const char *p; if (n_args > 1) { + // Get SSID 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))); + // Get password (optional) 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))); if ((n_args > 3)) { - // Get channel + // Get channel (optional int chan = mp_obj_get_int(args[3]); if ((chan >= 1) && (chan <= 13)) wifi_sta_config.sta.channel = chan; } ret = esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_sta_config); if (ret != ESP_OK) { - ESP_LOGE("modnetwork", "Error configuring WiFi (%d)", ret); + ESP_LOGE(MODNETTWORK_TAG, "Error configuring WiFi (%d)", ret); return mp_const_none; } } @@ -603,9 +715,9 @@ STATIC mp_obj_t esp_connect(size_t n_args, const mp_obj_t *args) { MP_THREAD_GIL_EXIT(); ret = esp_wifi_connect(); MP_THREAD_GIL_ENTER(); - if (ret == ESP_OK) wifi_sta_connected = true; + if (ret == ESP_OK) wifi_sta_reconnect = true; else { - ESP_LOGE("modnetwork", "Error connecting to AP (%d)", ret); + ESP_LOGE(MODNETTWORK_TAG, "Error connecting to AP (%d)", ret); } return mp_const_none; @@ -614,16 +726,19 @@ 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) { - if (wifi_network_state < 2) { - ESP_LOGW("modnetwork", "WiFi not started"); - return mp_const_none; + if (!_check_wifi_started(false)) return mp_const_none; + + wlan_if_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (self->wifi_mode != WIFI_MODE_STA) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "Not supported in AP mode")); } - if (wifi_sta_connected) { + + if (wifi_sta_reconnect) { esp_err_t ret = esp_wifi_disconnect(); if (ret != ESP_OK) { - ESP_LOGW("modnetwork", "Error disconnecting from AP (%d)", ret); + ESP_LOGW(MODNETTWORK_TAG, "Error disconnecting from AP (%d)", ret); } - else wifi_sta_connected = false; + else wifi_sta_reconnect = false; } return mp_const_none; } @@ -631,11 +746,9 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp_disconnect_obj, esp_disconnect); //--------------------------------------------------------------- STATIC mp_obj_t esp_status(size_t n_args, const mp_obj_t *args) { - if (wifi_network_state < 2) { - ESP_LOGW("modnetwork", "WiFi not started"); - return mp_const_none; - } - if (n_args == 1) { + if (!_check_wifi_started(false)) return mp_const_none; + + if (n_args == 1) { // no arguments: return None until link status is implemented return mp_const_none; } @@ -667,10 +780,9 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp_status_obj, 1, 2, esp_status); //------------------------------------------------------------- STATIC mp_obj_t esp_scan(size_t n_args, const mp_obj_t *args) { - if (wifi_network_state < 2) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "WiFi not started")); - } - // check that STA mode is active + _check_wifi_started(true); + + // check that STA mode is active wifi_mode_t mode; esp_err_t ret = esp_wifi_get_mode(&mode); if (ret != ESP_OK) { @@ -682,11 +794,12 @@ STATIC mp_obj_t esp_scan(size_t n_args, const mp_obj_t *args) { 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?) if (n_args > 1) config.show_hidden = mp_obj_is_true(args[1]); + 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) ); @@ -711,18 +824,20 @@ STATIC mp_obj_t esp_scan(size_t n_args, const mp_obj_t *args) { } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp_scan_obj, 1, 2, esp_scan); -//------------------------------------------------- -STATIC mp_obj_t esp_isconnected(mp_obj_t self_in) { - if (wifi_network_state < 2) { - return mp_obj_new_bool(false); - } - wlan_if_obj_t *self = MP_OBJ_TO_PTR(self_in); +//-------------------------------------------------------------------- +STATIC mp_obj_t esp_isconnected(size_t n_args, const mp_obj_t *args) { + if (!_check_wifi_started(false)) return mp_obj_new_bool(false); + + wlan_if_obj_t *self = MP_OBJ_TO_PTR(args[0]); + bool check_clients = true; + if (n_args > 1) check_clients = mp_obj_is_true(args[1]); + if (self->if_id == WIFI_IF_STA) { return mp_obj_new_bool(((wifi_sta_isconnected) && (wifi_sta_has_ipaddress))); } else { - bool res = false; - if (wifi_ap_isconnected) { + bool res = wifi_ap_isconnected; + if ((res) && (check_clients)) { wifi_sta_list_t sta; esp_wifi_ap_get_sta_list(&sta); res = (sta.num != 0); @@ -730,31 +845,60 @@ STATIC mp_obj_t esp_isconnected(mp_obj_t self_in) { return mp_obj_new_bool(res); } } -STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp_isconnected_obj, esp_isconnected); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp_isconnected_obj, 1, 2, esp_isconnected); + +//--------------------------- +static bool wifi_is_started() +{ + wifi_mode_t wifi_mode; + esp_err_t ret = esp_wifi_get_mode(&wifi_mode); + if (ret != ESP_OK) return false; + + bool sta_f = ((wifi_sta_isconnected) && (wifi_sta_has_ipaddress)); + bool ap_f = wifi_ap_isconnected; + if (wifi_mode == WIFI_MODE_STA) return sta_f; + else if (wifi_mode == WIFI_MODE_AP) return ap_f; + else if (wifi_mode == WIFI_MODE_APSTA) return (sta_f | ap_f); + return false; +} + +//---------------------------------------------- +STATIC mp_obj_t esp_isactive(mp_obj_t self_in) { + if (wifi_network_state < WIFI_STATE_STARTED) return mp_obj_new_bool(false); + + return mp_obj_new_bool(wifi_is_started()); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp_isactive_obj, esp_isactive); //----------------------------------------------------------------- STATIC mp_obj_t esp_ifconfig(size_t n_args, const mp_obj_t *args) { - if (wifi_network_state < 2) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "WiFi not started")); - } - wlan_if_obj_t *self = MP_OBJ_TO_PTR(args[0]); + if (wifi_network_state < WIFI_STATE_INIT) { + mp_raise_ValueError("TCT/IP Adapter not initialized"); + } + + wlan_if_obj_t *self = MP_OBJ_TO_PTR(args[0]); tcpip_adapter_ip_info_t info; tcpip_adapter_dns_info_t dns_info; + tcpip_adapter_get_ip_info(self->if_id, &info); tcpip_adapter_get_dns_info(self->if_id, TCPIP_ADAPTER_DNS_MAIN, &dns_info); if (n_args == 1) { - // get + // === Get configuration === 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_info.ip, NETUTILS_BIG), }; + // Return tuple: (ip, netmask, gateway, dns_ip) return mp_obj_new_tuple(4, tuple); - } else { - // set + } + else { + // === set configuration parameters from tuple: (ip, netmask, gateway, dns_ip) === mp_obj_t *items; mp_obj_get_array_fixed_n(args[1], 4, &items); + + // Static IP netutils_parse_ipv4_addr(items[0], (void*)&info.ip, NETUTILS_BIG); if (mp_obj_is_integer(items[1])) { // allow numeric netmask, i.e.: @@ -767,8 +911,11 @@ STATIC mp_obj_t esp_ifconfig(size_t n_args, const mp_obj_t *args) { else { netutils_parse_ipv4_addr(items[1], (void*)&info.netmask, NETUTILS_BIG); } + // net mask netutils_parse_ipv4_addr(items[2], (void*)&info.gw, NETUTILS_BIG); + // gateway netutils_parse_ipv4_addr(items[3], (void*)&dns_info.ip, NETUTILS_BIG); + // To set a static IP we have to disable DHCP first if ((self->if_id == WIFI_IF_STA) || (self->if_id == ESP_IF_ETH)) { esp_err_t e = tcpip_adapter_dhcpc_stop(self->if_id); @@ -788,12 +935,83 @@ STATIC mp_obj_t esp_ifconfig(size_t n_args, const mp_obj_t *args) { } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp_ifconfig_obj, 1, 2, esp_ifconfig); +//-------------------------------------------------------------------------------------- +static mp_obj_t get_config_param(uintptr_t arg, wlan_if_obj_t *self, wifi_config_t *cfg) +{ + mp_obj_t val = mp_const_none; + + #define QS(x) (uintptr_t)MP_OBJ_NEW_QSTR(x) + mp_obj_tuple_t *t; + switch (arg) { + case QS(MP_QSTR_mac): { + uint8_t mac[6]; + if (esp_wifi_get_mac(self->if_id, mac) != ESP_OK) val = mp_const_false; + else val = mp_obj_new_bytes(mac, sizeof(mac)); + break; + } + case QS(MP_QSTR_essid): + if (self->if_id == WIFI_IF_AP) val = mp_obj_new_str((char*)cfg->ap.ssid, cfg->ap.ssid_len); + break; + case QS(MP_QSTR_hidden): + if (self->if_id == WIFI_IF_AP) val = mp_obj_new_bool(cfg->ap.ssid_hidden); + break; + case QS(MP_QSTR_authmode): + if (self->if_id == WIFI_IF_AP) { + t = mp_obj_new_tuple(2, NULL); + t->items[0] = MP_OBJ_NEW_SMALL_INT(cfg->ap.authmode); + t->items[1] = mp_obj_new_str(wifi_auth_modes[cfg->ap.authmode], strlen(wifi_auth_modes[cfg->ap.authmode])); + val = MP_OBJ_FROM_PTR(t); + } + break; + case QS(MP_QSTR_mode): + t = mp_obj_new_tuple(2, NULL); + t->items[0] = MP_OBJ_NEW_SMALL_INT(self->if_id); + if (self->if_id == WIFI_IF_STA) + t->items[1] = mp_obj_new_str("STA_IF", 6); + else t->items[1] = mp_obj_new_str("AP_IF", 5); + val = MP_OBJ_FROM_PTR(t); + break; + case QS(MP_QSTR_wifimode): + t = mp_obj_new_tuple(2, NULL); + wifi_mode_t mode; + esp_err_t ret = esp_wifi_get_mode(&mode); + if (ret == ESP_OK) { + t->items[0] = MP_OBJ_NEW_SMALL_INT(mode); + if (mode ==WIFI_MODE_STA) t->items[1] = mp_obj_new_str("STA", 3); + else if (mode ==WIFI_MODE_AP) t->items[1] = mp_obj_new_str("AP", 2); + else if (mode ==WIFI_MODE_APSTA) t->items[1] = mp_obj_new_str("APSTA", 5); + else t->items[1] = mp_obj_new_str("Unknown", 7); + } + else { + t->items[0] = MP_OBJ_NEW_SMALL_INT(0); + t->items[1] = mp_obj_new_str("Unknown", 7); + } + val = MP_OBJ_FROM_PTR(t); + break; + case QS(MP_QSTR_channel): + if (self->if_id == WIFI_IF_AP) val = MP_OBJ_NEW_SMALL_INT(cfg->ap.channel); + break; + case QS(MP_QSTR_dhcp_hostname): { + const char *s; + if (tcpip_adapter_get_hostname(self->if_id, &s) != ESP_OK) val = mp_const_false; + else val = mp_obj_new_str(s, strlen(s)); + break; + } + default: + val = mp_obj_new_str("Unknown config param", 20); + } + #undef QS + + return val; +} + +// Set or get wifi configuration parameters //--------------------------------------------------------------------------------- STATIC mp_obj_t esp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { - if (wifi_network_state < 2) { - nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "WiFi not started")); - } - if (n_args != 1 && kwargs->used != 0) { + //_check_wifi_started(true); + if (wifi_network_state < WIFI_STATE_STOPPED) _init_wifi(); + + if (n_args != 1 && kwargs->used != 0) { mp_raise_TypeError("either pos or kw args are allowed"); } @@ -859,7 +1077,7 @@ STATIC mp_obj_t esp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs break; } default: - goto unknown; + mp_raise_ValueError("unknown config param"); } #undef QS @@ -877,68 +1095,49 @@ STATIC mp_obj_t esp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs // Get config if (n_args != 2) { - mp_raise_TypeError("can query only one parameter"); + mp_raise_TypeError("only one query argument allowed"); } - int req_if = -1; mp_obj_t val; + #define QS(x) (uintptr_t)MP_OBJ_NEW_QSTR(x) + bool get_all = ((uintptr_t)args[1] == QS(MP_QSTR_all)); - #define QS(x) (uintptr_t)MP_OBJ_NEW_QSTR(x) - mp_obj_tuple_t *t; - 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; - t = mp_obj_new_tuple(2, NULL); - t->items[0] = MP_OBJ_NEW_SMALL_INT(cfg.ap.authmode); - t->items[1] = mp_obj_new_str(wifi_auth_modes[cfg.ap.authmode], strlen(wifi_auth_modes[cfg.ap.authmode])); - val = MP_OBJ_FROM_PTR(t); - break; - case QS(MP_QSTR_wifimode): - t = mp_obj_new_tuple(2, NULL); - t->items[0] = MP_OBJ_NEW_SMALL_INT(self->if_id); - if (self->if_id == WIFI_IF_STA) - t->items[1] = mp_obj_new_str("STA_IF", 6); - else t->items[1] = mp_obj_new_str("AP_IF", 5); - val = MP_OBJ_FROM_PTR(t); - break; - case QS(MP_QSTR_channel): - req_if = WIFI_IF_AP; - val = MP_OBJ_NEW_SMALL_INT(cfg.ap.channel); - break; - case QS(MP_QSTR_dhcp_hostname): { - const char *s; - ESP_EXCEPTIONS(tcpip_adapter_get_hostname(self->if_id, &s)); - val = mp_obj_new_str(s, strlen(s)); - break; - } - default: - goto unknown; - } - #undef QS + if (get_all) { + // Get all config parameters + mp_obj_dict_t *dct = mp_obj_new_dict(0); - // We post-check interface requirements to save on code size - if (req_if >= 0) { - require_if(args[0], req_if); + val = get_config_param(QS(MP_QSTR_mac), self, &cfg); + if ((val != mp_const_none) && (val != mp_const_false)) mp_obj_dict_store(dct, mp_obj_new_str("mac", 3), val); + val = get_config_param(QS(MP_QSTR_essid), self, &cfg); + if ((val != mp_const_none) && (val != mp_const_false)) mp_obj_dict_store(dct, mp_obj_new_str("essid", 5), val); + val = get_config_param(QS(MP_QSTR_hidden), self, &cfg); + if ((val != mp_const_none) && (val != mp_const_false)) mp_obj_dict_store(dct, mp_obj_new_str("hidden", 6), val); + val = get_config_param(QS(MP_QSTR_authmode), self, &cfg); + if ((val != mp_const_none) && (val != mp_const_false)) mp_obj_dict_store(dct, mp_obj_new_str("authmode", 8), val); + val = get_config_param(QS(MP_QSTR_mode), self, &cfg); + if ((val != mp_const_none) && (val != mp_const_false)) mp_obj_dict_store(dct, mp_obj_new_str("mode", 4), val); + val = get_config_param(QS(MP_QSTR_wifimode), self, &cfg); + if ((val != mp_const_none) && (val != mp_const_false)) mp_obj_dict_store(dct, mp_obj_new_str("wifimode", 8), val); + val = get_config_param(QS(MP_QSTR_channel), self, &cfg); + if ((val != mp_const_none) && (val != mp_const_false)) mp_obj_dict_store(dct, mp_obj_new_str("channel", 7), val); + val = get_config_param(QS(MP_QSTR_dhcp_hostname), self, &cfg); + if ((val != mp_const_none) && (val != mp_const_false)) mp_obj_dict_store(dct, mp_obj_new_str("dhcp_hostname", 13), val); + + val = dct; } + else { + // Get one config parameter + val = get_config_param((uintptr_t)args[1], self, &cfg); + if (val == mp_const_none) { + mp_raise_msg(&mp_type_OSError, self->if_id == WIFI_IF_STA ? "AP required" : "STA required"); + } + if (val == mp_const_false) { + mp_raise_msg(&mp_type_OSError, "Parameter not available"); + } + } + #undef QS return val; - -unknown: - mp_raise_ValueError("unknown config param"); - return mp_const_none; } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(esp_config_obj, 1, esp_config); @@ -983,16 +1182,17 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp_probereq_callback_obj, 1, 2, esp_ //======================================================== 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 }, - { MP_OBJ_NEW_QSTR(MP_QSTR_eventCB), (mp_obj_t)&esp_callback_obj }, - { MP_OBJ_NEW_QSTR(MP_QSTR_probereqCB), (mp_obj_t)&esp_probereq_callback_obj }, + { 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_wifiactive), (mp_obj_t)&esp_isactive_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 }, + { MP_OBJ_NEW_QSTR(MP_QSTR_eventCB), (mp_obj_t)&esp_callback_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_probereqCB), (mp_obj_t)&esp_probereq_callback_obj }, }; STATIC MP_DEFINE_CONST_DICT(wlan_if_locals_dict, wlan_if_locals_dict_table); @@ -1014,8 +1214,50 @@ extern const mp_obj_type_t mqtt_type; #endif +// ============================== +// ==== FTP & Telnet services === + #if defined(CONFIG_MICROPY_USE_TELNET) || defined(CONFIG_MICROPY_USE_FTPSERVER) #include "mpthreadport.h" + +//----------------------------- +static mp_obj_t get_listen_ip() +{ + if ((wifi_network_state == WIFI_STATE_STARTED) && (wifi_is_started())) { + wifi_mode_t mode; + tcpip_adapter_if_t tcpip_if = TCPIP_ADAPTER_IF_MAX; + int n_if = 0; + esp_err_t ret = esp_wifi_get_mode(&mode); + if (ret == ESP_OK) { + if (mode == WIFI_MODE_STA) { + n_if = 1; + tcpip_if = TCPIP_ADAPTER_IF_STA; + } + else if (mode == WIFI_MODE_AP) { + n_if = 1; + tcpip_if = TCPIP_ADAPTER_IF_AP; + } + else if (mode == WIFI_MODE_APSTA) n_if = 2; + } + if (n_if > 0) { + tcpip_adapter_ip_info_t info; + mp_obj_t ip_tuple[n_if]; + if (n_if == 1) { + tcpip_adapter_get_ip_info(tcpip_if, &info); + ip_tuple[0] = netutils_format_ipv4_addr((uint8_t*)&info.ip, NETUTILS_BIG); + } + else { + tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &info); + ip_tuple[0] = netutils_format_ipv4_addr((uint8_t*)&info.ip, NETUTILS_BIG); + tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_AP, &info); + ip_tuple[1] = netutils_format_ipv4_addr((uint8_t*)&info.ip, NETUTILS_BIG); + } + return mp_obj_new_tuple(n_if, ip_tuple); + } + } + return mp_const_none; +} + #endif //============================== @@ -1034,17 +1276,10 @@ STATIC mp_obj_t mod_network_startTelnet(mp_uint_t n_args, const mp_obj_t *pos_ar 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); - if (wifi_network_state < 2) { - ESP_LOGE("[Telnet_start]", "WiFi not started"); + if ((wifi_network_state < WIFI_STATE_STARTED) || (!wifi_is_started())) { + ESP_LOGE("[Telnet_start]", "WiFi not started or not connected"); return mp_const_false; } - wifi_mode_t wifi_mode = WIFI_MODE_MAX; - esp_err_t res = esp_wifi_get_mode(&wifi_mode); - if ((res != ESP_OK) || ((wifi_mode != WIFI_MODE_STA) && (wifi_mode != WIFI_MODE_AP))) { - ESP_LOGE("[Telnet_start]", "Error, (res=%d, mode=%d)", res, wifi_mode); - return mp_const_false; - } - ESP_LOGD("[Telnet_start]", "WiFi mode: %s", (wifi_mode == WIFI_MODE_STA) ? "STA" : "AP"); if (MP_OBJ_IS_STR(args[0].u_obj)) { snprintf(telnet_user, TELNET_USER_PASS_LEN_MAX, mp_obj_str_get_str(args[0].u_obj)); @@ -1101,7 +1336,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_network_TelnetMaxStack_obj, mod_network_Tel //-------------------------------------- STATIC mp_obj_t mod_network_stateTelnet() { - mp_obj_t tuple[2]; + mp_obj_t tuple[3]; char state[16] = {'\0'}; int telnet_state = telnet_getstate(); @@ -1116,8 +1351,9 @@ STATIC mp_obj_t mod_network_stateTelnet() tuple[0] = mp_obj_new_int(telnet_state); tuple[1] = mp_obj_new_str(state, strlen(state)); + tuple[2] = get_listen_ip(); - return mp_obj_new_tuple(2, tuple); + return mp_obj_new_tuple(3, tuple); } STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_network_stateTelnet_obj, mod_network_stateTelnet); @@ -1159,17 +1395,10 @@ STATIC mp_obj_t mod_network_startFtp(mp_uint_t n_args, const mp_obj_t *pos_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); - if (wifi_network_state < 2) { - ESP_LOGE("[Ftp_start]", "WiFi not started"); + if ((wifi_network_state < WIFI_STATE_STARTED) || (!wifi_is_started())) { + ESP_LOGE("[Ftp_start]", "WiFi not started or not connected"); return mp_const_false; } - wifi_mode_t wifi_mode = WIFI_MODE_MAX; - esp_err_t res = esp_wifi_get_mode(&wifi_mode); - if ((res != ESP_OK) || ((wifi_mode != WIFI_MODE_STA) && (wifi_mode != WIFI_MODE_AP))) { - ESP_LOGE("[Ftp_start]", "Error, (res=%d, mode=%d)", res, wifi_mode); - return mp_const_false; - } - ESP_LOGD("[Ftp_start]", "WiFi mode: %s", (wifi_mode == WIFI_MODE_STA) ? "STA" : "AP"); if (MP_OBJ_IS_STR(args[0].u_obj)) { snprintf(ftp_user, FTP_USER_PASS_LEN_MAX, mp_obj_str_get_str(args[0].u_obj)); @@ -1229,7 +1458,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_network_FtpMaxStack_obj, mod_network_FtpMax //------------------------------------ STATIC mp_obj_t mod_network_stateFtp() { - mp_obj_t tuple[4]; + mp_obj_t tuple[5]; char state[20] = {'\0'}; int ftp_state, ftp_substate; @@ -1264,8 +1493,9 @@ STATIC mp_obj_t mod_network_stateFtp() else if (ftp_substate == E_FTP_STE_SUB_DATA_CONNECTED) sprintf(state, "Data: Connected"); else sprintf(state, "Unknown"); tuple[3] = mp_obj_new_str(state, strlen(state)); + tuple[4] = get_listen_ip(); - return mp_obj_new_tuple(4, tuple); + return mp_obj_new_tuple(5, tuple); } STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_network_stateFtp_obj, mod_network_stateFtp); @@ -1294,12 +1524,32 @@ const mp_obj_type_t network_ftp_type = { extern const mp_obj_type_t mdns_type; #endif +//-------------------------------------------------------------------- +STATIC mp_obj_t esp_wlan_callback(size_t n_args, const mp_obj_t *args) +{ + if (n_args == 0) { + if (event_callback == NULL) return mp_const_false; + return mp_const_true; + } + + if (wifi_mutex) xSemaphoreTake(wifi_mutex, 1000); + if ((MP_OBJ_IS_FUN(args[0])) || (MP_OBJ_IS_METH(args[0]))) { + event_callback = args[0]; + } + else event_callback = NULL; + if (wifi_mutex) xSemaphoreGive(wifi_mutex); + + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(esp_wlan_callback_obj, 0, 1, esp_wlan_callback); + //============================================================== 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_WLANcallback), (mp_obj_t)&esp_wlan_callback_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_phy_mode), (mp_obj_t)&esp_phy_mode_obj }, #ifdef CONFIG_MICROPY_USE_ETHERNET { MP_OBJ_NEW_QSTR(MP_QSTR_LAN), (mp_obj_t)&get_lan_obj }, diff --git a/MicroPython_BUILD/components/micropython/esp32/modnetwork.h b/MicroPython_BUILD/components/micropython/esp32/modnetwork.h index 7e6210a..a2b5af8 100644 --- a/MicroPython_BUILD/components/micropython/esp32/modnetwork.h +++ b/MicroPython_BUILD/components/micropython/esp32/modnetwork.h @@ -24,14 +24,23 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ + #ifndef MICROPY_INCLUDED_ESP32_MODNETWORK_H #define MICROPY_INCLUDED_ESP32_MODNETWORK_H +#include "esp_wifi_types.h" + +#define WIFI_STATE_NOTINIT -1 +#define WIFI_STATE_INIT 0 +#define WIFI_STATE_STOPPED 1 +#define WIFI_STATE_STARTED 2 + enum { PHY_LAN8720, PHY_TLK110 }; typedef struct _wlan_if_obj_t { mp_obj_base_t base; int if_id; + wifi_mode_t wifi_mode; } wlan_if_obj_t; typedef void (*wifi_sta_rx_probe_req_t)(const uint8_t *frame, int len, int rssi); diff --git a/MicroPython_BUILD/components/micropython/esp32/modsocket.c b/MicroPython_BUILD/components/micropython/esp32/modsocket.c index c0ab698..dfbd861 100644 --- a/MicroPython_BUILD/components/micropython/esp32/modsocket.c +++ b/MicroPython_BUILD/components/micropython/esp32/modsocket.c @@ -510,6 +510,16 @@ STATIC mp_uint_t socket_stream_ioctl(mp_obj_t self_in, mp_uint_t request, uintpt if (FD_ISSET(socket->fd, &wfds)) ret |= MP_STREAM_POLL_WR; if (FD_ISSET(socket->fd, &efds)) ret |= MP_STREAM_POLL_HUP; return ret; + } else if (request == MP_STREAM_CLOSE) { + if (socket->fd >= 0) { + int ret = lwip_close_r(socket->fd); + if (ret != 0) { + *errcode = errno; + return MP_STREAM_ERROR; + } + socket->fd = -1; + } + return 0; } *errcode = MP_EINVAL; @@ -517,8 +527,8 @@ STATIC mp_uint_t socket_stream_ioctl(mp_obj_t self_in, mp_uint_t request, uintpt } 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___del__), (mp_obj_t)&mp_stream_close_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_close), (mp_obj_t)&mp_stream_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 }, diff --git a/MicroPython_BUILD/components/micropython/esp32/modules/microWebSocket.py b/MicroPython_BUILD/components/micropython/esp32/modules/microWebSocket.py old mode 100644 new mode 100755 index afc1664..f37a532 --- a/MicroPython_BUILD/components/micropython/esp32/modules/microWebSocket.py +++ b/MicroPython_BUILD/components/micropython/esp32/modules/microWebSocket.py @@ -1,8 +1,16 @@ +""" +The MIT License (MIT) +Copyright © 2018 Jean-Christophe Bos & HC² (www.hc2.fr) +Copyright © 2018 LoBo (https://github.com/loboris/MicroPython_ESP32_psRAM_LoBo) +""" from hashlib import sha1 from binascii import b2a_base64 from struct import pack -import gc, _thread, time +import _thread +import time +import gc +import websocket class MicroWebSocket : @@ -26,6 +34,7 @@ class MicroWebSocket : # ===( Utils )=============================================================== # ============================================================================ + @staticmethod def _tryAllocByteArray(size) : for x in range(10) : try : @@ -37,6 +46,7 @@ class MicroWebSocket : # ---------------------------------------------------------------------------- + @staticmethod def _tryStartThread(func, args=(), stackSize=4096) : _ = _thread.stack_size(stackSize) for x in range(4) : @@ -61,6 +71,7 @@ class MicroWebSocket : self.ClosedCallback = None self.thID = None self.isThreaded = threaded + if self._handshake(httpResponse) : self._ctrlBuf = MicroWebSocket._tryAllocByteArray(0x7D) self._msgBuf = MicroWebSocket._tryAllocByteArray(maxRecvLen) @@ -71,7 +82,7 @@ class MicroWebSocket : th = MicroWebSocket._tryStartThread(self._wsProcess, (acceptCallback, ), stackSize) if th: self.thID = th - return + return else : self._wsProcess(acceptCallback) return @@ -89,8 +100,9 @@ class MicroWebSocket : try : key = self._httpCli.GetRequestHeaders().get('Sec-WebSocket-Key', None) if key : - r = sha1(key + self._handshakeSign).digest() - r = b2a_base64(r).decode() + key += self._handshakeSign + r = sha1(key.encode()).digest() + r = b2a_base64(r).decode().strip() httpResponse.WriteSwitchProto("websocket", { "Sec-WebSocket-Accept" : r }) return True except : @@ -106,7 +118,6 @@ class MicroWebSocket : acceptCallback(self, self._httpCli) except Exception as ex : print("MicroWebSocket : Error on accept callback (%s)." % str(ex)) - while not self._closed : if self.isThreaded: notify = _thread.getnotification() @@ -115,7 +126,6 @@ class MicroWebSocket : break if not self._receiveFrame() : self.Close() - if self.ClosedCallback : try : self.ClosedCallback(self) @@ -126,7 +136,6 @@ class MicroWebSocket : def _receiveFrame(self) : try : - b = self._socket.read(2) if not b or len(b) != 2 : return False @@ -163,7 +172,8 @@ class MicroWebSocket : buf = memoryview(self._msgBuf)[self._msgLen:] if length > len(buf) : return False - if self._socket.readinto(buf, length) != length : + x = self._socket.readinto(buf[0:length]) + if x != length : return False if masked : for i in range(length) : @@ -194,7 +204,8 @@ class MicroWebSocket : if length > len(self._ctrlBuf) : return False if length > 0 : - if self._socket.readinto(self._ctrlBuf, length) != length : + x = self._socket.readinto(self._ctrlBuf[0:length]) + if x != length : return False pingData = memoryview(self._ctrlBuf)[:length] else : @@ -222,9 +233,12 @@ class MicroWebSocket : if dataLen > 0 : if dataLen >= 0x7E : self._socket.write(pack('>H', dataLen)) - return self._socket.write(data) == dataLen + ret = self._socket.write(data) == dataLen else : - return True + ret = True + if self._socket is not self._socket : + self._socket.flush() # CPython needs flush to continue protocol + return ret except : pass return False diff --git a/MicroPython_BUILD/components/micropython/esp32/modules/microWebSocket.py.new b/MicroPython_BUILD/components/micropython/esp32/modules/microWebSocket.py.new deleted file mode 100755 index 593eb1a..0000000 --- a/MicroPython_BUILD/components/micropython/esp32/modules/microWebSocket.py.new +++ /dev/null @@ -1,279 +0,0 @@ -""" -The MIT License (MIT) -Copyright © 2018 Jean-Christophe Bos & HC² (www.hc2.fr) -Copyright © 2018 LoBo (https://github.com/loboris/MicroPython_ESP32_psRAM_LoBo) -""" - -from hashlib import sha1 -from binascii import b2a_base64 -from struct import pack -import _thread -import time -import gc - -class MicroWebSocket : - - # ============================================================================ - # ===( Constants )============================================================ - # ============================================================================ - - _handshakeSign = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" - - _opContFrame = 0x0 - _opTextFrame = 0x1 - _opBinFrame = 0x2 - _opCloseFrame = 0x8 - _opPingFrame = 0x9 - _opPongFrame = 0xA - - _msgTypeText = 1 - _msgTypeBin = 2 - - # ============================================================================ - # ===( Utils )=============================================================== - # ============================================================================ - - @staticmethod - def _tryAllocByteArray(size) : - for x in range(10) : - try : - gc.collect() - return bytearray(size) - except : - pass - return None - - # ---------------------------------------------------------------------------- - - @staticmethod - def _tryStartThread(func, args=(), stackSize=4096) : - _ = _thread.stack_size(stackSize) - for x in range(4) : - try : - gc.collect() - th = _thread.start_new_thread("MicroWebSocket", func, args) - return th - except : - time.sleep_ms(100) - return False - - # ============================================================================ - # ===( Constructor )========================================================== - # ============================================================================ - - def __init__(self, socket, httpClient, httpResponse, maxRecvLen, threaded, acceptCallback) : - self._socket = socket - self._httpCli = httpClient - self._closed = True - self.RecvTextCallback = None - self.RecvBinaryCallback = None - self.ClosedCallback = None - self.thID = None - self.isThreaded = threaded - - if self._handshake(httpResponse) : - self._ctrlBuf = MicroWebSocket._tryAllocByteArray(0x7D) - self._msgBuf = MicroWebSocket._tryAllocByteArray(maxRecvLen) - if self._ctrlBuf and self._msgBuf : - self._msgType = None - self._msgLen = 0 - if threaded : - th = MicroWebSocket._tryStartThread(self._wsProcess, (acceptCallback, ), stackSize) - if th: - self.thID = th - return - else : - self._wsProcess(acceptCallback) - return - print("MicroWebSocket : Out of memory on new WebSocket connection.") - try : - self._socket.close() - except : - pass - - # ============================================================================ - # ===( Functions )============================================================ - # ============================================================================ - - def _handshake(self, httpResponse) : - try : - key = self._httpCli.GetRequestHeaders().get('Sec-WebSocket-Key', None) - if key : - key += self._handshakeSign - r = sha1(key.encode()).digest() - r = b2a_base64(r).decode().strip() - httpResponse.WriteSwitchProto("websocket", { "Sec-WebSocket-Accept" : r }) - return True - except : - pass - return False - - # ---------------------------------------------------------------------------- - - def _wsProcess(self, acceptCallback) : - self._socket.settimeout(3600) - self._closed = False - try : - acceptCallback(self, self._httpCli) - except Exception as ex : - print("MicroWebSocket : Error on accept callback (%s)." % str(ex)) - while not self._closed : - if self.isThreaded: - notify = _thread.getnotification() - if notify == _thread.EXIT: - self.Close() - break - if not self._receiveFrame() : - self.Close() - if self.ClosedCallback : - try : - self.ClosedCallback(self) - except Exception as ex : - print("MicroWebSocket : Error on closed callback (%s)." % str(ex)) - - # ---------------------------------------------------------------------------- - - def _receiveFrame(self) : - try : - b = self._socket.read(2) - if not b or len(b) != 2 : - return False - - fin = b[0] & 0x80 > 0 - opcode = b[0] & 0x0F - masked = b[1] & 0x80 > 0 - length = b[1] & 0x7F - - if opcode == self._opContFrame and not self._msgType : - return False - elif opcode == self._opTextFrame : - self._msgType = self._msgTypeText - elif opcode == self._opBinFrame : - self._msgType = self._msgTypeBin - - if length == 0x7E : - b = self._socket.read(2) - if not b or len(b) != 2 : - return False - length = (b[0] << 8) + b[1] - elif length == 0x7F : - return False - - mask = self._socket.read(4) if masked else None - if masked and (not mask or len(mask) != 4) : - return False - - if opcode == self._opContFrame or \ - opcode == self._opTextFrame or \ - opcode == self._opBinFrame : - - if length > 0 : - buf = memoryview(self._msgBuf)[self._msgLen:] - if length > len(buf) : - return False - x = self._socket.readinto(buf[0:length]) - if x != length : - return False - if masked : - for i in range(length) : - idx = self._msgLen + i - self._msgBuf[idx] ^= mask[i%4] - self._msgLen += length - if fin : - b = bytes(memoryview(self._msgBuf)[:self._msgLen]) - if self._msgType == self._msgTypeText : - if self.RecvTextCallback : - try : - self.RecvTextCallback(self, b.decode()) - except Exception as ex : - print("MicroWebSocket : Error on recv text callback (%s)." % str(ex)) - else : - if self.RecvBinaryCallback : - try : - self.RecvBinaryCallback(self, b) - except Exception as ex : - print("MicroWebSocket : Error on recv binary callback (%s)." % str(ex)) - self._msgType = None - self._msgLen = 0 - else : - return False - - elif opcode == self._opPingFrame : - - if length > len(self._ctrlBuf) : - return False - if length > 0 : - x = self._socket.readinto(self._ctrlBuf[0:length]) - if x != length : - return False - pingData = memoryview(self._ctrlBuf)[:length] - else : - pingData = None - self._sendFrame(self._opPongFrame, pingData) - - elif opcode == self._opCloseFrame : - self.Close() - - except : - return False - - return True - - # ---------------------------------------------------------------------------- - - def _sendFrame(self, opcode, data=None, fin=True) : - if not self._closed and opcode >= 0x00 and opcode <= 0x0F : - dataLen = 0 if not data else len(data) - if dataLen <= 0xFFFF : - b1 = (0x80 | opcode) if fin else opcode - b2 = 0x7E if dataLen >= 0x7E else dataLen - try : - if self._socket.write(pack('>BB', b1, b2)) == 2 : - if dataLen > 0 : - if dataLen >= 0x7E : - self._socket.write(pack('>H', dataLen)) - ret = self._socket.write(data) == dataLen - else : - ret = True - if self._socket is not self._socket : - self._socket.flush() # CPython needs flush to continue protocol - return ret - except : - pass - return False - - # ---------------------------------------------------------------------------- - - def SendText(self, msg) : - return self._sendFrame(self._opTextFrame, msg.encode()) - - # ---------------------------------------------------------------------------- - - def SendBinary(self, data) : - return self._sendFrame(self._opBinFrame, data) - - # ---------------------------------------------------------------------------- - - def IsClosed(self) : - return self._closed - - # ---------------------------------------------------------------------------- - - def threadID(self) : - return self.thID - - # ---------------------------------------------------------------------------- - - def Close(self) : - if not self._closed : - try : - self._sendFrame(self._opCloseFrame) - self._socket.close() - self._closed = True - except : - pass - - # ============================================================================ - # ============================================================================ - # ============================================================================ - diff --git a/MicroPython_BUILD/components/micropython/esp32/modules/microWebSrv.py b/MicroPython_BUILD/components/micropython/esp32/modules/microWebSrv.py old mode 100644 new mode 100755 index 363be88..b05b6ca --- a/MicroPython_BUILD/components/micropython/esp32/modules/microWebSrv.py +++ b/MicroPython_BUILD/components/micropython/esp32/modules/microWebSrv.py @@ -1,10 +1,19 @@ +""" +The MIT License (MIT) +Copyright © 2018 Jean-Christophe Bos & HC² (www.hc2.fr) +Copyright © 2018 LoBo (https://github.com/loboris/MicroPython_ESP32_psRAM_LoBo) +""" -from json import dumps -from os import stat -import socket -import gc -import network -import socket, gc, _thread, time + +from json import loads, dumps +from os import stat +import _thread +import network +import time +import socket +import websocket +import gc +import re try : from microWebTemplate import MicroWebTemplate @@ -16,6 +25,15 @@ try : except : pass +class MicroWebSrvRoute : + def __init__(self, route, method, func, routeArgNames, routeRegex) : + self.route = route + self.method = method + self.func = func + self.routeArgNames = routeArgNames + self.routeRegex = routeRegex + + class MicroWebSrv : # ============================================================================ @@ -61,15 +79,34 @@ class MicroWebSrv : _pyhtmlPagesExt = '.pyhtml' + # ============================================================================ + # ===( Class globals )======================================================= + # ============================================================================ + + _docoratedRouteHandlers = [] + # ============================================================================ # ===( Utils )=============================================================== # ============================================================================ + @classmethod + def route(cls, url, method='GET'): + """ Adds a route handler function to the routing list """ + def route_decorator(func): + item = (url, method, func) + cls._docoratedRouteHandlers.append(item) + return func + return route_decorator + + # ---------------------------------------------------------------------------- + + @staticmethod def HTMLEscape(s) : return ''.join(MicroWebSrv._html_escape_chars.get(c, c) for c in s) # ---------------------------------------------------------------------------- + @staticmethod def _tryAllocByteArray(size) : for x in range(10) : try : @@ -81,9 +118,10 @@ class MicroWebSrv : # ---------------------------------------------------------------------------- - def _tryStartThread(func, args=()) : - _ = _thread.stack_size(8*1024) - for x in range(4) : + @staticmethod + def _tryStartThread(func, args=(), stacksize=8192) : + _ = _thread.stack_size(stacksize) + for x in range(10) : try : gc.collect() th = _thread.start_new_thread("MicroWebServer", func, args) @@ -93,6 +131,8 @@ class MicroWebSrv : return False # ---------------------------------------------------------------------------- + + @staticmethod def _unquote(s) : r = s.split('%') for i in range(1, len(r)) : @@ -105,11 +145,13 @@ class MicroWebSrv : # ---------------------------------------------------------------------------- + @staticmethod def _unquote_plus(s) : return MicroWebSrv._unquote(s.replace('+', ' ')) # ---------------------------------------------------------------------------- + @staticmethod def _fileExists(path) : try : stat(path) @@ -119,6 +161,7 @@ class MicroWebSrv : # ---------------------------------------------------------------------------- + @staticmethod def _isPyHTMLFile(filename) : return filename.lower().endswith(MicroWebSrv._pyhtmlPagesExt) @@ -127,11 +170,11 @@ class MicroWebSrv : # ============================================================================ def __init__( self, - routeHandlers = None, + routeHandlers = [], port = 80, bindIP = '0.0.0.0', webPath = "/flash/www" ) : - self._routeHandlers = routeHandlers + self._srvAddr = (bindIP, port) self._webPath = webPath self._notFoundUrl = None @@ -145,6 +188,25 @@ class MicroWebSrv : self.WebSocketStackSize = 4096 self.AcceptWebSocketCallback = None + self._routeHandlers = [] + routeHandlers += self._docoratedRouteHandlers + for route, method, func in routeHandlers : + routeParts = route.split('/') + # -> ['', 'users', '', 'addresses', '', 'test', ''] + routeArgNames = [] + routeRegex = '' + for s in routeParts : + if s.startswith('<') and s.endswith('>') : + routeArgNames.append(s[1:-1]) + routeRegex += '/(\\w*)' + elif s : + routeRegex += '/' + s + routeRegex += '$' + # -> '/users/(\w*)/addresses/(\w*)/test/(\w*)$' + routeRegex = re.compile(routeRegex) + + self._routeHandlers.append(MicroWebSrvRoute(route, method, func, routeArgNames, routeRegex)) + # ============================================================================ # ===( Server Process )======================================================= # ============================================================================ @@ -168,21 +230,31 @@ class MicroWebSrv : # gc.collect() time.sleep_ms(2) continue - except : + except Exception as e: + if not self.isThreaded: + print(e) break self._client(self, client, cliAddr) self._started = False + self._state = "Stoped" + self.thID = None # ============================================================================ # ===( Functions )============================================================ # ============================================================================ - def Start(self, threaded=True) : + def Start(self, threaded=True, stackSize=8192) : if not self._started : +<<<<<<< HEAD # May used at AP mode # if not network.WLAN().isconnected(): # print("WLAN not connected!") # return +======= + if not network.WLAN().wifiactive(): + print("WLAN not connected!") + return +>>>>>>> master gc.collect() self._server = socket.socket( socket.AF_INET, socket.SOCK_STREAM, @@ -196,7 +268,7 @@ class MicroWebSrv : # using non-blocking socket self._server.settimeout(0.5) if threaded : - th = MicroWebSrv._tryStartThread(self._serverProcess) + th = MicroWebSrv._tryStartThread(self._serverProcess, stacksize=stackSize) if th: self.thID = th else : @@ -207,6 +279,8 @@ class MicroWebSrv : def Stop(self) : if self._started : self._server.close() + if self.isThreaded: + _ = _thread.notify(self.thID, _thread.EXIT) # ---------------------------------------------------------------------------- @@ -238,17 +312,30 @@ class MicroWebSrv : return None # ---------------------------------------------------------------------------- - + def GetRouteHandler(self, resUrl, method) : if self._routeHandlers : - resUrl = resUrl.upper() + #resUrl = resUrl.upper() + if resUrl.endswith('/') : + resUrl = resUrl[:-1] method = method.upper() - for route in self._routeHandlers : - if len(route) == 3 and \ - route[0].upper() == resUrl and \ - route[1].upper() == method : - return route[2] - return None + for rh in self._routeHandlers : + if rh.method == method : + m = rh.routeRegex.match(resUrl) + if m : # found matching route? + if rh.routeArgNames : + routeArgs = {} + for i, name in enumerate(rh.routeArgNames) : + value = m.group(i+1) + try : + value = int(value) + except : + pass + routeArgs[name] = value + return (rh.func, routeArgs) + else : + return (rh.func, None) + return (None, None) # ---------------------------------------------------------------------------- @@ -273,7 +360,7 @@ class MicroWebSrv : # ------------------------------------------------------------------------ def __init__(self, microWebSrv, socket, addr) : - socket.settimeout(4) + socket.settimeout(2) self._microWebSrv = microWebSrv self._socket = socket self._addr = addr @@ -286,6 +373,7 @@ class MicroWebSrv : self._headers = { } self._contentType = None self._contentLength = 0 + self._processRequest() # ------------------------------------------------------------------------ @@ -297,9 +385,12 @@ class MicroWebSrv : if self._parseHeader(response) : upg = self._getConnUpgrade() if not upg : - routeHandler = self._microWebSrv.GetRouteHandler(self._resPath, self._method) + routeHandler, routeArgs = self._microWebSrv.GetRouteHandler(self._resPath, self._method) if routeHandler : - routeHandler(self, response) + if routeArgs is not None: + routeHandler(self, response, routeArgs) + else: + routeHandler(self, response) elif self._method.upper() == "GET" : filepath = self._microWebSrv._physPathFromURLPath(self._resPath) if filepath : @@ -471,6 +562,14 @@ class MicroWebSrv : value = MicroWebSrv._unquote(param[1]) if len(param) > 1 else '' res[MicroWebSrv._unquote(param[0])] = value return res + + # ------------------------------------------------------------------------ + + def ReadRequestContentAsJSON(self) : + try : + return loads(self.ReadRequestContent()) + except : + return None # ============================================================================ # ===( Class Response )====================================================== @@ -486,13 +585,15 @@ class MicroWebSrv : # ------------------------------------------------------------------------ def _write(self, data) : + if type(data) == str: + data = data.encode() return self._client._socket.write(data) # ------------------------------------------------------------------------ def _writeFirstLine(self, code) : reason = self._responseCodes.get(code, ('Unknown reason', ))[0] - self._write("HTTP/1.0 %s %s\r\n" % (code, reason)) + self._write("HTTP/1.1 %s %s\r\n" % (code, reason)) # ------------------------------------------------------------------------ @@ -511,6 +612,11 @@ class MicroWebSrv : # ------------------------------------------------------------------------ + def _writeServerHeader(self) : + self._writeHeader("Server", "MicroWebSrv by JC`zic") + + # ------------------------------------------------------------------------ + def _writeEndHeader(self) : self._write("\r\n") @@ -524,9 +630,9 @@ class MicroWebSrv : if contentLength > 0 : self._writeContentTypeHeader(contentType, contentCharset) self._writeHeader("Content-Length", contentLength) - self._writeHeader("Server", "MicroWebSrv by JC`zic") + self._writeServerHeader() self._writeHeader("Connection", "close") - self._writeEndHeader() + self._writeEndHeader() # ------------------------------------------------------------------------ @@ -537,6 +643,8 @@ class MicroWebSrv : if isinstance(headers, dict) : for header in headers : self._writeHeader(header, headers[header]) + self._writeServerHeader() + self._writeEndHeader() # ------------------------------------------------------------------------ @@ -556,9 +664,10 @@ class MicroWebSrv : if 'MicroWebTemplate' in globals() : with open(filepath, 'r') as file : code = file.read() - mWebTmpl = MicroWebTemplate(code, escapeStrFunc=MicroWebSrv.HTMLEscape) + mWebTmpl = MicroWebTemplate(code, escapeStrFunc=MicroWebSrv.HTMLEscape, filepath=filepath) try : - return self.WriteResponseOk(headers, "text/html", "UTF-8", mWebTmpl.Execute()) + tmplResult = mWebTmpl.Execute() + return self.WriteResponse(200, headers, "text/html", "UTF-8", tmplResult) except Exception as ex : return self.WriteResponse( 500, None, @@ -610,7 +719,7 @@ class MicroWebSrv : # ------------------------------------------------------------------------ def WriteResponseJSONOk(self, obj=None, headers=None) : - return self.WriteResponseOk(headers, "application/json", "UTF-8", dumps(obj)) + return self.WriteResponse(200, headers, "application/json", "UTF-8", dumps(obj)) # ------------------------------------------------------------------------ diff --git a/MicroPython_BUILD/components/micropython/esp32/modules/microWebSrv.py.new b/MicroPython_BUILD/components/micropython/esp32/modules/microWebSrv.py.new deleted file mode 100755 index db7f183..0000000 --- a/MicroPython_BUILD/components/micropython/esp32/modules/microWebSrv.py.new +++ /dev/null @@ -1,874 +0,0 @@ -""" -The MIT License (MIT) -Copyright © 2018 Jean-Christophe Bos & HC² (www.hc2.fr) -Copyright © 2018 LoBo (https://github.com/loboris/MicroPython_ESP32_psRAM_LoBo) -""" - - -from json import loads, dumps -from os import stat -import _thread -import network -import time -import socket -import gc -import re - -try : - from microWebTemplate import MicroWebTemplate -except : - pass - -try : - from microWebSocket import MicroWebSocket -except : - pass - -class MicroWebSrvRoute : - def __init__(self, route, method, func, routeArgNames, routeRegex) : - self.route = route - self.method = method - self.func = func - self.routeArgNames = routeArgNames - self.routeRegex = routeRegex - - -class MicroWebSrv : - - # ============================================================================ - # ===( Constants )============================================================ - # ============================================================================ - - _indexPages = [ - "index.pyhtml", - "index.html", - "index.htm", - "default.pyhtml", - "default.html", - "default.htm" - ] - - _mimeTypes = { - ".txt" : "text/plain", - ".htm" : "text/html", - ".html" : "text/html", - ".css" : "text/css", - ".csv" : "text/csv", - ".js" : "application/javascript", - ".xml" : "application/xml", - ".xhtml" : "application/xhtml+xml", - ".json" : "application/json", - ".zip" : "application/zip", - ".pdf" : "application/pdf", - ".jpg" : "image/jpeg", - ".jpeg" : "image/jpeg", - ".png" : "image/png", - ".gif" : "image/gif", - ".svg" : "image/svg+xml", - ".ico" : "image/x-icon" - } - - _html_escape_chars = { - "&" : "&", - '"' : """, - "'" : "'", - ">" : ">", - "<" : "<" - } - - _pyhtmlPagesExt = '.pyhtml' - - # ============================================================================ - # ===( Class globals )======================================================= - # ============================================================================ - - _docoratedRouteHandlers = [] - - # ============================================================================ - # ===( Utils )=============================================================== - # ============================================================================ - - @classmethod - def route(cls, url, method='GET'): - """ Adds a route handler function to the routing list """ - def route_decorator(func): - item = (url, method, func) - cls._docoratedRouteHandlers.append(item) - return func - return route_decorator - - # ---------------------------------------------------------------------------- - - @staticmethod - def HTMLEscape(s) : - return ''.join(MicroWebSrv._html_escape_chars.get(c, c) for c in s) - - # ---------------------------------------------------------------------------- - - @staticmethod - def _tryAllocByteArray(size) : - for x in range(10) : - try : - gc.collect() - return bytearray(size) - except : - pass - return None - - # ---------------------------------------------------------------------------- - - @staticmethod - def _tryStartThread(func, args=()) : - _ = _thread.stack_size(8*1024) - for x in range(10) : - try : - gc.collect() - th = _thread.start_new_thread("MicroWebServer", func, args) - return th - except : - time.sleep_ms(100) - return False - - # ---------------------------------------------------------------------------- - - @staticmethod - def _unquote(s) : - r = s.split('%') - for i in range(1, len(r)) : - s = r[i] - try : - r[i] = chr(int(s[:2], 16)) + s[2:] - except : - r[i] = '%' + s - return ''.join(r) - - # ---------------------------------------------------------------------------- - - @staticmethod - def _unquote_plus(s) : - return MicroWebSrv._unquote(s.replace('+', ' ')) - - # ---------------------------------------------------------------------------- - - @staticmethod - def _fileExists(path) : - try : - stat(path) - return True - except : - return False - - # ---------------------------------------------------------------------------- - - @staticmethod - def _isPyHTMLFile(filename) : - return filename.lower().endswith(MicroWebSrv._pyhtmlPagesExt) - - # ============================================================================ - # ===( Constructor )========================================================== - # ============================================================================ - - def __init__( self, - routeHandlers = [], - port = 80, - bindIP = '0.0.0.0', - webPath = "/flash/www" ) : - - self._srvAddr = (bindIP, port) - self._webPath = webPath - self._notFoundUrl = None - self._started = False - self.thID = None - self.isThreaded = False - self._state = "Stoped" - - self.MaxWebSocketRecvLen = 1024 - self.WebSocketThreaded = True - self.WebSocketStackSize = 4096 - self.AcceptWebSocketCallback = None - - self._routeHandlers = [] - routeHandlers += self._docoratedRouteHandlers - for route, method, func in routeHandlers : - routeParts = route.split('/') - # -> ['', 'users', '', 'addresses', '', 'test', ''] - routeArgNames = [] - routeRegex = '' - for s in routeParts : - if s.startswith('<') and s.endswith('>') : - routeArgNames.append(s[1:-1]) - routeRegex += '/(\\w*)' - elif s : - routeRegex += '/' + s - routeRegex += '$' - # -> '/users/(\w*)/addresses/(\w*)/test/(\w*)$' - routeRegex = re.compile(routeRegex) - - self._routeHandlers.append(MicroWebSrvRoute(route, method, func, routeArgNames, routeRegex)) - - # ============================================================================ - # ===( Server Process )======================================================= - # ============================================================================ - - def _serverProcess(self) : - self._started = True - self._state = "Running" - while True : - try : - client, cliAddr = self._server.accepted() - if client == None: - if self.isThreaded: - notify = _thread.getnotification() - if notify == _thread.EXIT: - break - elif notify == _thread.SUSPEND: - self._state = "Suspended" - while _thread.wait() != _thread.RESUME: - pass - self._state = "Running" - # gc.collect() - time.sleep_ms(2) - continue - except Exception as e: - if not self.isThreaded: - print(e) - break - self._client(self, client, cliAddr) - self._started = False - self._state = "Stoped" - - # ============================================================================ - # ===( Functions )============================================================ - # ============================================================================ - - def Start(self, threaded=True) : - if not self._started : - if not network.WLAN().isconnected(): - print("WLAN not connected!") - return - gc.collect() - self._server = socket.socket( socket.AF_INET, - socket.SOCK_STREAM, - socket.IPPROTO_TCP ) - self._server.setsockopt( socket.SOL_SOCKET, - socket.SO_REUSEADDR, - 1 ) - self._server.bind(self._srvAddr) - self._server.listen(1) - self.isThreaded = threaded - # using non-blocking socket - self._server.settimeout(0.5) - if threaded : - th = MicroWebSrv._tryStartThread(self._serverProcess) - if th: - self.thID = th - else : - self._serverProcess() - - # ---------------------------------------------------------------------------- - - def Stop(self) : - if self._started : - self._server.close() - - # ---------------------------------------------------------------------------- - - def IsStarted(self) : - return self._started - - # ---------------------------------------------------------------------------- - - def threadID(self) : - return self.thID - - # ---------------------------------------------------------------------------- - - def State(self) : - return self._state - - # ---------------------------------------------------------------------------- - - def SetNotFoundPageUrl(self, url=None) : - self._notFoundUrl = url - - # ---------------------------------------------------------------------------- - - def GetMimeTypeFromFilename(self, filename) : - filename = filename.lower() - for ext in self._mimeTypes : - if filename.endswith(ext) : - return self._mimeTypes[ext] - return None - - # ---------------------------------------------------------------------------- - - def GetRouteHandler(self, resUrl, method) : - if self._routeHandlers : - #resUrl = resUrl.upper() - if resUrl.endswith('/') : - resUrl = resUrl[:-1] - method = method.upper() - for rh in self._routeHandlers : - if rh.method == method : - m = rh.routeRegex.match(resUrl) - if m : # found matching route? - if rh.routeArgNames : - routeArgs = {} - for i, name in enumerate(rh.routeArgNames) : - value = m.group(i+1) - try : - value = int(value) - except : - pass - routeArgs[name] = value - return (rh.func, routeArgs) - else : - return (rh.func, None) - return (None, None) - - # ---------------------------------------------------------------------------- - - def _physPathFromURLPath(self, urlPath) : - if urlPath == '/' : - for idxPage in self._indexPages : - physPath = self._webPath + '/' + idxPage - if MicroWebSrv._fileExists(physPath) : - return physPath - else : - physPath = self._webPath + urlPath - if MicroWebSrv._fileExists(physPath) : - return physPath - return None - - # ============================================================================ - # ===( Class Client )======================================================== - # ============================================================================ - - class _client : - - # ------------------------------------------------------------------------ - - def __init__(self, microWebSrv, socket, addr) : - socket.settimeout(2) - self._microWebSrv = microWebSrv - self._socket = socket - self._addr = addr - self._method = None - self._path = None - self._httpVer = None - self._resPath = "/" - self._queryString = "" - self._queryParams = { } - self._headers = { } - self._contentType = None - self._contentLength = 0 - - self._processRequest() - - # ------------------------------------------------------------------------ - - def _processRequest(self) : - try : - response = MicroWebSrv._response(self) - if self._parseFirstLine(response) : - if self._parseHeader(response) : - upg = self._getConnUpgrade() - if not upg : - routeHandler, routeArgs = self._microWebSrv.GetRouteHandler(self._resPath, self._method) - if routeHandler : - if routeArgs is not None: - routeHandler(self, response, routeArgs) - else: - routeHandler(self, response) - elif self._method.upper() == "GET" : - filepath = self._microWebSrv._physPathFromURLPath(self._resPath) - if filepath : - if MicroWebSrv._isPyHTMLFile(filepath) : - response.WriteResponsePyHTMLFile(filepath) - else : - contentType = self._microWebSrv.GetMimeTypeFromFilename(filepath) - if contentType : - response.WriteResponseFile(filepath, contentType) - else : - response.WriteResponseForbidden() - else : - response.WriteResponseNotFound() - else : - response.WriteResponseMethodNotAllowed() - elif upg == 'websocket' and 'MicroWebSocket' in globals() \ - and self._microWebSrv.AcceptWebSocketCallback : - MicroWebSocket( socket = self._socket, - httpClient = self, - httpResponse = response, - maxRecvLen = self._microWebSrv.MaxWebSocketRecvLen, - threaded = self._microWebSrv.WebSocketThreaded, - acceptCallback = self._microWebSrv.AcceptWebSocketCallback, - stackSize = self._microWebSrv.WebSocketStackSize ) - return - else : - response.WriteResponseNotImplemented() - else : - response.WriteResponseBadRequest() - except : - response.WriteResponseInternalServerError() - try : - self._socket.close() - except : - pass - - # ------------------------------------------------------------------------ - - def _parseFirstLine(self, response) : - try : - elements = self._socket.readline().decode().strip().split() - if len(elements) == 3 : - self._method = elements[0].upper() - self._path = elements[1] - self._httpVer = elements[2].upper() - elements = self._path.split('?', 1) - if len(elements) > 0 : - self._resPath = MicroWebSrv._unquote_plus(elements[0]) - if len(elements) > 1 : - self._queryString = elements[1] - elements = self._queryString.split('&') - for s in elements : - param = s.split('=', 1) - if len(param) > 0 : - value = MicroWebSrv._unquote(param[1]) if len(param) > 1 else '' - self._queryParams[MicroWebSrv._unquote(param[0])] = value - return True - except : - pass - return False - - # ------------------------------------------------------------------------ - - def _parseHeader(self, response) : - while True : - elements = self._socket.readline().decode().strip().split(':', 1) - if len(elements) == 2 : - self._headers[elements[0].strip()] = elements[1].strip() - elif len(elements) == 1 and len(elements[0]) == 0 : - if self._method == 'POST' : - self._contentType = self._headers.get("Content-Type", None) - self._contentLength = int(self._headers.get("Content-Length", 0)) - return True - else : - return False - - # ------------------------------------------------------------------------ - - def _getConnUpgrade(self) : - if 'upgrade' in self._headers.get('Connection', '').lower() : - return self._headers.get('Upgrade', '').lower() - return None - - # ------------------------------------------------------------------------ - - def GetServer(self) : - return self._microWebSrv - - # ------------------------------------------------------------------------ - - def GetAddr(self) : - return self._addr - - # ------------------------------------------------------------------------ - - def GetIPAddr(self) : - return self._addr[0] - - # ------------------------------------------------------------------------ - - def GetPort(self) : - return self._addr[1] - - # ------------------------------------------------------------------------ - - def GetRequestMethod(self) : - return self._method - - # ------------------------------------------------------------------------ - - def GetRequestTotalPath(self) : - return self._path - - # ------------------------------------------------------------------------ - - def GetRequestPath(self) : - return self._resPath - - # ------------------------------------------------------------------------ - - def GetRequestQueryString(self) : - return self._queryString - - # ------------------------------------------------------------------------ - - def GetRequestQueryParams(self) : - return self._queryParams - - # ------------------------------------------------------------------------ - - def GetRequestHeaders(self) : - return self._headers - - # ------------------------------------------------------------------------ - - def GetRequestContentType(self) : - return self._contentType - - # ------------------------------------------------------------------------ - - def GetRequestContentLength(self) : - return self._contentLength - - # ------------------------------------------------------------------------ - - def ReadRequestContent(self, size=None) : - self._socket.setblocking(False) - b = None - try : - if not size : - b = self._socket.read(self._contentLength) - elif size > 0 : - b = self._socket.read(size) - except : - pass - self._socket.setblocking(True) - return b if b else b'' - - # ------------------------------------------------------------------------ - - def ReadRequestPostedFormData(self) : - res = { } - data = self.ReadRequestContent() - if len(data) > 0 : - elements = data.decode().split('&') - for s in elements : - param = s.split('=', 1) - if len(param) > 0 : - value = MicroWebSrv._unquote(param[1]) if len(param) > 1 else '' - res[MicroWebSrv._unquote(param[0])] = value - return res - - # ------------------------------------------------------------------------ - - def ReadRequestContentAsJSON(self) : - try : - return loads(self.ReadRequestContent()) - except : - return None - - # ============================================================================ - # ===( Class Response )====================================================== - # ============================================================================ - - class _response : - - # ------------------------------------------------------------------------ - - def __init__(self, client) : - self._client = client - - # ------------------------------------------------------------------------ - - def _write(self, data) : - if type(data) == str: - data = data.encode() - return self._client._socket.write(data) - - # ------------------------------------------------------------------------ - - def _writeFirstLine(self, code) : - reason = self._responseCodes.get(code, ('Unknown reason', ))[0] - self._write("HTTP/1.1 %s %s\r\n" % (code, reason)) - - # ------------------------------------------------------------------------ - - def _writeHeader(self, name, value) : - self._write("%s: %s\r\n" % (name, value)) - - # ------------------------------------------------------------------------ - - def _writeContentTypeHeader(self, contentType, charset=None) : - if contentType : - ct = contentType \ - + (("; charset=%s" % charset) if charset else "") - else : - ct = "application/octet-stream" - self._writeHeader("Content-Type", ct) - - # ------------------------------------------------------------------------ - - def _writeServerHeader(self) : - self._writeHeader("Server", "MicroWebSrv by JC`zic") - - # ------------------------------------------------------------------------ - - def _writeEndHeader(self) : - self._write("\r\n") - - # ------------------------------------------------------------------------ - - def _writeBeforeContent(self, code, headers, contentType, contentCharset, contentLength) : - self._writeFirstLine(code) - if isinstance(headers, dict) : - for header in headers : - self._writeHeader(header, headers[header]) - if contentLength > 0 : - self._writeContentTypeHeader(contentType, contentCharset) - self._writeHeader("Content-Length", contentLength) - self._writeServerHeader() - self._writeHeader("Connection", "close") - self._writeEndHeader() - - # ------------------------------------------------------------------------ - - def WriteSwitchProto(self, upgrade, headers=None) : - self._writeFirstLine(101) - self._writeHeader("Connection", "Upgrade") - self._writeHeader("Upgrade", upgrade) - if isinstance(headers, dict) : - for header in headers : - self._writeHeader(header, headers[header]) - self._writeServerHeader() - self._writeEndHeader() - - # ------------------------------------------------------------------------ - - def WriteResponse(self, code, headers, contentType, contentCharset, content) : - try : - contentLength = len(content) if content else 0 - self._writeBeforeContent(code, headers, contentType, contentCharset, contentLength) - if contentLength > 0 : - self._write(content) - return True - except : - return False - - # ------------------------------------------------------------------------ - - def WriteResponsePyHTMLFile(self, filepath, headers=None) : - if 'MicroWebTemplate' in globals() : - with open(filepath, 'r') as file : - code = file.read() - mWebTmpl = MicroWebTemplate(code, escapeStrFunc=MicroWebSrv.HTMLEscape, filepath=filepath) - try : - tmplResult = mWebTmpl.Execute() - return self.WriteResponse(200, headers, "text/html", "UTF-8", tmplResult) - except Exception as ex : - return self.WriteResponse( 500, - None, - "text/html", - "UTF-8", - self._execErrCtnTmpl % { - 'module' : 'PyHTML', - 'message' : str(ex) - } ) - return self.WriteResponseNotImplemented() - - # ------------------------------------------------------------------------ - - def WriteResponseFile(self, filepath, contentType=None, headers=None) : - try : - size = stat(filepath)[6] - if size > 0 : - with open(filepath, 'rb') as file : - self._writeBeforeContent(200, headers, contentType, None, size) - buf = MicroWebSrv._tryAllocByteArray(1024) - if buf : - while size > 0 : - x = file.readinto(buf) - if x < len(buf) : - buf = memoryview(buf)[:x] - self._write(buf) - size -= x - return True - self.WriteResponseInternalServerError() - return False - except : - pass - self.WriteResponseNotFound() - return False - - # ------------------------------------------------------------------------ - - def WriteResponseFileAttachment(self, filepath, attachmentName, headers=None) : - if not isinstance(headers, dict) : - headers = { } - headers["Content-Disposition"] = "attachment; filename=\"%s\"" % attachmentName - return self.WriteResponseFile(filepath, None, headers) - - # ------------------------------------------------------------------------ - - def WriteResponseOk(self, headers=None, contentType=None, contentCharset=None, content=None) : - return self.WriteResponse(200, headers, contentType, contentCharset, content) - - # ------------------------------------------------------------------------ - - def WriteResponseJSONOk(self, obj=None, headers=None) : - return self.WriteResponse(200, headers, "application/json", "UTF-8", dumps(obj)) - - # ------------------------------------------------------------------------ - - def WriteResponseRedirect(self, location) : - headers = { "Location" : location } - return self.WriteResponse(302, headers, None, None, None) - - # ------------------------------------------------------------------------ - - def WriteResponseError(self, code) : - responseCode = self._responseCodes.get(code, ('Unknown reason', '')) - return self.WriteResponse( code, - None, - "text/html", - "UTF-8", - self._errCtnTmpl % { - 'code' : code, - 'reason' : responseCode[0], - 'message' : responseCode[1] - } ) - - # ------------------------------------------------------------------------ - - def WriteResponseJSONError(self, code, obj=None) : - return self.WriteResponse( code, - None, - "application/json", - "UTF-8", - dumps(obj if obj else { }) ) - - # ------------------------------------------------------------------------ - - def WriteResponseBadRequest(self) : - return self.WriteResponseError(400) - - # ------------------------------------------------------------------------ - - def WriteResponseForbidden(self) : - return self.WriteResponseError(403) - - # ------------------------------------------------------------------------ - - def WriteResponseNotFound(self) : - if self._client._microWebSrv._notFoundUrl : - self.WriteResponseRedirect(self._client._microWebSrv._notFoundUrl) - else : - return self.WriteResponseError(404) - - # ------------------------------------------------------------------------ - - def WriteResponseMethodNotAllowed(self) : - return self.WriteResponseError(405) - - # ------------------------------------------------------------------------ - - def WriteResponseInternalServerError(self) : - return self.WriteResponseError(500) - - # ------------------------------------------------------------------------ - - def WriteResponseNotImplemented(self) : - return self.WriteResponseError(501) - - # ------------------------------------------------------------------------ - - _errCtnTmpl = """\ - - - Error - - -

%(code)d %(reason)s

- %(message)s - - - """ - - # ------------------------------------------------------------------------ - - _execErrCtnTmpl = """\ - - - Page execution error - - -

%(module)s page execution error

- %(message)s - - - """ - - # ------------------------------------------------------------------------ - - _responseCodes = { - 100: ('Continue', 'Request received, please continue'), - 101: ('Switching Protocols', - 'Switching to new protocol; obey Upgrade header'), - - 200: ('OK', 'Request fulfilled, document follows'), - 201: ('Created', 'Document created, URL follows'), - 202: ('Accepted', - 'Request accepted, processing continues off-line'), - 203: ('Non-Authoritative Information', 'Request fulfilled from cache'), - 204: ('No Content', 'Request fulfilled, nothing follows'), - 205: ('Reset Content', 'Clear input form for further input.'), - 206: ('Partial Content', 'Partial content follows.'), - - 300: ('Multiple Choices', - 'Object has several resources -- see URI list'), - 301: ('Moved Permanently', 'Object moved permanently -- see URI list'), - 302: ('Found', 'Object moved temporarily -- see URI list'), - 303: ('See Other', 'Object moved -- see Method and URL list'), - 304: ('Not Modified', - 'Document has not changed since given time'), - 305: ('Use Proxy', - 'You must use proxy specified in Location to access this ' - 'resource.'), - 307: ('Temporary Redirect', - 'Object moved temporarily -- see URI list'), - - 400: ('Bad Request', - 'Bad request syntax or unsupported method'), - 401: ('Unauthorized', - 'No permission -- see authorization schemes'), - 402: ('Payment Required', - 'No payment -- see charging schemes'), - 403: ('Forbidden', - 'Request forbidden -- authorization will not help'), - 404: ('Not Found', 'Nothing matches the given URI'), - 405: ('Method Not Allowed', - 'Specified method is invalid for this resource.'), - 406: ('Not Acceptable', 'URI not available in preferred format.'), - 407: ('Proxy Authentication Required', 'You must authenticate with ' - 'this proxy before proceeding.'), - 408: ('Request Timeout', 'Request timed out; try again later.'), - 409: ('Conflict', 'Request conflict.'), - 410: ('Gone', - 'URI no longer exists and has been permanently removed.'), - 411: ('Length Required', 'Client must specify Content-Length.'), - 412: ('Precondition Failed', 'Precondition in headers is false.'), - 413: ('Request Entity Too Large', 'Entity is too large.'), - 414: ('Request-URI Too Long', 'URI is too long.'), - 415: ('Unsupported Media Type', 'Entity body in unsupported format.'), - 416: ('Requested Range Not Satisfiable', - 'Cannot satisfy request range.'), - 417: ('Expectation Failed', - 'Expect condition could not be satisfied.'), - - 500: ('Internal Server Error', 'Server got itself in trouble'), - 501: ('Not Implemented', - 'Server does not support this operation'), - 502: ('Bad Gateway', 'Invalid responses from another server/proxy.'), - 503: ('Service Unavailable', - 'The server cannot process the request due to a high load'), - 504: ('Gateway Timeout', - 'The gateway server did not receive a timely response'), - 505: ('HTTP Version Not Supported', 'Cannot fulfill request.'), - } - - # ============================================================================ - # ============================================================================ - # ============================================================================ - diff --git a/MicroPython_BUILD/components/micropython/esp32/modules/microWebTemplate.py b/MicroPython_BUILD/components/micropython/esp32/modules/microWebTemplate.py old mode 100644 new mode 100755 index 35dcfaf..8ace5ca --- a/MicroPython_BUILD/components/micropython/esp32/modules/microWebTemplate.py +++ b/MicroPython_BUILD/components/micropython/esp32/modules/microWebTemplate.py @@ -1,3 +1,7 @@ +""" +The MIT License (MIT) +Copyright © 2018 Jean-Christophe Bos & HC² (www.hc2.fr) +""" import re @@ -18,14 +22,16 @@ class MicroWebTemplate : INSTRUCTION_ELSE = 'else' INSTRUCTION_FOR = 'for' INSTRUCTION_END = 'end' + INSTRUCTION_INCLUDE = 'include' # ============================================================================ # ===( Constructor )========================================================== # ============================================================================ - def __init__(self, code, escapeStrFunc=None) : + def __init__(self, code, escapeStrFunc=None, filepath='') : self._code = code self._escapeStrFunc = escapeStrFunc + self._filepath = filepath self._pos = 0 self._endPos = len(code)-1 self._line = 1 @@ -40,6 +46,7 @@ class MicroWebTemplate : MicroWebTemplate.INSTRUCTION_ELSE : self._processInstructionELSE, MicroWebTemplate.INSTRUCTION_FOR : self._processInstructionFOR, MicroWebTemplate.INSTRUCTION_END : self._processInstructionEND, + MicroWebTemplate.INSTRUCTION_INCLUDE: self._processInstructionINCLUDE, } # ============================================================================ @@ -81,7 +88,7 @@ class MicroWebTemplate : while self._pos <= self._endPos : c = self._code[self._pos] if c == MicroWebTemplate.TOKEN_OPEN[0] and \ - self._code[ self._pos : self._pos + MicroWebTemplate.TOKEN_OPEN_LEN ] == MicroWebTemplate.TOKEN_OPEN : + self._code[ self._pos : self._pos + MicroWebTemplate.TOKEN_OPEN_LEN ] == MicroWebTemplate.TOKEN_OPEN : self._pos += MicroWebTemplate.TOKEN_OPEN_LEN tokenContent = '' x = self._pos @@ -264,7 +271,7 @@ class MicroWebTemplate : try : result = eval(expression, self._pyGlobalVars, self._pyLocalVars) except : - raise Exception('%s (line %s)' % (str(ex), self._line)) + raise Exception('%s (line %s)' % (str(expression), self._line)) if execute and len(result) > 0 : for x in result : self._pyLocalVars[identifier] = x @@ -294,6 +301,21 @@ class MicroWebTemplate : % (MicroWebTemplate.INSTRUCTION_END, self._line) ) return MicroWebTemplate.INSTRUCTION_END + # ---------------------------------------------------------------------------- + + def _processInstructionINCLUDE(self, instructionBody, execute) : + if not instructionBody : + raise Exception( '"%s" alone is an incomplete syntax (line %s)' % (MicroWebTemplate.INSTRUCTION_INCLUDE, self._line) ) + filename = instructionBody.replace('"','').replace("'",'').strip() + idx = self._filepath.rindex('/') + if idx >= 0 : + filename = self._filepath[:idx+1] + filename + with open(filename, 'r') as file : + includeCode = file.read() + + self._code = self._code[:self._pos] + includeCode + self._code[self._pos:] + self._endPos += len(includeCode) + # ============================================================================ # ============================================================================ # ============================================================================ diff --git a/MicroPython_BUILD/components/micropython/esp32/modules/microWebTemplate.py.new b/MicroPython_BUILD/components/micropython/esp32/modules/microWebTemplate.py.new deleted file mode 100755 index 8ace5ca..0000000 --- a/MicroPython_BUILD/components/micropython/esp32/modules/microWebTemplate.py.new +++ /dev/null @@ -1,321 +0,0 @@ -""" -The MIT License (MIT) -Copyright © 2018 Jean-Christophe Bos & HC² (www.hc2.fr) -""" - -import re - -class MicroWebTemplate : - - # ============================================================================ - # ===( Constants )============================================================ - # ============================================================================ - - TOKEN_OPEN = '{{' - TOKEN_CLOSE = '}}' - TOKEN_OPEN_LEN = len(TOKEN_OPEN) - TOKEN_CLOSE_LEN = len(TOKEN_CLOSE) - - INSTRUCTION_PYTHON = 'py' - INSTRUCTION_IF = 'if' - INSTRUCTION_ELIF = 'elif' - INSTRUCTION_ELSE = 'else' - INSTRUCTION_FOR = 'for' - INSTRUCTION_END = 'end' - INSTRUCTION_INCLUDE = 'include' - - # ============================================================================ - # ===( Constructor )========================================================== - # ============================================================================ - - def __init__(self, code, escapeStrFunc=None, filepath='') : - self._code = code - self._escapeStrFunc = escapeStrFunc - self._filepath = filepath - self._pos = 0 - self._endPos = len(code)-1 - self._line = 1 - self._reIdentifier = re.compile(r'[a-zA-Z_][a-zA-Z0-9_]*$') - self._pyGlobalVars = { } - self._pyLocalVars = { } - self._rendered = '' - self._instructions = { - MicroWebTemplate.INSTRUCTION_PYTHON : self._processInstructionPYTHON, - MicroWebTemplate.INSTRUCTION_IF : self._processInstructionIF, - MicroWebTemplate.INSTRUCTION_ELIF : self._processInstructionELIF, - MicroWebTemplate.INSTRUCTION_ELSE : self._processInstructionELSE, - MicroWebTemplate.INSTRUCTION_FOR : self._processInstructionFOR, - MicroWebTemplate.INSTRUCTION_END : self._processInstructionEND, - MicroWebTemplate.INSTRUCTION_INCLUDE: self._processInstructionINCLUDE, - } - - # ============================================================================ - # ===( Functions )============================================================ - # ============================================================================ - - def Validate(self) : - try : - self._parseCode(execute=False) - return None - except Exception as ex : - return str(ex) - - # ---------------------------------------------------------------------------- - - def Execute(self) : - try : - self._parseCode(execute=True) - return self._rendered - except Exception as ex : - raise Exception(str(ex)) - - # ============================================================================ - # ===( Utils )=============================================================== - # ============================================================================ - - def _parseCode(self, execute) : - self._pyGlobalVars = { } - self._pyLocalVars = { } - self._rendered = '' - newTokenToProcess = self._parseBloc(execute) - if newTokenToProcess is not None : - raise Exception( '"%s" instruction is not valid here (line %s)' - % (newTokenToProcess, self._line) ) - - # ---------------------------------------------------------------------------- - - def _parseBloc(self, execute) : - while self._pos <= self._endPos : - c = self._code[self._pos] - if c == MicroWebTemplate.TOKEN_OPEN[0] and \ - self._code[ self._pos : self._pos + MicroWebTemplate.TOKEN_OPEN_LEN ] == MicroWebTemplate.TOKEN_OPEN : - self._pos += MicroWebTemplate.TOKEN_OPEN_LEN - tokenContent = '' - x = self._pos - while True : - if x > self._endPos : - raise Exception("%s is missing (line %s)" % (MicroWebTemplate.TOKEN_CLOSE, self._line)) - c = self._code[x] - if c == MicroWebTemplate.TOKEN_CLOSE[0] and \ - self._code[ x : x + MicroWebTemplate.TOKEN_CLOSE_LEN ] == MicroWebTemplate.TOKEN_CLOSE : - self._pos = x + MicroWebTemplate.TOKEN_CLOSE_LEN - break - elif c == '\n' : - self._line += 1 - tokenContent += c - x += 1 - newTokenToProcess = self._processToken(tokenContent, execute) - if newTokenToProcess is not None : - return newTokenToProcess - continue - elif c == '\n' : - self._line += 1 - if execute : - self._rendered += c - self._pos += 1 - return None - - # ---------------------------------------------------------------------------- - - def _processToken(self, tokenContent, execute) : - tokenContent = tokenContent.strip() - parts = tokenContent.split(' ', 1) - instructName = parts[0].strip() - instructBody = parts[1].strip() if len(parts) > 1 else None - if len(instructName) == 0 : - raise Exception( '"%s %s" : instruction is missing (line %s)' - % (MicroWebTemplate.TOKEN_OPEN, MicroWebTemplate.TOKEN_CLOSE, self._line) ) - newTokenToProcess = None - if instructName in self._instructions : - newTokenToProcess = self._instructions[instructName](instructBody, execute) - elif execute : - try : - s = str( eval( tokenContent, - self._pyGlobalVars, - self._pyLocalVars ) ) - if (self._escapeStrFunc is not None) : - self._rendered += self._escapeStrFunc(s) - else : - self._rendered += s - except Exception as ex : - raise Exception('%s (line %s)' % (str(ex), self._line)) - return newTokenToProcess - - # ---------------------------------------------------------------------------- - - def _processInstructionPYTHON(self, instructionBody, execute) : - if instructionBody is not None : - raise Exception( 'Instruction "%s" is invalid (line %s)' - % (MicroWebTemplate.INSTRUCTION_PYTHON, self._line) ) - pyCode = '' - while True : - if self._pos > self._endPos : - raise Exception( '"%s" instruction is missing (line %s)' - % (MicroWebTemplate.INSTRUCTION_END, self._line) ) - c = self._code[self._pos] - if c == MicroWebTemplate.TOKEN_OPEN[0] and \ - self._code[ self._pos : self._pos + MicroWebTemplate.TOKEN_OPEN_LEN ] == MicroWebTemplate.TOKEN_OPEN : - self._pos += MicroWebTemplate.TOKEN_OPEN_LEN - tokenContent = '' - x = self._pos - while True : - if x > self._endPos : - raise Exception("%s is missing (line %s)" % (MicroWebTemplate.TOKEN_CLOSE, self._line)) - c = self._code[x] - if c == MicroWebTemplate.TOKEN_CLOSE[0] and \ - self._code[ x : x + MicroWebTemplate.TOKEN_CLOSE_LEN ] == MicroWebTemplate.TOKEN_CLOSE : - self._pos = x + MicroWebTemplate.TOKEN_CLOSE_LEN - break - elif c == '\n' : - self._line += 1 - tokenContent += c - x += 1 - tokenContent = tokenContent.strip() - if tokenContent == MicroWebTemplate.INSTRUCTION_END : - break - raise Exception( '"%s" is a bad instruction in a python bloc (line %s)' - % (tokenContent, self._line) ) - elif c == '\n' : - self._line += 1 - if execute : - pyCode += c - self._pos += 1 - if execute : - lines = pyCode.split('\n') - indent = '' - for line in lines : - if len(line.strip()) > 0 : - for c in line : - if c == ' ' or c == '\t' : - indent += c - else : - break - break - pyCode = '' - for line in lines : - if line.find(indent) == 0 : - line = line[len(indent):] - pyCode += line + '\n' - try : - exec(pyCode, self._pyGlobalVars, self._pyLocalVars) - except Exception as ex : - raise Exception('%s (line %s)' % (str(ex), self._line)) - return None - - # ---------------------------------------------------------------------------- - - def _processInstructionIF(self, instructionBody, execute) : - if instructionBody is not None : - if execute : - try : - result = eval(instructionBody, self._pyGlobalVars, self._pyLocalVars) - if not isinstance(result, bool) : - raise Exception('"%s" is not a boolean expression (line %s)' % (instructionBody, self._line)) - except Exception as ex : - raise Exception('%s (line %s)' % (str(ex), self._line)) - else : - result = False - newTokenToProcess = self._parseBloc(execute and result) - if newTokenToProcess is not None : - if newTokenToProcess == MicroWebTemplate.INSTRUCTION_END : - return None - elif newTokenToProcess == MicroWebTemplate.INSTRUCTION_ELSE : - newTokenToProcess = self._parseBloc(execute and not result) - if newTokenToProcess is not None : - if newTokenToProcess == MicroWebTemplate.INSTRUCTION_END : - return None - raise Exception( '"%s" instruction waited (line %s)' - % (MicroWebTemplate.INSTRUCTION_END, self._line) ) - raise Exception( '"%s" instruction is missing (line %s)' - % (MicroWebTemplate.INSTRUCTION_END, self._line) ) - elif newTokenToProcess == MicroWebTemplate.INSTRUCTION_ELIF : - self._processInstructionIF(self._elifInstructionBody, execute and not result) - return None - raise Exception( '"%s" instruction waited (line %s)' - % (MicroWebTemplate.INSTRUCTION_END, self._line) ) - raise Exception( '"%s" instruction is missing (line %s)' - % (MicroWebTemplate.INSTRUCTION_END, self._line) ) - raise Exception( '"%s" alone is an incomplete syntax (line %s)' - % (MicroWebTemplate.INSTRUCTION_IF, self._line) ) - - # ---------------------------------------------------------------------------- - - def _processInstructionELIF(self, instructionBody, execute) : - if instructionBody is None : - raise Exception( '"%s" alone is an incomplete syntax (line %s)' - % (MicroWebTemplate.INSTRUCTION_ELIF, self._line) ) - self._elifInstructionBody = instructionBody - return MicroWebTemplate.INSTRUCTION_ELIF - - # ---------------------------------------------------------------------------- - - def _processInstructionELSE(self, instructionBody, execute) : - if instructionBody is not None : - raise Exception( 'Instruction "%s" is invalid (line %s)' - % (MicroWebTemplate.INSTRUCTION_ELSE, self._line) ) - return MicroWebTemplate.INSTRUCTION_ELSE - - # ---------------------------------------------------------------------------- - - def _processInstructionFOR(self, instructionBody, execute) : - if instructionBody is not None : - parts = instructionBody.split(' ', 1) - identifier = parts[0].strip() - if self._reIdentifier.match(identifier) is not None and len(parts) > 1 : - parts = parts[1].strip().split(' ', 1) - if parts[0] == 'in' and len(parts) > 1 : - expression = parts[1].strip() - newTokenToProcess = None - beforePos = self._pos - if execute : - try : - result = eval(expression, self._pyGlobalVars, self._pyLocalVars) - except : - raise Exception('%s (line %s)' % (str(expression), self._line)) - if execute and len(result) > 0 : - for x in result : - self._pyLocalVars[identifier] = x - self._pos = beforePos - newTokenToProcess = self._parseBloc(True) - if newTokenToProcess != MicroWebTemplate.INSTRUCTION_END : - break - else : - newTokenToProcess = self._parseBloc(False) - if newTokenToProcess is not None : - if newTokenToProcess == MicroWebTemplate.INSTRUCTION_END : - return None - raise Exception( '"%s" instruction waited (line %s)' - % (MicroWebTemplate.INSTRUCTION_END, self._line) ) - raise Exception( '"%s" instruction is missing (line %s)' - % (MicroWebTemplate.INSTRUCTION_END, self._line) ) - raise Exception( '"%s %s" is an invalid syntax' - % (MicroWebTemplate.INSTRUCTION_FOR, instructionBody) ) - raise Exception( '"%s" alone is an incomplete syntax (line %s)' - % (MicroWebTemplate.INSTRUCTION_FOR, self._line) ) - - # ---------------------------------------------------------------------------- - - def _processInstructionEND(self, instructionBody, execute) : - if instructionBody is not None : - raise Exception( 'Instruction "%s" is invalid (line %s)' - % (MicroWebTemplate.INSTRUCTION_END, self._line) ) - return MicroWebTemplate.INSTRUCTION_END - - # ---------------------------------------------------------------------------- - - def _processInstructionINCLUDE(self, instructionBody, execute) : - if not instructionBody : - raise Exception( '"%s" alone is an incomplete syntax (line %s)' % (MicroWebTemplate.INSTRUCTION_INCLUDE, self._line) ) - filename = instructionBody.replace('"','').replace("'",'').strip() - idx = self._filepath.rindex('/') - if idx >= 0 : - filename = self._filepath[:idx+1] + filename - with open(filename, 'r') as file : - includeCode = file.read() - - self._code = self._code[:self._pos] + includeCode + self._code[self._pos:] - self._endPos += len(includeCode) - - # ============================================================================ - # ============================================================================ - # ============================================================================ diff --git a/MicroPython_BUILD/components/micropython/esp32/modules_examples/uftpserver.py b/MicroPython_BUILD/components/micropython/esp32/modules_examples/uftpserver.py index b4eee5d..d3ce4c1 100644 --- a/MicroPython_BUILD/components/micropython/esp32/modules_examples/uftpserver.py +++ b/MicroPython_BUILD/components/micropython/esp32/modules_examples/uftpserver.py @@ -148,8 +148,8 @@ def ftpserver(timeout = 300, inthread = False, prnip = True): if prnip: print ("Starting ftp server. Version 1.2") - if not network.WLAN().isconnected(): - print("Not connected!") + if not network.WLAN().wifiactive(): + print("WiFi not started!") return DATA_PORT = 1050 diff --git a/MicroPython_BUILD/components/micropython/esp32/modules_examples/webserver/webserver_example.py b/MicroPython_BUILD/components/micropython/esp32/modules_examples/webserver/webserver_example.py index 047d430..588fe0e 100644 --- a/MicroPython_BUILD/components/micropython/esp32/modules_examples/webserver/webserver_example.py +++ b/MicroPython_BUILD/components/micropython/esp32/modules_examples/webserver/webserver_example.py @@ -1,16 +1,40 @@ -from microWebSrv import MicroWebSrv +from microWebSrv import MicroWebSrv import _thread # set to True to print WebSocket messages WS_messages = False -# ---------------------------------------------------------------------------- +# ================================================= +# Recommended configuration: +# - run microWebServer in thread +# - do NOT run MicroWebSocket in thread +# ================================================= +# Run microWebServer in thread +srv_run_in_thread = True +# Run microWebSocket in thread +ws_run_in_thread = False + +# ---------------------------------------------------------------------- +# To test *.pyhtml* rendered pages goto /test.pyhtml in your browser +# ---------------------------------------------------------------------- + +# ------------------------------------------------------------ +# To test websocket page goto /wstest.html in your browser +# ------------------------------------------------------------ + + +# ----------------------------------------------------- +# Define microWebServer route handlers using decorators +# ----------------------------------------------------- + +# /TEST +@MicroWebSrv.route('/TEST') def _httpHandlerTestGet(httpClient, httpResponse) : content = """\ - + TEST GET @@ -32,13 +56,14 @@ def _httpHandlerTestGet(httpClient, httpResponse) : contentCharset = "UTF-8", content = content ) +@MicroWebSrv.route('/TEST', 'POST') def _httpHandlerTestPost(httpClient, httpResponse) : formData = httpClient.ReadRequestPostedFormData() firstname = formData["firstname"] lastname = formData["lastname"] content = """\ - + TEST POST @@ -56,12 +81,48 @@ def _httpHandlerTestPost(httpClient, httpResponse) : contentCharset = "UTF-8", content = content ) -# ---------------------------------------------------------------------------- +@MicroWebSrv.route('/edit/') # /edit/123 -> args['index']=123 +@MicroWebSrv.route('/edit//abc/') # /edit/123/abc/bar -> args['index']=123 args['foo']='bar' +@MicroWebSrv.route('/edit') # /edit -> args={} +def _httpHandlerEditWithArgs(httpClient, httpResponse, args={}) : + content = """\ + + + + + TEST EDIT + + + """ + content += "

EDIT item with {} variable arguments

"\ + .format(len(args)) + + if 'index' in args : + content += "

index = {}

".format(args['index']) + + if 'foo' in args : + content += "

foo = {}

".format(args['foo']) + + content += """ + + + """ + httpResponse.WriteResponseOk( headers = None, + contentType = "text/html", + contentCharset = "UTF-8", + content = content ) + +# ------------------------------------ + +# === MicroWebSocket callbacks === def _acceptWebSocketCallback(webSocket, httpClient) : if WS_messages: print("WS ACCEPT") - _thread.list() + if ws_run_in_thread or srv_run_in_thread: + # Print thread list so that we can monitor maximum stack size + # of WebServer thread and WebSocket thread if any is used + _thread.list() webSocket.RecvTextCallback = _recvTextCallback webSocket.RecvBinaryCallback = _recvBinaryCallback webSocket.ClosedCallback = _closedCallback @@ -77,30 +138,31 @@ def _recvBinaryCallback(webSocket, data) : def _closedCallback(webSocket) : if WS_messages: - _thread.list() + if ws_run_in_thread or srv_run_in_thread: + _thread.list() print("WS CLOSED") # ---------------------------------------------------------------------------- -routeHandlers = [ - ( "/test", "GET", _httpHandlerTestGet ), - ( "/test", "POST", _httpHandlerTestPost ) -] - -srv = MicroWebSrv(routeHandlers=routeHandlers) +srv = MicroWebSrv(webPath='www/') # ------------------------------------------------------ # WebSocket configuration srv.MaxWebSocketRecvLen = 256 -# Run WebSocket in thread -srv.WebSocketThreaded = True -# If WS is running in thread, set the thread stack size -# For this example 4096 is enough, for more complex -# webSocket handling you may need to increase this size + +# Run WebSocket in thread or not +srv.WebSocketThreaded = ws_run_in_thread +# If WebSocket is running in thread, set the thread stack size +# For this example 4096 should be enough, for more complex +# webSocket handling you may need to increase this size +# If WebSocketS is NOT running in thread, and WebServer IS running in thread +# make shure WebServer has enough stack size to handle also the WebSocket requests srv.WebSocketStackSize = 4096 srv.AcceptWebSocketCallback = _acceptWebSocketCallback # ------------------------------------------------------ -srv.Start(threaded=True) +# If WebSocketS used and NOT running in thread, and WebServer IS running in thread +# make shure WebServer has enough stack size to handle also the WebSocket requests +srv.Start(threaded=srv_run_in_thread, stackSize=8192) # ---------------------------------------------------------------------------- diff --git a/MicroPython_BUILD/components/micropython/esp32/modules_examples/webserver/webserver_example.py.new b/MicroPython_BUILD/components/micropython/esp32/modules_examples/webserver/webserver_example.py.new deleted file mode 100644 index 9516c57..0000000 --- a/MicroPython_BUILD/components/micropython/esp32/modules_examples/webserver/webserver_example.py.new +++ /dev/null @@ -1,140 +0,0 @@ - -from microWebSrv import MicroWebSrv -import _thread - -# set to True to print WebSocket messages -WS_messages = True - -# ---------------------------------------------------------------------------- - -@MicroWebSrv.route('/TEST') -def _httpHandlerTestGet(httpClient, httpResponse) : - content = """\ - - - - - TEST GET - - -

TEST GET

- Client IP address = %s -
-
- First name:
- Last name:
- -
- - - """ % httpClient.GetIPAddr() - httpResponse.WriteResponseOk( headers = None, - contentType = "text/html", - contentCharset = "UTF-8", - content = content ) - -@MicroWebSrv.route('/TEST', 'POST') -def _httpHandlerTestPost(httpClient, httpResponse) : - formData = httpClient.ReadRequestPostedFormData() - firstname = formData["firstname"] - lastname = formData["lastname"] - content = """\ - - - - - TEST POST - - -

TEST POST

- Firstname = %s
- Lastname = %s
- - - """ % ( MicroWebSrv.HTMLEscape(firstname), - MicroWebSrv.HTMLEscape(lastname) ) - httpResponse.WriteResponseOk( headers = None, - contentType = "text/html", - contentCharset = "UTF-8", - content = content ) - -@MicroWebSrv.route('/edit/') # /edit/123 -> args['index']=123 -@MicroWebSrv.route('/edit//abc/') # /edit/123/abc/bar -> args['index']=123 args['foo']='bar' -@MicroWebSrv.route('/edit') # /edit -> args={} -def _httpHandlerEditWithArgs(httpClient, httpResponse, args={}) : - content = """\ - - - - - TEST EDIT - - - """ - content += "

EDIT item with {} variable arguments

"\ - .format(len(args)) - - if 'index' in args : - content += "

index = {}

".format(args['index']) - - if 'foo' in args : - content += "

foo = {}

".format(args['foo']) - - content += """ - - - """ - httpResponse.WriteResponseOk( headers = None, - contentType = "text/html", - contentCharset = "UTF-8", - content = content ) - -# ---------------------------------------------------------------------------- - -def _acceptWebSocketCallback(webSocket, httpClient) : - if WS_messages: - print("WS ACCEPT") - _thread.list() - webSocket.RecvTextCallback = _recvTextCallback - webSocket.RecvBinaryCallback = _recvBinaryCallback - webSocket.ClosedCallback = _closedCallback - -def _recvTextCallback(webSocket, msg) : - if WS_messages: - print("WS RECV TEXT : %s" % msg) - webSocket.SendText("Reply for %s" % msg) - -def _recvBinaryCallback(webSocket, data) : - if WS_messages: - print("WS RECV DATA : %s" % data) - -def _closedCallback(webSocket) : - if WS_messages: - _thread.list() - print("WS CLOSED") - -# ---------------------------------------------------------------------------- - -#routeHandlers = [ -# ( "/test", "GET", _httpHandlerTestGet ), -# ( "/test", "POST", _httpHandlerTestPost ) -#] - -#srv = MicroWebSrv(routeHandlers=routeHandlers) -srv = MicroWebSrv(webPath='www/') - -# ------------------------------------------------------ -# WebSocket configuration -srv.MaxWebSocketRecvLen = 256 -# Run WebSocket in thread -srv.WebSocketThreaded = True -# If WS is running in thread, set the thread stack size -# For this example 4096 is enough, for more complex -# webSocket handling you may need to increase this size -srv.WebSocketStackSize = 4096 -srv.AcceptWebSocketCallback = _acceptWebSocketCallback -# ------------------------------------------------------ - -srv.Start(threaded=True) - -# ---------------------------------------------------------------------------- diff --git a/MicroPython_BUILD/components/micropython/esp32/mpconfigport.h b/MicroPython_BUILD/components/micropython/esp32/mpconfigport.h index bc4a0bd..75fff33 100644 --- a/MicroPython_BUILD/components/micropython/esp32/mpconfigport.h +++ b/MicroPython_BUILD/components/micropython/esp32/mpconfigport.h @@ -368,6 +368,8 @@ extern const struct _mp_obj_module_t mp_module_bluetooth; 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 diff --git a/MicroPython_BUILD/components/micropython/esp32/mpthreadport.c b/MicroPython_BUILD/components/micropython/esp32/mpthreadport.c index 0075ebb..665814c 100644 --- a/MicroPython_BUILD/components/micropython/esp32/mpthreadport.c +++ b/MicroPython_BUILD/components/micropython/esp32/mpthreadport.c @@ -282,9 +282,9 @@ STATIC void mp_clean_thread(thread_t *th) { if (th->threadQueue) { int n = 1; - while (n) { + while (n > 0) { n = uxQueueMessagesWaiting(th->threadQueue); - if (n) { + if (n > 0) { thread_msg_t msg; xQueueReceive(th->threadQueue, &msg, 0); if (msg.strdata != NULL) free(msg.strdata); @@ -324,24 +324,6 @@ void mp_thread_mutex_unlock(mp_thread_mutex_t *mutex) { xSemaphoreGive(mutex->handle); } -// Terminate all Python threads -// used before entering sleep/reset -//--------------------------- -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; - } - mp_clean_thread(th); - vTaskDelete(th->id); - } - mp_thread_mutex_unlock(&thread_mutex); - // allow FreeRTOS to clean-up the threads - vTaskDelay(2); -} - //-------------------------------------- void mp_thread_allowsuspend(int allow) { mp_thread_mutex_lock(&thread_mutex, 1); @@ -447,6 +429,17 @@ int mp_thread_notify(TaskHandle_t id, uint32_t value) { return res; } +//--------------------------- +int mp_thread_num_threads() { + int res = 0; + mp_thread_mutex_lock(&thread_mutex, 1); + for (thread_t *th = thread; th != NULL; th = th->next) { + if (th->id != xTaskGetCurrentTaskHandle()) res++; + } + mp_thread_mutex_unlock(&thread_mutex); + return res; +} + //--------------------------------------------- uint32_t mp_thread_getnotify(bool check_only) { uint32_t value = 0; @@ -707,29 +700,20 @@ int mp_thread_replAcceptMsg(int8_t accept) { #if defined(CONFIG_MICROPY_USE_TELNET) || defined(CONFIG_MICROPY_USE_FTPSERVER) // Check if WiFi connection is available -//---------------------- -static int _check_wifi() +//----------------------- +static bool _check_wifi() { - if (wifi_network_state < 2) return 2; + if (wifi_network_state < 2) return false; - tcpip_adapter_if_t if_type; - tcpip_adapter_ip_info_t info; + bool res = 0; wifi_mode_t wifi_mode; esp_err_t ret = esp_wifi_get_mode(&wifi_mode); - if (ret != ESP_OK) return 0; - if (wifi_mode == WIFI_MODE_AP) if_type = TCPIP_ADAPTER_IF_AP; - else if (wifi_mode == WIFI_MODE_STA) if_type = TCPIP_ADAPTER_IF_STA; - else return 2; - - ret = tcpip_adapter_get_ip_info(if_type, &info); - if (ret != ESP_OK) return 0; - if (info.ip.addr == 0) return 0; - - if ((wifi_mode == WIFI_MODE_STA) && ((!wifi_sta_isconnected) || (!wifi_sta_has_ipaddress))) return 0; - else if ((wifi_mode == WIFI_MODE_AP) && (!wifi_ap_isconnected)) return 0; - - return 1; + if (ret == ESP_OK) { + if ((wifi_mode & WIFI_MODE_STA) && ((wifi_sta_isconnected) && (wifi_sta_has_ipaddress))) res = true; + if ((wifi_mode & WIFI_MODE_AP) && wifi_ap_isconnected) res = true; + } + return res; } #endif @@ -737,45 +721,40 @@ static int _check_wifi() //=================================== void telnet_task (void *pvParameters) { - int res; // Initialize telnet, create rx buffer and mutex telnet_init(); // Check if WiFi connection is available - res = _check_wifi(); - while ( res == 0) { + while (!_check_wifi()) { vTaskDelay(1000 / portTICK_PERIOD_MS); - res = _check_wifi(); + if (telnet_stop_requested()) goto exit; } - if (res == 2) goto exit; // We have WiFi connection, enable telnet telnet_enable(); while (1) { - res = telnet_run(); + int res = telnet_run(); if ( res < 0) { if (res == -1) { ESP_LOGD("[Telnet]", "\nRun Error"); } + // -2 is returned if Telnet stop was requested by user break; } vTaskDelay(1); // ---- Check if WiFi is still available ---- - res = _check_wifi(); - if (res == 0) { + if (!_check_wifi()) { bool was_enabled = telnet_isenabled(); telnet_disable(); - while ( res == 0) { + while (!_check_wifi()) { vTaskDelay(200 / portTICK_PERIOD_MS); - res = _check_wifi(); - if (res == 2) goto exit; + if (telnet_stop_requested()) goto exit; } if (was_enabled) telnet_enable(); } - else if (res == 2) break; // ------------------------------------------ } exit: @@ -809,17 +788,14 @@ uintptr_t mp_thread_createTelnetTask(size_t stack_size) //================================ void ftp_task (void *pvParameters) { - int res; uint64_t elapsed, time_ms = mp_hal_ticks_ms(); // Initialize ftp, create rx buffer and mutex ftp_init(); - res = _check_wifi(); - while ( res == 0) { + while (!_check_wifi()) { vTaskDelay(1000 / portTICK_PERIOD_MS); - res = _check_wifi(); + if (ftp_stop_requested()) goto exit; } - if (res == 2) goto exit; // We have WiFi connection, enable ftp ftp_enable(); @@ -829,29 +805,27 @@ void ftp_task (void *pvParameters) elapsed = mp_hal_ticks_ms() - time_ms; time_ms = mp_hal_ticks_ms(); - res = ftp_run(elapsed); + int res = ftp_run(elapsed); if (res < 0) { if (res == -1) { ESP_LOGD("[Ftp]", "\nRun Error"); } + // -2 is returned if Ftp stop was requested by user break; } vTaskDelay(1); // ---- Check if WiFi is still available ---- - res = _check_wifi(); - if (res == 0) { + if (!_check_wifi()) { bool was_enabled = ftp_isenabled(); ftp_disable(); - while ( res == 0) { + while (!_check_wifi()) { vTaskDelay(200 / portTICK_PERIOD_MS); - res = _check_wifi(); - if (res == 2) goto exit; + if (ftp_stop_requested()) goto exit; } if (was_enabled) ftp_enable(); } - else if (res == 2) break; // ------------------------------------------ } exit: diff --git a/MicroPython_BUILD/components/micropython/esp32/mpthreadport.h b/MicroPython_BUILD/components/micropython/esp32/mpthreadport.h index 79a148a..a890090 100644 --- a/MicroPython_BUILD/components/micropython/esp32/mpthreadport.h +++ b/MicroPython_BUILD/components/micropython/esp32/mpthreadport.h @@ -118,8 +118,8 @@ uint8_t main_accept_msg; void mp_thread_preinit(void *stack, uint32_t stack_len); void mp_thread_init(void); +int mp_thread_num_threads(); void mp_thread_gc_others(void); -void mp_thread_deinit(void); void mp_thread_allowsuspend(int allow); int mp_thread_suspend(TaskHandle_t id); diff --git a/MicroPython_BUILD/components/micropython/esp32/mpversion.h b/MicroPython_BUILD/components/micropython/esp32/mpversion.h index 83f5282..79b5e86 100644 --- a/MicroPython_BUILD/components/micropython/esp32/mpversion.h +++ b/MicroPython_BUILD/components/micropython/esp32/mpversion.h @@ -24,12 +24,12 @@ * THE SOFTWARE. */ -#define MICROPY_GIT_TAG "ESP32_LoBo_v3.2.6" -#define MICROPY_GIT_HASH "gf586f5e6" -#define MICROPY_BUILD_DATE "2018-04-09" +#define MICROPY_GIT_TAG "ESP32_LoBo_v3.2.10" +#define MICROPY_GIT_HASH "gbae9709a" +#define MICROPY_BUILD_DATE "2018-04-15" #define MICROPY_VERSION_MAJOR (3) #define MICROPY_VERSION_MINOR (2) -#define MICROPY_VERSION_MICRO (6) -#define MICROPY_VERSION_STRING "3.2.6" -#define MICROPY_CORE_VERSION "bcfff4f" -#define MICROPY_CORE_DATE "2018-03-30" +#define MICROPY_VERSION_MICRO (10) +#define MICROPY_VERSION_STRING "3.2.10" +#define MICROPY_CORE_VERSION "59dda71" +#define MICROPY_CORE_DATE "2018-04-10" diff --git a/MicroPython_BUILD/components/micropython/extmod/modlwip.c b/MicroPython_BUILD/components/micropython/extmod/modlwip.c index 2a8496f..234dedd 100644 --- a/MicroPython_BUILD/components/micropython/extmod/modlwip.c +++ b/MicroPython_BUILD/components/micropython/extmod/modlwip.c @@ -638,42 +638,6 @@ STATIC mp_obj_t lwip_socket_make_new(const mp_obj_type_t *type, size_t n_args, s return socket; } -STATIC mp_obj_t lwip_socket_close(mp_obj_t self_in) { - lwip_socket_obj_t *socket = self_in; - bool socket_is_listener = false; - - if (socket->pcb.tcp == NULL) { - return mp_const_none; - } - switch (socket->type) { - case MOD_NETWORK_SOCK_STREAM: { - if (socket->pcb.tcp->state == LISTEN) { - socket_is_listener = true; - } - if (tcp_close(socket->pcb.tcp) != ERR_OK) { - DEBUG_printf("lwip_close: had to call tcp_abort()\n"); - tcp_abort(socket->pcb.tcp); - } - break; - } - case MOD_NETWORK_SOCK_DGRAM: udp_remove(socket->pcb.udp); break; - //case MOD_NETWORK_SOCK_RAW: raw_remove(socket->pcb.raw); break; - } - socket->pcb.tcp = NULL; - socket->state = _ERR_BADF; - if (socket->incoming.pbuf != NULL) { - if (!socket_is_listener) { - pbuf_free(socket->incoming.pbuf); - } else { - tcp_abort(socket->incoming.connection); - } - socket->incoming.pbuf = NULL; - } - - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(lwip_socket_close_obj, lwip_socket_close); - STATIC mp_obj_t lwip_socket_bind(mp_obj_t self_in, mp_obj_t addr_in) { lwip_socket_obj_t *socket = self_in; @@ -1180,6 +1144,39 @@ STATIC mp_uint_t lwip_socket_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_ ret |= flags & (MP_STREAM_POLL_RD | MP_STREAM_POLL_WR); } + } else if (request == MP_STREAM_CLOSE) { + bool socket_is_listener = false; + + if (socket->pcb.tcp == NULL) { + return 0; + } + switch (socket->type) { + case MOD_NETWORK_SOCK_STREAM: { + if (socket->pcb.tcp->state == LISTEN) { + socket_is_listener = true; + } + if (tcp_close(socket->pcb.tcp) != ERR_OK) { + DEBUG_printf("lwip_close: had to call tcp_abort()\n"); + tcp_abort(socket->pcb.tcp); + } + break; + } + case MOD_NETWORK_SOCK_DGRAM: udp_remove(socket->pcb.udp); break; + //case MOD_NETWORK_SOCK_RAW: raw_remove(socket->pcb.raw); break; + } + socket->pcb.tcp = NULL; + socket->state = _ERR_BADF; + if (socket->incoming.pbuf != NULL) { + if (!socket_is_listener) { + pbuf_free(socket->incoming.pbuf); + } else { + tcp_abort(socket->incoming.connection); + } + socket->incoming.pbuf = NULL; + } + ret = 0; + + } else { *errcode = MP_EINVAL; ret = MP_STREAM_ERROR; @@ -1189,8 +1186,8 @@ STATIC mp_uint_t lwip_socket_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_ } STATIC const mp_rom_map_elem_t lwip_socket_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&lwip_socket_close_obj) }, - { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&lwip_socket_close_obj) }, + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mp_stream_close_obj) }, + { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_close_obj) }, { MP_ROM_QSTR(MP_QSTR_bind), MP_ROM_PTR(&lwip_socket_bind_obj) }, { MP_ROM_QSTR(MP_QSTR_listen), MP_ROM_PTR(&lwip_socket_listen_obj) }, { MP_ROM_QSTR(MP_QSTR_accept), MP_ROM_PTR(&lwip_socket_accept_obj) }, diff --git a/MicroPython_BUILD/components/micropython/extmod/moduselect.c b/MicroPython_BUILD/components/micropython/extmod/moduselect.c index 2e9225a..bfdabf2 100644 --- a/MicroPython_BUILD/components/micropython/extmod/moduselect.c +++ b/MicroPython_BUILD/components/micropython/extmod/moduselect.c @@ -130,7 +130,7 @@ STATIC mp_obj_t select_select(uint n_args, const mp_obj_t *args) { timeout = (mp_uint_t)(timeout_f * 1000); } #else - timeout = mp_obj_get_int(args[3]) * 1000; + timeout = mp_obj_get_int64(args[3]) * 1000; #endif } } @@ -233,7 +233,7 @@ STATIC mp_uint_t poll_poll_internal(uint n_args, const mp_obj_t *args) { int flags = 0; if (n_args >= 2) { if (args[1] != mp_const_none) { - int64_t timeout_i = mp_obj_get_int(args[1]); + int64_t timeout_i = mp_obj_get_int64(args[1]); if (timeout_i >= 0) { timeout = timeout_i; } diff --git a/MicroPython_BUILD/components/micropython/extmod/modussl_mbedtls.c b/MicroPython_BUILD/components/micropython/extmod/modussl_mbedtls.c index aab1d65..9a5375f 100644 --- a/MicroPython_BUILD/components/micropython/extmod/modussl_mbedtls.c +++ b/MicroPython_BUILD/components/micropython/extmod/modussl_mbedtls.c @@ -280,20 +280,26 @@ STATIC mp_obj_t socket_setblocking(mp_obj_t self_in, mp_obj_t flag_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_2(socket_setblocking_obj, socket_setblocking); -STATIC mp_obj_t socket_close(mp_obj_t self_in) { - mp_obj_ssl_socket_t *self = MP_OBJ_TO_PTR(self_in); +STATIC mp_uint_t socket_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, int *errcode) { + mp_obj_ssl_socket_t *self = MP_OBJ_TO_PTR(o_in); + (void)arg; + switch (request) { + case MP_STREAM_CLOSE: + mbedtls_pk_free(&self->pkey); + mbedtls_x509_crt_free(&self->cert); + mbedtls_x509_crt_free(&self->cacert); + mbedtls_ssl_free(&self->ssl); + mbedtls_ssl_config_free(&self->conf); + mbedtls_ctr_drbg_free(&self->ctr_drbg); + mbedtls_entropy_free(&self->entropy); + mp_stream_close(self->sock); + return 0; - mbedtls_pk_free(&self->pkey); - mbedtls_x509_crt_free(&self->cert); - mbedtls_x509_crt_free(&self->cacert); - mbedtls_ssl_free(&self->ssl); - mbedtls_ssl_config_free(&self->conf); - mbedtls_ctr_drbg_free(&self->ctr_drbg); - mbedtls_entropy_free(&self->entropy); - - return mp_stream_close(self->sock); + default: + *errcode = MP_EINVAL; + return MP_STREAM_ERROR; + } } -STATIC MP_DEFINE_CONST_FUN_OBJ_1(socket_close_obj, socket_close); STATIC const mp_rom_map_elem_t ussl_socket_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, @@ -301,9 +307,9 @@ STATIC const mp_rom_map_elem_t ussl_socket_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) }, { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, { MP_ROM_QSTR(MP_QSTR_setblocking), MP_ROM_PTR(&socket_setblocking_obj) }, - { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&socket_close_obj) }, + { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_close_obj) }, #if MICROPY_PY_USSL_FINALISER - { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&socket_close_obj) }, + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mp_stream_close_obj) }, #endif { MP_ROM_QSTR(MP_QSTR_getpeercert), MP_ROM_PTR(&mod_ssl_getpeercert_obj) }, }; @@ -313,6 +319,7 @@ STATIC MP_DEFINE_CONST_DICT(ussl_socket_locals_dict, ussl_socket_locals_dict_tab STATIC const mp_stream_p_t ussl_socket_stream_p = { .read = socket_read, .write = socket_write, + .ioctl = socket_ioctl, }; STATIC const mp_obj_type_t ussl_socket_type = { diff --git a/MicroPython_BUILD/components/micropython/extmod/modutimeq.c b/MicroPython_BUILD/components/micropython/extmod/modutimeq.c index ba112ee..d37b56b 100644 --- a/MicroPython_BUILD/components/micropython/extmod/modutimeq.c +++ b/MicroPython_BUILD/components/micropython/extmod/modutimeq.c @@ -41,7 +41,7 @@ // the algorithm here is modelled on CPython's heapq.py struct qentry { - mp_uint_t time; + uint64_t time; mp_uint_t id; mp_obj_t callback; mp_obj_t args; @@ -131,12 +131,12 @@ STATIC mp_obj_t mod_utimeq_heappush(size_t n_args, const mp_obj_t *args) { mp_raise_msg(&mp_type_IndexError, "queue overflow"); } mp_uint_t l = heap->len; - mp_uint_t itime; + uint64_t itime; if (mp_obj_is_float(args[1])) { mp_float_t time = mp_obj_float_get(args[1]); - itime = (uint32_t)time; + itime = (uint64_t)time; } - else itime = mp_obj_get_int(args[1]); + else itime = mp_obj_get_int64(args[1]); heap->items[l].time = itime; heap->items[l].id = utimeq_id++; diff --git a/MicroPython_BUILD/components/micropython/extmod/modwebsocket.c b/MicroPython_BUILD/components/micropython/extmod/modwebsocket.c index a651164..5a826ec 100644 --- a/MicroPython_BUILD/components/micropython/extmod/modwebsocket.c +++ b/MicroPython_BUILD/components/micropython/extmod/modwebsocket.c @@ -256,6 +256,11 @@ STATIC mp_uint_t websocket_write(mp_obj_t self_in, const void *buf, mp_uint_t si STATIC mp_uint_t websocket_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) { mp_obj_websocket_t *self = MP_OBJ_TO_PTR(self_in); switch (request) { + case MP_STREAM_CLOSE: + // TODO: Send close signaling to the other side, otherwise it's + // abrupt close (connection abort). + mp_stream_close(self->sock); + return 0; case MP_STREAM_GET_DATA_OPTS: return self->ws_flags & FRAME_OPCODE_MASK; case MP_STREAM_SET_DATA_OPTS: { @@ -269,21 +274,13 @@ STATIC mp_uint_t websocket_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t } } -STATIC mp_obj_t websocket_close(mp_obj_t self_in) { - mp_obj_websocket_t *self = MP_OBJ_TO_PTR(self_in); - // TODO: Send close signaling to the other side, otherwise it's - // abrupt close (connection abort). - return mp_stream_close(self->sock); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(websocket_close_obj, websocket_close); - STATIC const mp_rom_map_elem_t websocket_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) }, { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, { MP_ROM_QSTR(MP_QSTR_ioctl), MP_ROM_PTR(&mp_stream_ioctl_obj) }, - { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&websocket_close_obj) }, + { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_close_obj) }, }; STATIC MP_DEFINE_CONST_DICT(websocket_locals_dict, websocket_locals_dict_table); diff --git a/MicroPython_BUILD/components/micropython/extmod/re1.5/compilecode.c b/MicroPython_BUILD/components/micropython/extmod/re1.5/compilecode.c index 3267a41..a685a50 100644 --- a/MicroPython_BUILD/components/micropython/extmod/re1.5/compilecode.c +++ b/MicroPython_BUILD/components/micropython/extmod/re1.5/compilecode.c @@ -5,9 +5,9 @@ #include "re1.5.h" #define INSERT_CODE(at, num, pc) \ - ((code ? memmove(code + at + num, code + at, pc - at) : (void)0), pc += num) + ((code ? memmove(code + at + num, code + at, pc - at) : 0), pc += num) #define REL(at, to) (to - at - 2) -#define EMIT(at, byte) (code ? (code[at] = byte) : (void)(at)) +#define EMIT(at, byte) (code ? (code[at] = byte) : (at)) #define PC (prog->bytelen) static const char *_compilecode(const char *re, ByteProg *prog, int sizecode) diff --git a/MicroPython_BUILD/components/micropython/extmod/vfs_native.c b/MicroPython_BUILD/components/micropython/extmod/vfs_native.c index 710a642..4ad93fa 100644 --- a/MicroPython_BUILD/components/micropython/extmod/vfs_native.c +++ b/MicroPython_BUILD/components/micropython/extmod/vfs_native.c @@ -713,6 +713,31 @@ STATIC void sdcard_print_info(const sdmmc_card_t* card, int mode) #endif } +//-------------------------------------------------------------- +static void _setPins(int8_t p1, int8_t p2, int8_t p3, int8_t p4) +{ + if (p1 >= 0) { + gpio_pad_select_gpio(p1); + gpio_set_direction(p1, GPIO_MODE_INPUT); + gpio_set_pull_mode(p1, GPIO_PULLUP_ONLY); + } + if (p2 >= 0) { + gpio_pad_select_gpio(p2); + gpio_set_direction(p2, GPIO_MODE_INPUT); + gpio_set_pull_mode(p2, GPIO_PULLUP_ONLY); + } + if (p3 >= 0) { + gpio_pad_select_gpio(p3); + gpio_set_direction(p3, GPIO_MODE_INPUT); + gpio_set_pull_mode(p3, GPIO_PULLUP_ONLY); + } + if (p4 >= 0) { + gpio_pad_select_gpio(p4); + gpio_set_direction(p4, GPIO_MODE_INPUT); + gpio_set_pull_mode(p4, GPIO_PULLUP_ONLY); + } +} + //------------------------- static void _sdcard_mount() { @@ -730,17 +755,9 @@ static void _sdcard_mount() sdspi_slot_config_t slot_config = SDSPI_SLOT_CONFIG_DEFAULT(); host.slot = VSPI_HOST; host.max_freq_khz = SDMMC_FREQ_HIGHSPEED; - slot_config.dma_channel = 2; - gpio_pad_select_gpio(sdcard_config.miso); - gpio_pad_select_gpio(sdcard_config.mosi); - gpio_pad_select_gpio(sdcard_config.clk); - gpio_pad_select_gpio(sdcard_config.cs); - gpio_set_direction(sdcard_config.miso, GPIO_MODE_INPUT); - gpio_set_pull_mode(sdcard_config.miso, GPIO_PULLUP_ONLY); - gpio_set_pull_mode(sdcard_config.clk, GPIO_PULLUP_ONLY); - gpio_set_pull_mode(sdcard_config.mosi, GPIO_PULLUP_ONLY); - gpio_set_pull_mode(sdcard_config.cs, GPIO_PULLUP_ONLY); + _setPins(sdcard_config.miso, sdcard_config.mosi, sdcard_config.clk, sdcard_config.cs); + slot_config.gpio_miso = sdcard_config.miso; slot_config.gpio_mosi = sdcard_config.mosi; slot_config.gpio_sck = sdcard_config.clk; @@ -751,22 +768,15 @@ static void _sdcard_mount() sdmmc_host_t host = SDMMC_HOST_DEFAULT(); sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); host.max_freq_khz = SDMMC_FREQ_HIGHSPEED; + _setPins(2, 14, 15, 13); if (sdcard_config.mode == 2) { // Use 1-line SD mode - gpio_set_pull_mode(2, GPIO_PULLUP_ONLY); - gpio_set_pull_mode(14, GPIO_PULLUP_ONLY); - gpio_set_pull_mode(15, GPIO_PULLUP_ONLY); host.flags = SDMMC_HOST_FLAG_1BIT; slot_config.width = 1; } else { // Use 4-line SD mode - gpio_set_pull_mode(2, GPIO_PULLUP_ONLY); - gpio_set_pull_mode(14, GPIO_PULLUP_ONLY); - gpio_set_pull_mode(15, GPIO_PULLUP_ONLY); - gpio_set_pull_mode(4, GPIO_PULLUP_ONLY); - gpio_set_pull_mode(12, GPIO_PULLUP_ONLY); - gpio_set_pull_mode(13, GPIO_PULLUP_ONLY); + _setPins(4, 12, -1, -1); } ret = esp_vfs_fat_sdmmc_mount(VFS_NATIVE_SDCARD_MOUNT_POINT, &host, &slot_config, &mount_config, &sdmmc_card); } diff --git a/MicroPython_BUILD/components/micropython/extmod/vfs_native_file.c b/MicroPython_BUILD/components/micropython/extmod/vfs_native_file.c index d9a76c3..e75e832 100644 --- a/MicroPython_BUILD/components/micropython/extmod/vfs_native_file.c +++ b/MicroPython_BUILD/components/micropython/extmod/vfs_native_file.c @@ -152,7 +152,19 @@ STATIC mp_uint_t file_obj_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, // fsync() not implemented. return 0; - } else { + } else if (request == MP_STREAM_CLOSE) { + // if fs==NULL then the file is closed and in that case this method is a no-op + if (self->fd != -1) { + int res = close(self->fd); + self->fd = -1; + if (res < 0) { + *errcode = errno; + return MP_STREAM_ERROR; + } + } + return 0; + + } else { ESP_LOGD(TAG, "ioctl(%d, %d, ..): error %d", self->fd, request, MP_EINVAL); *errcode = MP_EINVAL; return MP_STREAM_ERROR; diff --git a/MicroPython_BUILD/components/micropython/py/asmthumb.h b/MicroPython_BUILD/components/micropython/py/asmthumb.h index 552ad75..8a7df5d 100644 --- a/MicroPython_BUILD/components/micropython/py/asmthumb.h +++ b/MicroPython_BUILD/components/micropython/py/asmthumb.h @@ -26,6 +26,7 @@ #ifndef MICROPY_INCLUDED_PY_ASMTHUMB_H #define MICROPY_INCLUDED_PY_ASMTHUMB_H +#include #include "py/misc.h" #include "py/asmbase.h" diff --git a/MicroPython_BUILD/components/micropython/py/emitnarm.c b/MicroPython_BUILD/components/micropython/py/emitnarm.c new file mode 100644 index 0000000..1b585f8 --- /dev/null +++ b/MicroPython_BUILD/components/micropython/py/emitnarm.c @@ -0,0 +1,15 @@ +// ARM specific stuff + +#include "py/mpconfig.h" + +#if MICROPY_EMIT_ARM + +// This is defined so that the assembler exports generic assembler API macros +#define GENERIC_ASM_API (1) +#include "py/asmarm.h" + +#define N_ARM (1) +#define EXPORT_FUN(name) emit_native_arm_##name +#include "py/emitnative.c" + +#endif diff --git a/MicroPython_BUILD/components/micropython/py/emitnative.c b/MicroPython_BUILD/components/micropython/py/emitnative.c index 964db95..7e035d5 100644 --- a/MicroPython_BUILD/components/micropython/py/emitnative.c +++ b/MicroPython_BUILD/components/micropython/py/emitnative.c @@ -63,9 +63,6 @@ || (MICROPY_EMIT_ARM && N_ARM) \ || (MICROPY_EMIT_XTENSA && N_XTENSA) \ -// this is defined so that the assembler exports generic assembler API macros -#define GENERIC_ASM_API (1) - // define additional generic helper macros #define ASM_MOV_LOCAL_IMM_VIA(as, local_num, imm, reg_temp) \ do { \ @@ -73,94 +70,6 @@ ASM_MOV_LOCAL_REG((as), (local_num), (reg_temp)); \ } while (false) -#if N_X64 - -// x64 specific stuff -#include "py/asmx64.h" -#define EXPORT_FUN(name) emit_native_x64_##name - -#elif N_X86 - -// x86 specific stuff - -STATIC byte mp_f_n_args[MP_F_NUMBER_OF] = { - [MP_F_CONVERT_OBJ_TO_NATIVE] = 2, - [MP_F_CONVERT_NATIVE_TO_OBJ] = 2, - [MP_F_LOAD_NAME] = 1, - [MP_F_LOAD_GLOBAL] = 1, - [MP_F_LOAD_BUILD_CLASS] = 0, - [MP_F_LOAD_ATTR] = 2, - [MP_F_LOAD_METHOD] = 3, - [MP_F_LOAD_SUPER_METHOD] = 2, - [MP_F_STORE_NAME] = 2, - [MP_F_STORE_GLOBAL] = 2, - [MP_F_STORE_ATTR] = 3, - [MP_F_OBJ_SUBSCR] = 3, - [MP_F_OBJ_IS_TRUE] = 1, - [MP_F_UNARY_OP] = 2, - [MP_F_BINARY_OP] = 3, - [MP_F_BUILD_TUPLE] = 2, - [MP_F_BUILD_LIST] = 2, - [MP_F_LIST_APPEND] = 2, - [MP_F_BUILD_MAP] = 1, - [MP_F_STORE_MAP] = 3, -#if MICROPY_PY_BUILTINS_SET - [MP_F_BUILD_SET] = 2, - [MP_F_STORE_SET] = 2, -#endif - [MP_F_MAKE_FUNCTION_FROM_RAW_CODE] = 3, - [MP_F_NATIVE_CALL_FUNCTION_N_KW] = 3, - [MP_F_CALL_METHOD_N_KW] = 3, - [MP_F_CALL_METHOD_N_KW_VAR] = 3, - [MP_F_NATIVE_GETITER] = 2, - [MP_F_NATIVE_ITERNEXT] = 1, - [MP_F_NLR_PUSH] = 1, - [MP_F_NLR_POP] = 0, - [MP_F_NATIVE_RAISE] = 1, - [MP_F_IMPORT_NAME] = 3, - [MP_F_IMPORT_FROM] = 2, - [MP_F_IMPORT_ALL] = 1, -#if MICROPY_PY_BUILTINS_SLICE - [MP_F_NEW_SLICE] = 3, -#endif - [MP_F_UNPACK_SEQUENCE] = 3, - [MP_F_UNPACK_EX] = 3, - [MP_F_DELETE_NAME] = 1, - [MP_F_DELETE_GLOBAL] = 1, - [MP_F_NEW_CELL] = 1, - [MP_F_MAKE_CLOSURE_FROM_RAW_CODE] = 3, - [MP_F_SETUP_CODE_STATE] = 5, - [MP_F_SMALL_INT_FLOOR_DIVIDE] = 2, - [MP_F_SMALL_INT_MODULO] = 2, -}; - -#include "py/asmx86.h" -#define EXPORT_FUN(name) emit_native_x86_##name - -#elif N_THUMB - -// thumb specific stuff -#include "py/asmthumb.h" -#define EXPORT_FUN(name) emit_native_thumb_##name - -#elif N_ARM - -// ARM specific stuff -#include "py/asmarm.h" -#define EXPORT_FUN(name) emit_native_arm_##name - -#elif N_XTENSA - -// Xtensa specific stuff -#include "py/asmxtensa.h" -#define EXPORT_FUN(name) emit_native_xtensa_##name - -#else - -#error unknown native emitter - -#endif - #define EMIT_NATIVE_VIPER_TYPE_ERROR(emit, ...) do { \ *emit->error_slot = mp_obj_new_exception_msg_varg(&mp_type_ViperTypeError, __VA_ARGS__); \ } while (0) diff --git a/MicroPython_BUILD/components/micropython/py/emitnthumb.c b/MicroPython_BUILD/components/micropython/py/emitnthumb.c new file mode 100644 index 0000000..2b68ca3 --- /dev/null +++ b/MicroPython_BUILD/components/micropython/py/emitnthumb.c @@ -0,0 +1,15 @@ +// thumb specific stuff + +#include "py/mpconfig.h" + +#if MICROPY_EMIT_THUMB + +// this is defined so that the assembler exports generic assembler API macros +#define GENERIC_ASM_API (1) +#include "py/asmthumb.h" + +#define N_THUMB (1) +#define EXPORT_FUN(name) emit_native_thumb_##name +#include "py/emitnative.c" + +#endif diff --git a/MicroPython_BUILD/components/micropython/py/emitnx64.c b/MicroPython_BUILD/components/micropython/py/emitnx64.c new file mode 100644 index 0000000..b9800f6 --- /dev/null +++ b/MicroPython_BUILD/components/micropython/py/emitnx64.c @@ -0,0 +1,15 @@ +// x64 specific stuff + +#include "py/mpconfig.h" + +#if MICROPY_EMIT_X64 + +// This is defined so that the assembler exports generic assembler API macros +#define GENERIC_ASM_API (1) +#include "py/asmx64.h" + +#define N_X64 (1) +#define EXPORT_FUN(name) emit_native_x64_##name +#include "py/emitnative.c" + +#endif diff --git a/MicroPython_BUILD/components/micropython/py/emitnx86.c b/MicroPython_BUILD/components/micropython/py/emitnx86.c new file mode 100644 index 0000000..d4cd24d --- /dev/null +++ b/MicroPython_BUILD/components/micropython/py/emitnx86.c @@ -0,0 +1,67 @@ +// x86 specific stuff + +#include "py/mpconfig.h" + +#if MICROPY_EMIT_X86 + +// This is defined so that the assembler exports generic assembler API macros +#define GENERIC_ASM_API (1) +#include "py/asmx86.h" + +// x86 needs a table to know how many args a given function has +STATIC byte mp_f_n_args[MP_F_NUMBER_OF] = { + [MP_F_CONVERT_OBJ_TO_NATIVE] = 2, + [MP_F_CONVERT_NATIVE_TO_OBJ] = 2, + [MP_F_LOAD_NAME] = 1, + [MP_F_LOAD_GLOBAL] = 1, + [MP_F_LOAD_BUILD_CLASS] = 0, + [MP_F_LOAD_ATTR] = 2, + [MP_F_LOAD_METHOD] = 3, + [MP_F_LOAD_SUPER_METHOD] = 2, + [MP_F_STORE_NAME] = 2, + [MP_F_STORE_GLOBAL] = 2, + [MP_F_STORE_ATTR] = 3, + [MP_F_OBJ_SUBSCR] = 3, + [MP_F_OBJ_IS_TRUE] = 1, + [MP_F_UNARY_OP] = 2, + [MP_F_BINARY_OP] = 3, + [MP_F_BUILD_TUPLE] = 2, + [MP_F_BUILD_LIST] = 2, + [MP_F_LIST_APPEND] = 2, + [MP_F_BUILD_MAP] = 1, + [MP_F_STORE_MAP] = 3, + #if MICROPY_PY_BUILTINS_SET + [MP_F_BUILD_SET] = 2, + [MP_F_STORE_SET] = 2, + #endif + [MP_F_MAKE_FUNCTION_FROM_RAW_CODE] = 3, + [MP_F_NATIVE_CALL_FUNCTION_N_KW] = 3, + [MP_F_CALL_METHOD_N_KW] = 3, + [MP_F_CALL_METHOD_N_KW_VAR] = 3, + [MP_F_NATIVE_GETITER] = 2, + [MP_F_NATIVE_ITERNEXT] = 1, + [MP_F_NLR_PUSH] = 1, + [MP_F_NLR_POP] = 0, + [MP_F_NATIVE_RAISE] = 1, + [MP_F_IMPORT_NAME] = 3, + [MP_F_IMPORT_FROM] = 2, + [MP_F_IMPORT_ALL] = 1, + #if MICROPY_PY_BUILTINS_SLICE + [MP_F_NEW_SLICE] = 3, + #endif + [MP_F_UNPACK_SEQUENCE] = 3, + [MP_F_UNPACK_EX] = 3, + [MP_F_DELETE_NAME] = 1, + [MP_F_DELETE_GLOBAL] = 1, + [MP_F_NEW_CELL] = 1, + [MP_F_MAKE_CLOSURE_FROM_RAW_CODE] = 3, + [MP_F_SETUP_CODE_STATE] = 5, + [MP_F_SMALL_INT_FLOOR_DIVIDE] = 2, + [MP_F_SMALL_INT_MODULO] = 2, +}; + +#define N_X86 (1) +#define EXPORT_FUN(name) emit_native_x86_##name +#include "py/emitnative.c" + +#endif diff --git a/MicroPython_BUILD/components/micropython/py/emitnxtensa.c b/MicroPython_BUILD/components/micropython/py/emitnxtensa.c new file mode 100644 index 0000000..1a423e2 --- /dev/null +++ b/MicroPython_BUILD/components/micropython/py/emitnxtensa.c @@ -0,0 +1,15 @@ +// Xtensa specific stuff + +#include "py/mpconfig.h" + +#if MICROPY_EMIT_XTENSA + +// this is defined so that the assembler exports generic assembler API macros +#define GENERIC_ASM_API (1) +#include "py/asmxtensa.h" + +#define N_XTENSA (1) +#define EXPORT_FUN(name) emit_native_xtensa_##name +#include "py/emitnative.c" + +#endif diff --git a/MicroPython_BUILD/components/micropython/py/modmicropython.c b/MicroPython_BUILD/components/micropython/py/modmicropython.c index c677ac8..38a1eeb 100644 --- a/MicroPython_BUILD/components/micropython/py/modmicropython.c +++ b/MicroPython_BUILD/components/micropython/py/modmicropython.c @@ -36,6 +36,7 @@ // Various builtins specific to MicroPython runtime, // living in micropython module +#if MICROPY_ENABLE_COMPILER STATIC mp_obj_t mp_micropython_opt_level(size_t n_args, const mp_obj_t *args) { if (n_args == 0) { return MP_OBJ_NEW_SMALL_INT(MP_STATE_VM(mp_optimise_value)); @@ -45,6 +46,7 @@ STATIC mp_obj_t mp_micropython_opt_level(size_t n_args, const mp_obj_t *args) { } } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_micropython_opt_level_obj, 0, 1, mp_micropython_opt_level); +#endif #if MICROPY_PY_MICROPYTHON_MEM_INFO @@ -159,7 +161,9 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_2(mp_micropython_schedule_obj, mp_micropython_sch STATIC const mp_rom_map_elem_t mp_module_micropython_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_micropython) }, { MP_ROM_QSTR(MP_QSTR_const), MP_ROM_PTR(&mp_identity_obj) }, + #if MICROPY_ENABLE_COMPILER { MP_ROM_QSTR(MP_QSTR_opt_level), MP_ROM_PTR(&mp_micropython_opt_level_obj) }, + #endif #if MICROPY_PY_MICROPYTHON_MEM_INFO #if MICROPY_MEM_STATS { MP_ROM_QSTR(MP_QSTR_mem_total), MP_ROM_PTR(&mp_micropython_mem_total_obj) }, diff --git a/MicroPython_BUILD/components/micropython/py/modsys.c b/MicroPython_BUILD/components/micropython/py/modsys.c index 1ef3ea7..174c32f 100644 --- a/MicroPython_BUILD/components/micropython/py/modsys.c +++ b/MicroPython_BUILD/components/micropython/py/modsys.c @@ -142,10 +142,12 @@ STATIC mp_obj_t mp_sys_exc_info(void) { MP_DEFINE_CONST_FUN_OBJ_0(mp_sys_exc_info_obj, mp_sys_exc_info); #endif +#if MICROPY_PY_SYS_GETSIZEOF STATIC mp_obj_t mp_sys_getsizeof(mp_obj_t obj) { return mp_unary_op(MP_UNARY_OP_SIZEOF, obj); } -MP_DEFINE_CONST_FUN_OBJ_1(mp_sys_getsizeof_obj, mp_sys_getsizeof); +STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_sys_getsizeof_obj, mp_sys_getsizeof); +#endif STATIC mp_obj_t mp_sys_mpycore() { mp_obj_t tuple[2]; diff --git a/MicroPython_BUILD/components/micropython/py/mpstate.h b/MicroPython_BUILD/components/micropython/py/mpstate.h index 4df202e..0e84f43 100644 --- a/MicroPython_BUILD/components/micropython/py/mpstate.h +++ b/MicroPython_BUILD/components/micropython/py/mpstate.h @@ -202,7 +202,9 @@ typedef struct _mp_state_vm_t { mp_thread_mutex_t qstr_mutex; #endif + #if MICROPY_ENABLE_COMPILER mp_uint_t mp_optimise_value; + #endif // size of the emergency exception buf, if it's dynamically allocated #if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF && MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE == 0 diff --git a/MicroPython_BUILD/components/micropython/py/objgenerator.c b/MicroPython_BUILD/components/micropython/py/objgenerator.c index 8c1260b..5fd13f8 100644 --- a/MicroPython_BUILD/components/micropython/py/objgenerator.c +++ b/MicroPython_BUILD/components/micropython/py/objgenerator.c @@ -32,6 +32,7 @@ #include "py/bc.h" #include "py/objgenerator.h" #include "py/objfun.h" +#include "py/stackctrl.h" /******************************************************************************/ /* generator wrapper */ @@ -92,6 +93,7 @@ STATIC void gen_instance_print(const mp_print_t *print, mp_obj_t self_in, mp_pri } mp_vm_return_kind_t mp_obj_gen_resume(mp_obj_t self_in, mp_obj_t send_value, mp_obj_t throw_value, mp_obj_t *ret_val) { + MP_STACK_CHECK(); mp_check_self(MP_OBJ_IS_TYPE(self_in, &mp_type_gen_instance)); mp_obj_gen_instance_t *self = MP_OBJ_TO_PTR(self_in); if (self->code_state.ip == 0) { diff --git a/MicroPython_BUILD/components/micropython/py/objint_longlong.c b/MicroPython_BUILD/components/micropython/py/objint_longlong.c index 3e5ebad..cb8d167 100644 --- a/MicroPython_BUILD/components/micropython/py/objint_longlong.c +++ b/MicroPython_BUILD/components/micropython/py/objint_longlong.c @@ -124,10 +124,9 @@ mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_i if (MP_OBJ_IS_SMALL_INT(lhs_in)) { lhs_val = MP_OBJ_SMALL_INT_VALUE(lhs_in); - } else if (MP_OBJ_IS_TYPE(lhs_in, &mp_type_int)) { - lhs_val = ((mp_obj_int_t*)lhs_in)->val; } else { - return MP_OBJ_NULL; // op not supported + assert(MP_OBJ_IS_TYPE(lhs_in, &mp_type_int)); + lhs_val = ((mp_obj_int_t*)lhs_in)->val; } if (MP_OBJ_IS_SMALL_INT(rhs_in)) { diff --git a/MicroPython_BUILD/components/micropython/py/objint_mpz.c b/MicroPython_BUILD/components/micropython/py/objint_mpz.c index ccf5e0b..f633915 100644 --- a/MicroPython_BUILD/components/micropython/py/objint_mpz.c +++ b/MicroPython_BUILD/components/micropython/py/objint_mpz.c @@ -171,11 +171,9 @@ mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_i if (MP_OBJ_IS_SMALL_INT(lhs_in)) { mpz_init_fixed_from_int(&z_int, z_int_dig, MPZ_NUM_DIG_FOR_INT, MP_OBJ_SMALL_INT_VALUE(lhs_in)); zlhs = &z_int; - } else if (MP_OBJ_IS_TYPE(lhs_in, &mp_type_int)) { - zlhs = &((mp_obj_int_t*)MP_OBJ_TO_PTR(lhs_in))->mpz; } else { - // unsupported type - return MP_OBJ_NULL; + assert(MP_OBJ_IS_TYPE(lhs_in, &mp_type_int)); + zlhs = &((mp_obj_int_t*)MP_OBJ_TO_PTR(lhs_in))->mpz; } // if rhs is small int, then lhs was not (otherwise mp_binary_op handles it) diff --git a/MicroPython_BUILD/components/micropython/py/objstr.c b/MicroPython_BUILD/components/micropython/py/objstr.c index 0b11533..da92523 100644 --- a/MicroPython_BUILD/components/micropython/py/objstr.c +++ b/MicroPython_BUILD/components/micropython/py/objstr.c @@ -699,8 +699,13 @@ STATIC mp_obj_t str_finder(size_t n_args, const mp_obj_t *args, int direction, b end = str_index_to_ptr(self_type, haystack, haystack_len, args[3], true); } + if (end < start) { + goto out_error; + } + const byte *p = find_subbytes(start, end - start, needle, needle_len, direction); if (p == NULL) { + out_error: // not found if (is_index) { mp_raise_ValueError("substring not found"); diff --git a/MicroPython_BUILD/components/micropython/py/objstringio.c b/MicroPython_BUILD/components/micropython/py/objstringio.c index 5c50aa3..b405ee2 100644 --- a/MicroPython_BUILD/components/micropython/py/objstringio.c +++ b/MicroPython_BUILD/components/micropython/py/objstringio.c @@ -143,6 +143,17 @@ STATIC mp_uint_t stringio_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, } case MP_STREAM_FLUSH: return 0; + case MP_STREAM_CLOSE: + #if MICROPY_CPYTHON_COMPAT + vstr_free(o->vstr); + o->vstr = NULL; + #else + vstr_clear(o->vstr); + o->vstr->alloc = 0; + o->vstr->len = 0; + o->pos = 0; + #endif + return 0; default: *errcode = MP_EINVAL; return MP_STREAM_ERROR; @@ -159,24 +170,9 @@ STATIC mp_obj_t stringio_getvalue(mp_obj_t self_in) { } STATIC MP_DEFINE_CONST_FUN_OBJ_1(stringio_getvalue_obj, stringio_getvalue); -STATIC mp_obj_t stringio_close(mp_obj_t self_in) { - mp_obj_stringio_t *self = MP_OBJ_TO_PTR(self_in); -#if MICROPY_CPYTHON_COMPAT - vstr_free(self->vstr); - self->vstr = NULL; -#else - vstr_clear(self->vstr); - self->vstr->alloc = 0; - self->vstr->len = 0; - self->pos = 0; -#endif - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(stringio_close_obj, stringio_close); - STATIC mp_obj_t stringio___exit__(size_t n_args, const mp_obj_t *args) { (void)n_args; - return stringio_close(args[0]); + return mp_stream_close(args[0]); } STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(stringio___exit___obj, 4, 4, stringio___exit__); @@ -233,7 +229,7 @@ STATIC const mp_rom_map_elem_t stringio_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, { MP_ROM_QSTR(MP_QSTR_seek), MP_ROM_PTR(&mp_stream_seek_obj) }, { MP_ROM_QSTR(MP_QSTR_flush), MP_ROM_PTR(&mp_stream_flush_obj) }, - { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&stringio_close_obj) }, + { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_close_obj) }, { MP_ROM_QSTR(MP_QSTR_getvalue), MP_ROM_PTR(&stringio_getvalue_obj) }, { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&mp_identity_obj) }, { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&stringio___exit___obj) }, diff --git a/MicroPython_BUILD/components/micropython/py/runtime.c b/MicroPython_BUILD/components/micropython/py/runtime.c index 219ec22..4efb29b 100644 --- a/MicroPython_BUILD/components/micropython/py/runtime.c +++ b/MicroPython_BUILD/components/micropython/py/runtime.c @@ -83,8 +83,10 @@ void mp_init(void) { MICROPY_PORT_INIT_FUNC; #endif + #if MICROPY_ENABLE_COMPILER // optimization disabled by default MP_STATE_VM(mp_optimise_value) = 0; + #endif // init global module dict mp_obj_dict_init(&MP_STATE_VM(mp_loaded_modules_dict), 3); diff --git a/MicroPython_BUILD/components/micropython/py/stream.c b/MicroPython_BUILD/components/micropython/py/stream.c index 453dee7..f51f634 100644 --- a/MicroPython_BUILD/components/micropython/py/stream.c +++ b/MicroPython_BUILD/components/micropython/py/stream.c @@ -105,13 +105,6 @@ const mp_stream_p_t *mp_get_stream_raise(mp_obj_t self_in, int flags) { return stream_p; } -mp_obj_t mp_stream_close(mp_obj_t stream) { - // TODO: Still consider using ioctl for close - mp_obj_t dest[2]; - mp_load_method(stream, MP_QSTR_close, dest); - return mp_call_method_n_kw(0, 0, dest); -} - STATIC mp_obj_t stream_read_generic(size_t n_args, const mp_obj_t *args, byte flags) { const mp_stream_p_t *stream_p = mp_get_stream_raise(args[0], MP_STREAM_OP_READ); @@ -434,6 +427,17 @@ mp_obj_t mp_stream_unbuffered_iter(mp_obj_t self) { return MP_OBJ_STOP_ITERATION; } +mp_obj_t mp_stream_close(mp_obj_t stream) { + const mp_stream_p_t *stream_p = mp_get_stream_raise(stream, MP_STREAM_OP_IOCTL); + int error; + mp_uint_t res = stream_p->ioctl(stream, MP_STREAM_CLOSE, 0, &error); + if (res == MP_STREAM_ERROR) { + mp_raise_OSError(error); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(mp_stream_close_obj, mp_stream_close); + STATIC mp_obj_t stream_seek(size_t n_args, const mp_obj_t *args) { const mp_stream_p_t *stream_p = mp_get_stream_raise(args[0], MP_STREAM_OP_IOCTL); diff --git a/MicroPython_BUILD/components/micropython/py/stream.h b/MicroPython_BUILD/components/micropython/py/stream.h index fbe3d7d..a7d8d08 100644 --- a/MicroPython_BUILD/components/micropython/py/stream.h +++ b/MicroPython_BUILD/components/micropython/py/stream.h @@ -35,7 +35,7 @@ #define MP_STREAM_FLUSH (1) #define MP_STREAM_SEEK (2) #define MP_STREAM_POLL (3) -//#define MP_STREAM_CLOSE (4) // Not yet implemented +#define MP_STREAM_CLOSE (4) #define MP_STREAM_TIMEOUT (5) // Get/set timeout (single op) #define MP_STREAM_GET_OPTS (6) // Get stream options #define MP_STREAM_SET_OPTS (7) // Set stream options @@ -69,6 +69,7 @@ MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_unbuffered_readline_obj); MP_DECLARE_CONST_FUN_OBJ_1(mp_stream_unbuffered_readlines_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_write_obj); MP_DECLARE_CONST_FUN_OBJ_2(mp_stream_write1_obj); +MP_DECLARE_CONST_FUN_OBJ_1(mp_stream_close_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_seek_obj); MP_DECLARE_CONST_FUN_OBJ_1(mp_stream_tell_obj); MP_DECLARE_CONST_FUN_OBJ_1(mp_stream_flush_obj); diff --git a/MicroPython_BUILD/components/micropython/py/vm.c b/MicroPython_BUILD/components/micropython/py/vm.c index d913f20..7281e2b 100644 --- a/MicroPython_BUILD/components/micropython/py/vm.c +++ b/MicroPython_BUILD/components/micropython/py/vm.c @@ -5,8 +5,8 @@ * * Copyright (c) 2013, 2014 Damien P. George * Copyright (c) 2014 Paul Sokolovsky -* Copyright (c) 2018 LoBo (https://github.com/loboris) - * + * Copyright (c) 2018 LoBo (https://github.com/loboris) + * * 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 @@ -914,21 +914,22 @@ unwind_jump:; code_state->sp = sp; code_state->exc_sp = MP_TAGPTR_MAKE(exc_sp, currently_in_except_block); mp_code_state_t *new_state = mp_obj_fun_bc_prepare_codestate(*sp, unum & 0xff, (unum >> 8) & 0xff, sp + 1); - if (new_state) { + #if !MICROPY_ENABLE_PYSTACK + if (new_state == NULL) { + // Couldn't allocate codestate on heap: in the strict case raise + // an exception, otherwise just fall through to stack allocation. + #if MICROPY_STACKLESS_STRICT + deep_recursion_error: + mp_raise_recursion_depth(); + #endif + } else + #endif + { new_state->prev = code_state; code_state = new_state; nlr_pop(); goto run_code_state; } - #if MICROPY_STACKLESS_STRICT - else { - deep_recursion_error: - mp_raise_recursion_depth(); - } - #else - // If we couldn't allocate codestate on heap, in - // non non-strict case fall thru to stack allocation. - #endif } #endif SET_TOP(mp_call_function_n_kw(*sp, unum & 0xff, (unum >> 8) & 0xff, sp + 1)); @@ -959,20 +960,21 @@ unwind_jump:; // pystack is not enabled. For pystack, they are freed when code_state is. mp_nonlocal_free(out_args.args, out_args.n_alloc * sizeof(mp_obj_t)); #endif - if (new_state) { + #if !MICROPY_ENABLE_PYSTACK + if (new_state == NULL) { + // Couldn't allocate codestate on heap: in the strict case raise + // an exception, otherwise just fall through to stack allocation. + #if MICROPY_STACKLESS_STRICT + goto deep_recursion_error; + #endif + } else + #endif + { new_state->prev = code_state; code_state = new_state; nlr_pop(); goto run_code_state; } - #if MICROPY_STACKLESS_STRICT - else { - goto deep_recursion_error; - } - #else - // If we couldn't allocate codestate on heap, in - // non non-strict case fall thru to stack allocation. - #endif } #endif SET_TOP(mp_call_method_n_kw_var(false, unum, sp)); @@ -996,20 +998,21 @@ unwind_jump:; int adjust = (sp[1] == MP_OBJ_NULL) ? 0 : 1; mp_code_state_t *new_state = mp_obj_fun_bc_prepare_codestate(*sp, n_args + adjust, n_kw, sp + 2 - adjust); - if (new_state) { + #if !MICROPY_ENABLE_PYSTACK + if (new_state == NULL) { + // Couldn't allocate codestate on heap: in the strict case raise + // an exception, otherwise just fall through to stack allocation. + #if MICROPY_STACKLESS_STRICT + goto deep_recursion_error; + #endif + } else + #endif + { new_state->prev = code_state; code_state = new_state; nlr_pop(); goto run_code_state; } - #if MICROPY_STACKLESS_STRICT - else { - goto deep_recursion_error; - } - #else - // If we couldn't allocate codestate on heap, in - // non non-strict case fall thru to stack allocation. - #endif } #endif SET_TOP(mp_call_method_n_kw(unum & 0xff, (unum >> 8) & 0xff, sp)); @@ -1040,20 +1043,21 @@ unwind_jump:; // pystack is not enabled. For pystack, they are freed when code_state is. mp_nonlocal_free(out_args.args, out_args.n_alloc * sizeof(mp_obj_t)); #endif - if (new_state) { + #if !MICROPY_ENABLE_PYSTACK + if (new_state == NULL) { + // Couldn't allocate codestate on heap: in the strict case raise + // an exception, otherwise just fall through to stack allocation. + #if MICROPY_STACKLESS_STRICT + goto deep_recursion_error; + #endif + } else + #endif + { new_state->prev = code_state; code_state = new_state; nlr_pop(); goto run_code_state; } - #if MICROPY_STACKLESS_STRICT - else { - goto deep_recursion_error; - } - #else - // If we couldn't allocate codestate on heap, in - // non non-strict case fall thru to stack allocation. - #endif } #endif SET_TOP(mp_call_method_n_kw_var(true, unum, sp)); @@ -1121,7 +1125,7 @@ unwind_return: ENTRY(MP_BC_RAISE_VARARGS): { MARK_EXC_IP_SELECTIVE(); - mp_uint_t unum = *ip++; + mp_uint_t unum = *ip; mp_obj_t obj; if (unum == 2) { mp_warning("exception chaining not supported"); @@ -1142,7 +1146,7 @@ unwind_return: RAISE(obj); } } else { - obj = POP(); + obj = TOP(); } obj = mp_make_raise_obj(obj); RAISE(obj); diff --git a/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32.zip b/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32.zip index 28d7b16..1715006 100644 Binary files a/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32.zip and b/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32.zip differ diff --git a/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32_all.zip b/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32_all.zip index 9b6dbfe..43ffe31 100644 Binary files a/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32_all.zip and b/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32_all.zip differ diff --git a/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32_ota.zip b/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32_ota.zip index 476917e..2f115a3 100644 Binary files a/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32_ota.zip and b/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32_ota.zip differ diff --git a/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32_psram.zip b/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32_psram.zip index 17655c8..a9afbb4 100644 Binary files a/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32_psram.zip and b/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32_psram.zip differ diff --git a/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32_psram_all.zip b/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32_psram_all.zip index 4945c8b..78cf950 100644 Binary files a/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32_psram_all.zip and b/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32_psram_all.zip differ diff --git a/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32_psram_ota.zip b/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32_psram_ota.zip index dd1dfe0..1e54afc 100644 Binary files a/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32_psram_ota.zip and b/MicroPython_BUILD/firmware/MicroPython_LoBo_esp32_psram_ota.zip differ diff --git a/MicroPython_BUILD/firmware/esp32_all/MicroPython.bin b/MicroPython_BUILD/firmware/esp32_all/MicroPython.bin index 50b7c1e..3cc8a9f 100644 Binary files a/MicroPython_BUILD/firmware/esp32_all/MicroPython.bin and b/MicroPython_BUILD/firmware/esp32_all/MicroPython.bin differ diff --git a/MicroPython_BUILD/firmware/esp32_all/bootloader/bootloader.bin b/MicroPython_BUILD/firmware/esp32_all/bootloader/bootloader.bin index 7ba4b8a..eba0c0e 100644 Binary files a/MicroPython_BUILD/firmware/esp32_all/bootloader/bootloader.bin and b/MicroPython_BUILD/firmware/esp32_all/bootloader/bootloader.bin differ diff --git a/MicroPython_BUILD/firmware/esp32_all/partitions_mpy.bin b/MicroPython_BUILD/firmware/esp32_all/partitions_mpy.bin index f37f3bf..0bdd9d7 100644 Binary files a/MicroPython_BUILD/firmware/esp32_all/partitions_mpy.bin and b/MicroPython_BUILD/firmware/esp32_all/partitions_mpy.bin differ diff --git a/MicroPython_BUILD/firmware/esp32_ota/MicroPython.bin b/MicroPython_BUILD/firmware/esp32_ota/MicroPython.bin index 354100f..c39bc31 100644 Binary files a/MicroPython_BUILD/firmware/esp32_ota/MicroPython.bin and b/MicroPython_BUILD/firmware/esp32_ota/MicroPython.bin differ diff --git a/MicroPython_BUILD/firmware/esp32_ota/bootloader/bootloader.bin b/MicroPython_BUILD/firmware/esp32_ota/bootloader/bootloader.bin index 9fc813e..5782405 100644 Binary files a/MicroPython_BUILD/firmware/esp32_ota/bootloader/bootloader.bin and b/MicroPython_BUILD/firmware/esp32_ota/bootloader/bootloader.bin differ diff --git a/MicroPython_BUILD/firmware/esp32_psram_all/MicroPython.bin b/MicroPython_BUILD/firmware/esp32_psram_all/MicroPython.bin index ca7d152..f367953 100644 Binary files a/MicroPython_BUILD/firmware/esp32_psram_all/MicroPython.bin and b/MicroPython_BUILD/firmware/esp32_psram_all/MicroPython.bin differ diff --git a/MicroPython_BUILD/firmware/esp32_psram_all/bootloader/bootloader.bin b/MicroPython_BUILD/firmware/esp32_psram_all/bootloader/bootloader.bin index 5fd2a75..bffbd19 100644 Binary files a/MicroPython_BUILD/firmware/esp32_psram_all/bootloader/bootloader.bin and b/MicroPython_BUILD/firmware/esp32_psram_all/bootloader/bootloader.bin differ diff --git a/MicroPython_BUILD/firmware/esp32_psram_ota/MicroPython.bin b/MicroPython_BUILD/firmware/esp32_psram_ota/MicroPython.bin index 3d0879a..9b2988e 100644 Binary files a/MicroPython_BUILD/firmware/esp32_psram_ota/MicroPython.bin and b/MicroPython_BUILD/firmware/esp32_psram_ota/MicroPython.bin differ diff --git a/MicroPython_BUILD/firmware/esp32_psram_ota/bootloader/bootloader.bin b/MicroPython_BUILD/firmware/esp32_psram_ota/bootloader/bootloader.bin index 0b5094d..ef9fd22 100644 Binary files a/MicroPython_BUILD/firmware/esp32_psram_ota/bootloader/bootloader.bin and b/MicroPython_BUILD/firmware/esp32_psram_ota/bootloader/bootloader.bin differ diff --git a/Tools/esp-idf.tar.xz b/Tools/esp-idf.tar.xz index 5985094..e11f698 100644 Binary files a/Tools/esp-idf.tar.xz and b/Tools/esp-idf.tar.xz differ