Merge branch 'master' into m5stack-dev

This commit is contained in:
0x1abin
2018-04-16 10:53:00 +08:00
68 changed files with 1470 additions and 2354 deletions
+1 -1
View File
@@ -48,7 +48,7 @@
#=======================
TOOLS_VER=ver20180408.id
TOOLS_VER=ver20180412.id
#=======================
# -----------------------------
@@ -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;
@@ -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
@@ -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;
@@ -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
@@ -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) },
@@ -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) {
@@ -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();
@@ -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) {
File diff suppressed because it is too large Load Diff
@@ -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);
@@ -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 },
+25 -11
View File
@@ -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
@@ -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
# ============================================================================
# ============================================================================
# ============================================================================
+140 -31
View File
@@ -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', '<uID>', 'addresses', '<addrID>', 'test', '<anotherID>']
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))
# ------------------------------------------------------------------------
File diff suppressed because it is too large Load Diff
+25 -3
View File
@@ -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)
# ============================================================================
# ============================================================================
# ============================================================================
@@ -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)
# ============================================================================
# ============================================================================
# ============================================================================
@@ -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
@@ -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 <IP>/test.pyhtml in your browser
# ----------------------------------------------------------------------
# ------------------------------------------------------------
# To test websocket page goto <IP>/wstest.html in your browser
# ------------------------------------------------------------
# -----------------------------------------------------
# Define microWebServer route handlers using decorators
# -----------------------------------------------------
# <IP>/TEST
@MicroWebSrv.route('/TEST')
def _httpHandlerTestGet(httpClient, httpResponse) :
content = """\
<!DOCTYPE html>
<html lang=fr>
<html lang=en>
<head>
<meta charset="UTF-8" />
<title>TEST GET</title>
@@ -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 = """\
<!DOCTYPE html>
<html lang=fr>
<html lang=en>
<head>
<meta charset="UTF-8" />
<title>TEST POST</title>
@@ -56,12 +81,48 @@ def _httpHandlerTestPost(httpClient, httpResponse) :
contentCharset = "UTF-8",
content = content )
# ----------------------------------------------------------------------------
@MicroWebSrv.route('/edit/<index>') # <IP>/edit/123 -> args['index']=123
@MicroWebSrv.route('/edit/<index>/abc/<foo>') # <IP>/edit/123/abc/bar -> args['index']=123 args['foo']='bar'
@MicroWebSrv.route('/edit') # <IP>/edit -> args={}
def _httpHandlerEditWithArgs(httpClient, httpResponse, args={}) :
content = """\
<!DOCTYPE html>
<html lang=en>
<head>
<meta charset="UTF-8" />
<title>TEST EDIT</title>
</head>
<body>
"""
content += "<h1>EDIT item with {} variable arguments</h1>"\
.format(len(args))
if 'index' in args :
content += "<p>index = {}</p>".format(args['index'])
if 'foo' in args :
content += "<p>foo = {}</p>".format(args['foo'])
content += """
</body>
</html>
"""
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)
# ----------------------------------------------------------------------------

Some files were not shown because too many files have changed in this diff Show More