diff --git a/esp32/frozen/Pybytes/_OTA.py b/esp32/frozen/Pybytes/_OTA.py index 1750d0fd45..48f8a1aac1 100644 --- a/esp32/frozen/Pybytes/_OTA.py +++ b/esp32/frozen/Pybytes/_OTA.py @@ -63,10 +63,12 @@ def get_update_manifest(self, fwtype=None, token=None): wmac = hexlify(machine.unique_id()).decode('ascii') if fwtype == 'pymesh': request_template = "manifest.json?current_ver={}&sysname={}&token={}&ota_slot={}&wmac={}&fwtype={}¤t_fwtype={}" - req = request_template.format(current_version, sysname, token, hex(pycom.ota_slot()), wmac.upper(), fwtype, 'pymesh' if hasattr(os.uname(),'pymesh') else 'pybytes') + current_fwtype = 'pymesh' if hasattr(os.uname(), 'pymesh') else 'pybytes' + req = request_template.format(current_version, sysname, token, hex(pycom.ota_slot()), wmac.upper(), fwtype, current_fwtype) elif fwtype == 'pygate': request_template = "manifest.json?current_ver={}&sysname={}&ota_slot={}&wmac={}&fwtype={}¤t_fwtype={}" - req = request_template.format(current_version, sysname, hex(pycom.ota_slot()), wmac.upper(), fwtype, 'pygate' if hasattr(os.uname(),'pygate') else 'pybytes') + current_fwtype = 'pygate' if hasattr(os.uname(), 'pygate') else 'pybytes' + req = request_template.format(current_version, sysname, hex(pycom.ota_slot()), wmac.upper(), fwtype, current_fwtype) else: request_template = "manifest.json?current_ver={}&sysname={}&wmac={}&ota_slot={}" req = request_template.format(current_version, sysname, wmac, hex(pycom.ota_slot())) diff --git a/esp32/frozen/Pybytes/_flash_control_OTA.py b/esp32/frozen/Pybytes/_flash_control_OTA.py index 79e5867d30..78682f2ba2 100644 --- a/esp32/frozen/Pybytes/_flash_control_OTA.py +++ b/esp32/frozen/Pybytes/_flash_control_OTA.py @@ -17,10 +17,20 @@ class FCOTA: def __init__(self): pass + def is_folder(self, name): + try: + os.listdir(name) + return True + except: + return False + + def is_file(self, name): + return not self.is_folder(name) + def update_file_content(self, path, newContent): print_debug(2, 'Updating file [{}]'.format(path)) - if '.' in path: + if self.is_file(path): listfDir = path.split('/') currentPath = '/' for value in listfDir: @@ -34,7 +44,7 @@ def update_file_content(self, path, newContent): # check if dir exists if value not in parentList: # create dir - if '.' in currentPath: + if self.is_file(currentPath): continue os.mkdir(currentPath) @@ -52,7 +62,7 @@ def update_file_content(self, path, newContent): def delete_file(self, path): print_debug(2, 'FCOTA deleting file [{}]'.format(path)) try: - if ('.' in path): + if self.is_file(path): os.remove(path) else: targetedFiles = [] @@ -67,7 +77,7 @@ def delete_file(self, path): while maxDepth >= 0: for elem in targetedFiles: if elem.count('/') == maxDepth: - if '.' in elem: + if self.is_file(elem): os.remove(elem) else: os.rmdir(elem) @@ -87,7 +97,7 @@ def convert_bytes(self, num): def get_file_size(self, path): print_debug(2, 'FCOTA getting file infos [{}]'.format(path)) - if '.' in path: + if self.is_file(path): fileInfo = os.stat(path) print_debug(2, 'printing fileInfo tupple: ' + str(fileInfo)) return self.convert_bytes(fileInfo[6]) @@ -96,7 +106,7 @@ def get_file_size(self, path): def get_file_content(self, path): print_debug(2, 'FCOTA reading file [{}]'.format(path)) - if '.' in path: + if self.is_file(path): f = open(path, 'r') content = f.read() f.close() @@ -113,7 +123,7 @@ def get_flash_hierarchy(self): hierarchy = os.listdir() folders = [] for elem in hierarchy: - if '.' not in elem: + if self.is_folder(elem): folders.append(elem) while len(folders) > 0: @@ -130,7 +140,7 @@ def get_flash_hierarchy(self): path = folders[i] + '/' + subFolders[j] hierarchy.append(path) - if '.' not in path: + if self.is_folder(path): foldersToCheck.append(path) j += 1 diff --git a/esp32/frozen/Pybytes/_main_pybytes.py b/esp32/frozen/Pybytes/_main_pybytes.py index 2afd232a9b..668ddd0e76 100644 --- a/esp32/frozen/Pybytes/_main_pybytes.py +++ b/esp32/frozen/Pybytes/_main_pybytes.py @@ -96,4 +96,4 @@ # def custom_print(params): # print("Custom method called") # return [255, 20] - # pybytes.add_custom_method(0, custom_print) + # pybytes.add_custom_method(0, custom_print) \ No newline at end of file diff --git a/esp32/frozen/Pybytes/_msg_handl.py b/esp32/frozen/Pybytes/_msg_handl.py index 421c9e6e47..d045d239c9 100644 --- a/esp32/frozen/Pybytes/_msg_handl.py +++ b/esp32/frozen/Pybytes/_msg_handl.py @@ -31,6 +31,7 @@ def __init__( receive_timeout=3000, reconnectMethod=None ): + print_debug(5, 'starting new MsgHandler') self._host = "" self._port = -1 self._sock = None @@ -152,7 +153,8 @@ def _receive_packet(self): try: self._sock.setblocking(False) msg_type = self._sock.recv(1) - except socket.error: + except socket.error as err: + print_debug(2, '_receive_packet() socket error: {}'.format(err)) self.disconnect() self.reconnectMethod() return False @@ -213,7 +215,7 @@ def _send_packet(self, packet): else: print_debug(2, 'Packet sent. (Length: %d)' % written) except socket.error as err: - print_debug(2, 'Socket send error {0}'.format(err)) + print_debug(2, '_send_packet() socket error {0}'.format(err)) return False return True if len(packet) == written else False diff --git a/esp32/frozen/Pybytes/_pybytes.py b/esp32/frozen/Pybytes/_pybytes.py index 2516982c0b..de5697df42 100644 --- a/esp32/frozen/Pybytes/_pybytes.py +++ b/esp32/frozen/Pybytes/_pybytes.py @@ -11,9 +11,10 @@ import time import pycom import sys +import gc from network import WLAN from binascii import hexlify, a2b_base64 -from machine import Timer, deepsleep, pin_sleep_wakeup, unique_id +from machine import Timer, deepsleep, pin_sleep_wakeup, unique_id, pygate_init, RTC, pygate_debug_level, reset try: from periodical_pin import PeriodicalPin @@ -21,9 +22,9 @@ from _periodical_pin import PeriodicalPin try: - from pybytes_debug import print_debug + from pybytes_debug import print_debug, DEBUG except: - from _pybytes_debug import print_debug + from _pybytes_debug import print_debug, DEBUG try: from pybytes_config_reader import PybytesConfigReader @@ -36,7 +37,7 @@ class Pybytes: WAKEUP_ALL_LOW = const(0) # noqa: F821 WAKEUP_ANY_HIGH = const(1) # noqa: F821 - def __init__(self, config, activation=False, autoconnect=False): + def __init__(self, config, activation=False, autoconnect=False, user_callback=None): self.__frozen = globals().get('__name__') == '_pybytes' self.__activation = activation self.__custom_message_callback = None @@ -45,11 +46,15 @@ def __init__(self, config, activation=False, autoconnect=False): self.__smart_config = False self.__conf = {} self.__pymesh = None + self.__user_callback = user_callback if not self.__activation: self.__conf = config self.__conf_reader = PybytesConfigReader(config) pycom.wifi_on_boot(False, True) + if pycom.lte_modem_en_on_boot(): + pycom.lte_modem_en_on_boot(False) + reset() self.__check_dump_ca() self.__create_pybytes_connection(self.__conf) @@ -66,7 +71,7 @@ def __create_pybytes_connection(self, conf): except: from _pybytes_connection import PybytesConnection - self.__pybytes_connection = PybytesConnection(conf, self.__recv_message) + self.__pybytes_connection = PybytesConnection(conf, self.__recv_message, user_callback=self.__user_callback) def __check_config(self): try: @@ -155,9 +160,9 @@ def send_node_signal(self, signal_number, value, token): topic = 'br/{}'.format(token) self.__pybytes_connection.__pybytes_protocol.send_pybytes_custom_method_values(signal_number, [value], topic) - def send_signal(self, signal_number, value): + def send_signal(self, signal_number, value, nomesh=False): self.__check_init() - if self.__pymesh: + if self.__pymesh and not nomesh: self.__pymesh.unpack_pymesh_message(signal_number, value) else: self.__pybytes_connection.__pybytes_protocol.send_pybytes_custom_method_values(signal_number, [value]) @@ -280,11 +285,51 @@ def connect(self): from _pybytes_pymesh_config import PybytesPymeshConfig self.__pymesh = PybytesPymeshConfig(self) self.__pymesh.pymesh_init() + elif hasattr(os.uname(), 'pygate') and self.get_config('gateway'): + # PYGATE FIRMWARE VERSION + buf = None + try: + with open('/flash/pybytes_pygate_config.json','r') as fp: + buf = fp.read() + except Exception as e: + print_debug(5, e) + print('pybytes_pygate_config.json file is missing or has wrong format') + return + + try: + print('Syncing RTC via ntp...', end='') + rtc = RTC() + if not rtc.synced(): + rtc.ntp_sync(server="pool.ntp.org") + to_s = 20 + while not rtc.synced() and to_s > 0: + print('.', end='') + time.sleep(1) + to_s -= 1 + if not rtc.synced(): + print('RTC sync failed. Gateway will not work') + return + print(" RTC synced") + + except Exception as e: + print_debug(5, e) + print('RTC sync failed. Gateway will not work') + return + + try: + gc.collect() + if not DEBUG: + pygate_debug_level(0) + pygate_init(buf) + except Exception as e: + print('Pygate failed to start', e) + return else: print('ERROR! Could not connect to Pybytes!') except Exception as ex: print("Unable to connect to Pybytes: {}".format(ex)) + sys.print_exception(ex) def write_config(self, file='/flash/pybytes_config.json', silent=False): try: @@ -428,6 +473,12 @@ def deepsleep(self, ms, pins=None, mode=None, enable_pull=None): self.disconnect() deepsleep(ms) + def message_queue_len(self): + try: + return len(pybytes.__pybytes_connection.__connection.__mqtt._msgHandler._output_queue) + except: + return None + def dump_ca(self, ca_file='/flash/cert/pycom-ca.pem'): try: from _pybytes_ca import PYBYTES_CA diff --git a/esp32/frozen/Pybytes/_pybytes_connection.py b/esp32/frozen/Pybytes/_pybytes_connection.py index e0a59ed73d..d24fe85c25 100644 --- a/esp32/frozen/Pybytes/_pybytes_connection.py +++ b/esp32/frozen/Pybytes/_pybytes_connection.py @@ -46,7 +46,7 @@ class PybytesConnection: - def __init__(self, config, message_callback): + def __init__(self, config, message_callback, user_callback=None): if config is not None: self.__conf = config self.__conf_reader = PybytesConfigReader(config) @@ -61,7 +61,8 @@ def __init__(self, config, message_callback): self.__mqtt_download_topic = "d" + self.__device_id self.__mqtt_upload_topic = "u" + self.__device_id self.__pybytes_protocol = PybytesProtocol( - config, message_callback, pybytes_connection=self + config, message_callback, pybytes_connection=self, + user_callback=user_callback ) self.__connection = None self.__connection_status = constants.__CONNECTION_STATUS_DISCONNECTED diff --git a/esp32/frozen/Pybytes/_pybytes_constants.py b/esp32/frozen/Pybytes/_pybytes_constants.py index 4e5463dcba..8d2efc54b1 100644 --- a/esp32/frozen/Pybytes/_pybytes_constants.py +++ b/esp32/frozen/Pybytes/_pybytes_constants.py @@ -75,6 +75,7 @@ class constants: __TYPE_PYMESH = 0x0D __TYPE_PYBYTES = 0x0E __TYPE_ML = 0x0F + __TYPE_USER = 0x08 __PYBYTES_PROTOCOL = ">B%ds" __PYBYTES_PROTOCOL_PING = ">B" __PYBYTES_INTERNAL_PROTOCOL = ">BBH" @@ -100,6 +101,8 @@ class constants: __FCOTA_PING = 0x03 __FCOTA_COMMAND_FILE_DELETE = 0x04 __FCOTA_COMMAND_FILE_UPDATE_NO_RESET = 0x05 + __FCOTA_COMMAND_GATEWAY_DEPLOY = 0x06 + __FCOTA_COMMAND_TOGGLE_GATEWAY = 0x07 __DEVICE_TYPE_WIPY = 0x00 __DEVICE_TYPE_LOPY = 0x01 diff --git a/esp32/frozen/Pybytes/_pybytes_debug.py b/esp32/frozen/Pybytes/_pybytes_debug.py index afb950dae3..36ad935fe4 100644 --- a/esp32/frozen/Pybytes/_pybytes_debug.py +++ b/esp32/frozen/Pybytes/_pybytes_debug.py @@ -8,6 +8,8 @@ import os import pycom # pylint: disable=import-error +import _thread +import time from machine import RTC from time import timezone @@ -22,7 +24,11 @@ def print_debug(level, msg): """Print log messages into console.""" if DEBUG is not None and level <= DEBUG: - print(msg) + print("[{:15.3f}] [{:10d}] {}".format( + time.ticks_ms() / 1000, + _thread.get_ident(), + msg + )) def print_debug_local(level, msg): diff --git a/esp32/frozen/Pybytes/_pybytes_library.py b/esp32/frozen/Pybytes/_pybytes_library.py index fd4fa2abb1..6cc5d040c5 100644 --- a/esp32/frozen/Pybytes/_pybytes_library.py +++ b/esp32/frozen/Pybytes/_pybytes_library.py @@ -94,10 +94,13 @@ def pack_info_message(self, releaseVersion=None): if hasattr(os.uname(), 'pymesh'): body.append(constants.__FWTYPE_PYMESH) + print_debug(4, "pymesh") elif hasattr(os.uname(), 'pygate'): body.append(constants.__FWTYPE_PYGATE) + print_debug(4, "pygate") else: body.append(constants.__FWTYPE_DEFAULT) + print_debug(4, "pybytes") return self.__pack_message(constants.__TYPE_INFO, body) diff --git a/esp32/frozen/Pybytes/_pybytes_protocol.py b/esp32/frozen/Pybytes/_pybytes_protocol.py index 190dd8eb78..94c4d90bcb 100644 --- a/esp32/frozen/Pybytes/_pybytes_protocol.py +++ b/esp32/frozen/Pybytes/_pybytes_protocol.py @@ -50,6 +50,12 @@ from pybytes_debug import print_debug except: from _pybytes_debug import print_debug + +try: + from pybytes_pyconfig import Pyconfig +except: + from _pybytes_pyconfig import Pyconfig + try: import urequest except: @@ -64,6 +70,7 @@ from network import Coap import os +import sys import _thread import time import struct @@ -73,7 +80,7 @@ class PybytesProtocol: - def __init__(self, config, message_callback, pybytes_connection): + def __init__(self, config, message_callback, pybytes_connection, user_callback=None): self.__conf = config self.__conf_reader = PybytesConfigReader(config) self.__thread_stack_size = 8192 @@ -84,6 +91,7 @@ def __init__(self, config, message_callback, pybytes_connection): self.__pybytes_library = PybytesLibrary( pybytes_connection=pybytes_connection, pybytes_protocol=self) self.__user_message_callback = message_callback + self.__user_callback = user_callback self.__pins = {} self.__pin_modes = {} self.__custom_methods = {} @@ -150,19 +158,28 @@ def __check_lora_messages(self): def __process_recv_message(self, message): print_debug(5, "This is PybytesProtocol.__process_recv_message()") - if message.payload: network_type, message_type, body = self.__pybytes_library.unpack_message(message.payload) + print_debug(99,'Message: network_type={}, message_type={}, body={}'.format(network_type, message_type, body)) else: # for lora messages network_type, message_type, body = self.__pybytes_library.unpack_message(message) + print_debug(99,'Message: network_type={}, message_type={}, body={}'.format(network_type, message_type, body)) if self.__user_message_callback is not None: + if (message_type == constants.__TYPE_PING): self.send_ping_message() elif message_type == constants.__TYPE_PONG and self.__conf.get('connection_watchdog', True): self.__pybytes_connection.__wifi_lte_watchdog.feed() + print_debug(5, 'message type is PONG, feed watchdog') + + elif (message_type == constants.__TYPE_USER and self.__user_callback is not None): + try: + self.__user_callback(body) + except Exception as ex: + sys.print_exception(ex) elif (message_type == constants.__TYPE_INFO): self.send_info_message() @@ -314,6 +331,21 @@ def __process_recv_message(self, message): else: self.send_fcota_ping('deletion failed!') + elif (command == constants.__FCOTA_COMMAND_GATEWAY_DEPLOY): + self.deploy_gateway() + + elif (command == constants.__FCOTA_COMMAND_TOGGLE_GATEWAY): + bodyString = body[1:len(body)].decode() + try: + self.toggleGateway( + True if bodyString == 'true' else False + ) + machine.reset() + except Exception as ex: + print( + 'error toggleGateway({}):{}'.format(bodyString, ex) + ) + else: print_debug(2, "Unknown FCOTA command received") @@ -836,3 +868,41 @@ def deploy_new_release(self, body): self.update_network_config(letResp) self.update_firmware(letResp, applicationID, fw_type=fwtype) machine.reset() + + def deploy_gateway(self): + print_debug(0, 'deploying gateway...') + pyconfig_url = '{}://{}'.format( + constants.__DEFAULT_PYCONFIG_PROTOCOL, + constants.__DEFAULT_PYCONFIG_DOMAIN + ) + device_id = self.__conf['device_id'] + pyconfig = Pyconfig(pyconfig_url, device_id) + pygate_configurations = pyconfig.get_gateway_config() + + print_debug(5, pygate_configurations) + + json_pygate_string = ujson.dumps(pygate_configurations) + self.__FCOTA.update_file_content( + '/flash/pybytes_pygate_config.json', + json_pygate_string + ) + self.toggleGateway(True) + + if not hasattr(os.uname(), 'pygate'): + print_debug(0, 'OTA deploy Pygate Firmware') + ota = WiFiOTA( + self.__conf['wifi']['ssid'], + self.__conf['wifi']['password'], + self.__conf['ota_server']['domain'], + self.__conf['ota_server']['port'] + ) + ota.update(fwtype='pygate', token=device_id) + machine.reset() + + def toggleGateway(self, value): + self.__conf['gateway'] = value + json_string = ujson.dumps(self.__conf) + self.__FCOTA.update_file_content( + '/flash/pybytes_config.json', + json_string + ) diff --git a/esp32/frozen/Pybytes/_pybytes_pyconfig.py b/esp32/frozen/Pybytes/_pybytes_pyconfig.py new file mode 100644 index 0000000000..fa41bb9189 --- /dev/null +++ b/esp32/frozen/Pybytes/_pybytes_pyconfig.py @@ -0,0 +1,44 @@ +''' +Copyright (c) 2020, Pycom Limited. +This software is licensed under the GNU GPL version 3 or any +later version, with permitted additional terms. For more information +see the Pycom Licence v1.0 document supplied with this file, or +available at https://www.pycom.io/opensource/licensing +''' +try: + from pybytes_debug import print_debug +except: + from _pybytes_debug import print_debug + +try: + import urequest +except: + import _urequest as urequest + + +class Pyconfig: + """ + A class used to represent communication with pybytes + all pybytes communication should happen with one rest api + called pyconfig microServices + + + """ + + def __init__(self, url, device_id): + self.base_url = url + self.device_id = device_id + + def get_gateway_config(self): + target_url = '{}/device/gateway/{}'.format(self.base_url, self.device_id) + headers = { + 'content-type': 'application/json' + } + try: + request = urequest.get(target_url, headers=headers) + configurations = request.json() + request.close() + print_debug(6, "Response Details: {}".format(configurations)) + return configurations + except Exception as ex: + print_debug(1, "error while calling {}!: {}".format(target_url, ex)) diff --git a/esp32/littlefs/sflash_diskio_littlefs.c b/esp32/littlefs/sflash_diskio_littlefs.c index f386cf47f4..ea61ada52c 100644 --- a/esp32/littlefs/sflash_diskio_littlefs.c +++ b/esp32/littlefs/sflash_diskio_littlefs.c @@ -45,7 +45,7 @@ struct lfs_config lfscfg = .prog_size = SFLASH_BLOCK_SIZE, .block_size = SFLASH_BLOCK_SIZE, .block_count = 0, // To be initialized according to the flash size of the chip - .block_cycles = 0, // No block-level wear-leveling + .block_cycles = 100, // Enable block-level wear-leveling /* Current implementation of littlefs_read/prog/erase/sync() functions only operates with * full blocks, always the starting address of the block is passed to the driver. * This helps on the Power-loss resilient behavior of LittleFS, with this approach the File System will not be corrupted by corruption of a single file/block diff --git a/esp32/littlefs/vfs_littlefs_file.c b/esp32/littlefs/vfs_littlefs_file.c index aa7a90edb5..4003521325 100644 --- a/esp32/littlefs/vfs_littlefs_file.c +++ b/esp32/littlefs/vfs_littlefs_file.c @@ -20,6 +20,7 @@ typedef struct _pyb_file_obj_t { vfs_lfs_struct_t* littlefs; struct lfs_file_config cfg; // Attributes of the file, e.g.: timestamp bool timestamp_update; // For requesting timestamp update when closing the file + bool opened; // Indicate whether the file is opened } pyb_file_obj_t; STATIC void file_obj_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { @@ -31,6 +32,12 @@ STATIC mp_uint_t file_obj_read(mp_obj_t self_in, void *buf, mp_uint_t size, int pyb_file_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (self->opened == false) { + // Return EINVAL just as FatFS if the file is not opened + *errcode = MP_EINVAL; + return MP_STREAM_ERROR; + } + xSemaphoreTake(self->littlefs->mutex, portMAX_DELAY); lfs_ssize_t sz_out = lfs_file_read(&self->littlefs->lfs ,&self->fp, buf, size); xSemaphoreGive(self->littlefs->mutex); @@ -46,6 +53,12 @@ STATIC mp_uint_t file_obj_write(mp_obj_t self_in, const void *buf, mp_uint_t siz pyb_file_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (self->opened == false) { + // Return EINVAL just as FatFS if the file is not opened + *errcode = MP_EINVAL; + return MP_STREAM_ERROR; + } + xSemaphoreTake(self->littlefs->mutex, portMAX_DELAY); lfs_ssize_t sz_out = lfs_file_write(&self->littlefs->lfs, &self->fp, buf, size); // Request timestamp update if file has been written successfully @@ -89,6 +102,12 @@ STATIC mp_uint_t file_obj_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, } else if (request == MP_STREAM_FLUSH) { + if (self->opened == false) { + // Return EINVAL just as FatFS if the file is not opened + *errcode = MP_EINVAL; + return MP_STREAM_ERROR; + } + xSemaphoreTake(self->littlefs->mutex, portMAX_DELAY); int res = lfs_file_sync(&self->littlefs->lfs, &self->fp); xSemaphoreGive(self->littlefs->mutex); @@ -101,16 +120,21 @@ STATIC mp_uint_t file_obj_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, } else if (request == MP_STREAM_CLOSE) { + if (self->opened == false) { + // Return 0 just as FatFs if the file is not opened + return 0; + } + xSemaphoreTake(self->littlefs->mutex, portMAX_DELAY); int res = littlefs_close_common_helper(&self->littlefs->lfs, &self->fp, &self->cfg, &self->timestamp_update); xSemaphoreGive(self->littlefs->mutex); + if (res < 0) { *errcode = littleFsErrorToErrno(res); return MP_STREAM_ERROR; } - // Free up the object so GC does not need to do that - m_del_obj(pyb_file_obj_t, self); + self->opened = false; // indicate a closed file return 0; } else { *errcode = MP_EINVAL; @@ -165,6 +189,7 @@ STATIC mp_obj_t file_open(fs_user_mount_t *vfs, const mp_obj_type_t *type, mp_ar pyb_file_obj_t *o = m_new_obj_with_finaliser(pyb_file_obj_t); o->base.type = type; o->timestamp_update = false; + o->opened = false; xSemaphoreTake(vfs->fs.littlefs.mutex, portMAX_DELAY); const char *fname = concat_with_cwd(&vfs->fs.littlefs, mp_obj_str_get_str(args[0].u_obj)); @@ -178,6 +203,7 @@ STATIC mp_obj_t file_open(fs_user_mount_t *vfs, const mp_obj_type_t *type, mp_ar } o->littlefs = &vfs->fs.littlefs; + o->opened = true; // File is opened successfully return MP_OBJ_FROM_PTR(o); } diff --git a/esp32/main.c b/esp32/main.c index 5a6ab0f909..ae2c7c8b67 100644 --- a/esp32/main.c +++ b/esp32/main.c @@ -173,7 +173,7 @@ void app_main(void) { micropy_lpwan_dio_pin_num = 23; micropy_lpwan_dio_pin = &pin_GPIO23; - mpTaskStack = malloc(MICROPY_TASK_STACK_SIZE_PSRAM); + mpTaskStack = heap_caps_malloc(MICROPY_TASK_STACK_SIZE_PSRAM, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); // create the MicroPython task mpTaskHandle = @@ -196,7 +196,7 @@ void app_main(void) { micropy_lpwan_dio_pin_num = 23; micropy_lpwan_dio_pin = &pin_GPIO23; - mpTaskStack = malloc(MICROPY_TASK_STACK_SIZE); + mpTaskStack = heap_caps_malloc(MICROPY_TASK_STACK_SIZE, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); // create the MicroPython task mpTaskHandle = diff --git a/esp32/mods/machrtc.c b/esp32/mods/machrtc.c index d6bdf8a566..e4f8335867 100644 --- a/esp32/mods/machrtc.c +++ b/esp32/mods/machrtc.c @@ -48,7 +48,6 @@ typedef struct _mach_rtc_obj_t { bool synced; } mach_rtc_obj_t; -static RTC_DATA_ATTR uint64_t delta_from_epoch_til_boot; static RTC_DATA_ATTR uint32_t rtc_user_mem_len; static RTC_DATA_ATTR uint8_t rtc_user_mem_data[MEM_USER_MAXLEN]; @@ -61,10 +60,10 @@ void rtc_init0(void) { void mach_rtc_set_us_since_epoch(uint64_t nowus) { struct timeval tv; - // store the packet timestamp - gettimeofday(&tv, NULL); - delta_from_epoch_til_boot = nowus - (uint64_t)((tv.tv_sec * 1000000ull) + tv.tv_usec); + tv.tv_usec = nowus % 1000000ull; + tv.tv_sec = nowus / 1000000ull; + settimeofday(&tv, NULL); } void mach_rtc_synced (void) { @@ -78,8 +77,9 @@ bool mach_is_rtc_synced (void) { uint64_t mach_rtc_get_us_since_epoch(void) { struct timeval tv; gettimeofday(&tv, NULL); - return (uint64_t)((tv.tv_sec * 1000000ull) + tv.tv_usec) + delta_from_epoch_til_boot; -}; + return (uint64_t)(tv.tv_sec * 1000000ull ) + (tv.tv_usec); + +} STATIC uint64_t mach_rtc_datetime_us(const mp_obj_t datetime) { timeutils_struct_time_t tm; @@ -132,8 +132,6 @@ STATIC void mach_rtc_datetime(const mp_obj_t datetime) { if (datetime != mp_const_none) { useconds = mach_rtc_datetime_us(datetime); mach_rtc_set_us_since_epoch(useconds); - } else { - mach_rtc_set_us_since_epoch(0); } } @@ -197,14 +195,10 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mach_rtc_init_obj, 1, mach_rtc_init); STATIC mp_obj_t mach_rtc_now (mp_obj_t self_in) { timeutils_struct_time_t tm; uint64_t useconds; + + useconds = mach_rtc_get_us_since_epoch(); - struct timeval now; - gettimeofday(&now, NULL); - - // get the time from the RTC - useconds = (now.tv_sec * 1000000ull ) + (now.tv_usec); timeutils_seconds_since_epoch_to_struct_time((useconds) / 1000000ull, &tm); - mp_obj_t tuple[8] = { mp_obj_new_int(tm.tm_year), mp_obj_new_int(tm.tm_mon), diff --git a/esp32/mods/machuart.c b/esp32/mods/machuart.c index 7cded2a2ec..0c7a06dafe 100644 --- a/esp32/mods/machuart.c +++ b/esp32/mods/machuart.c @@ -456,7 +456,7 @@ STATIC mp_obj_t mach_uart_init_helper(mach_uart_obj_t *self, const mp_arg_val_t } STATIC const mp_arg_t mach_uart_init_args[] = { - { MP_QSTR_id, MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_id, MP_ARG_INT, {.u_int = MACH_UART_1} }, { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 9600} }, { MP_QSTR_bits, MP_ARG_INT, {.u_int = 8} }, { MP_QSTR_parity, MP_ARG_OBJ, {.u_obj = mp_const_none} }, @@ -473,7 +473,7 @@ STATIC mp_obj_t mach_uart_make_new(const mp_obj_type_t *type, mp_uint_t n_args, mp_arg_parse_all(n_args, all_args, &kw_args, MP_ARRAY_SIZE(args), mach_uart_init_args, args); // work out the uart id - uint uart_id = mp_obj_get_int(args[0].u_obj); + uint uart_id = args[0].u_int; #if defined(GPY) || defined(FIPY) if (uart_id > MACH_UART_1) { diff --git a/esp32/mods/modbt.c b/esp32/mods/modbt.c index 1816e2d530..f11c7d2622 100644 --- a/esp32/mods/modbt.c +++ b/esp32/mods/modbt.c @@ -1062,12 +1062,9 @@ static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_ /// \class Bluetooth static mp_obj_t bt_init_helper(bt_obj_t *self, const mp_arg_val_t *args) { if (!self->init) { - if (!self->controller_active) { - esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); - esp_bt_controller_init(&bt_cfg); - self->controller_active = true; - } + esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + esp_bt_controller_init(&bt_cfg); esp_bt_controller_enable(ESP_BT_MODE_BLE); if (ESP_OK != esp_bluedroid_init()) { diff --git a/esp32/mods/modcoap.c b/esp32/mods/modcoap.c index d806bdaefd..1157892f62 100644 --- a/esp32/mods/modcoap.c +++ b/esp32/mods/modcoap.c @@ -43,7 +43,6 @@ typedef struct mod_coap_resource_obj_s { coap_resource_t* coap_resource; struct mod_coap_resource_obj_s* next; uint8_t* value; - unsigned char* uri; uint32_t max_age; uint16_t etag_value; uint16_t value_len; @@ -208,9 +207,10 @@ STATIC mod_coap_resource_obj_t* add_resource(const char* uri, uint8_t mediatype, resource->next = NULL; // uri parameter pointer will be destroyed, pass a pointer to a permanent location - resource->uri = m_malloc(strlen(uri)); - memcpy(resource->uri, uri, strlen(uri)); - resource->coap_resource = coap_resource_init((const unsigned char* )resource->uri, strlen(uri), 0); + unsigned char* uri_ptr = (unsigned char*)malloc(strlen(uri)); + memcpy(uri_ptr, uri, strlen(uri)); + // Pass COAP_RESOURCE_FLAGS_RELEASE_URI so Coap Library will free up the memory allocated to store the URI when the Resource is deleted + resource->coap_resource = coap_resource_init(uri_ptr, strlen(uri), COAP_RESOURCE_FLAGS_RELEASE_URI); if(resource->coap_resource != NULL) { // Add the resource to the Coap context coap_add_resource(context->context, resource->coap_resource); @@ -238,7 +238,7 @@ STATIC mod_coap_resource_obj_t* add_resource(const char* uri, uint8_t mediatype, return resource; } else { - m_free(resource->uri); + free(uri_ptr); m_del_obj(mod_coap_resource_obj_t, resource); // Resource cannot be created return NULL; @@ -278,14 +278,12 @@ STATIC void remove_resource_by_key(coap_key_t key) { previous->next = current->next; } - // Free the URI - m_free(current->uri); // Free the resource in coap's scope coap_delete_resource(context->context, key); // Free the element in MP scope - m_free(current->value); + free(current->value); // Free the resource itself - m_free(current); + m_del_obj(mod_coap_resource_obj_t, current); return; } @@ -320,7 +318,7 @@ STATIC void resource_update_value(mod_coap_resource_obj_t* resource, mp_obj_t ne // Invalidate current data first resource->value_len = 0; - m_free(resource->value); + free(resource->value); if (mp_obj_is_integer(new_value)) { @@ -334,7 +332,7 @@ STATIC void resource_update_value(mod_coap_resource_obj_t* resource, mp_obj_t ne } // Allocate memory for the new data - resource->value = m_malloc(resource->value_len); + resource->value = malloc(resource->value_len); memcpy(resource->value, &value, sizeof(value)); } else { @@ -344,7 +342,7 @@ STATIC void resource_update_value(mod_coap_resource_obj_t* resource, mp_obj_t ne resource->value_len = value_bufinfo.len; // Allocate memory for the new data - resource->value = m_malloc(resource->value_len); + resource->value = malloc(resource->value_len); memcpy(resource->value, value_bufinfo.buf, resource->value_len); } } @@ -748,16 +746,13 @@ STATIC coap_pdu_t * modcoap_new_request // Helper function to create a new option for a request message STATIC coap_list_t * modcoap_new_option_node(unsigned short key, unsigned int length, unsigned char *data) { - coap_list_t *node = m_malloc(sizeof(coap_list_t) + sizeof(coap_option) + length); + coap_list_t *node = malloc(sizeof(coap_list_t) + sizeof(coap_option) + length); if (node) { coap_option *option; option = (coap_option *)(node->data); COAP_OPTION_KEY(*option) = key; COAP_OPTION_LENGTH(*option) = length; memcpy(COAP_OPTION_DATA(*option), data, length); - } else { - m_free(node); - node = NULL; } return node; @@ -821,7 +816,7 @@ STATIC mp_obj_t mod_coap_resource_callback_enable(mp_obj_t self_in, mp_obj_t req mod_coap_resource_obj_t* self = (mod_coap_resource_obj_t*)self_in; mp_int_t request_type = mp_obj_get_int(request_type_in); - bool enable = mp_obj_get_int(request_type_in) == 0 ? false : true; + bool enable = mp_obj_get_int(enable_in) == 0 ? false : true; if(request_type & MODCOAP_REQUEST_GET) { if(enable) coap_register_handler(self->coap_resource, COAP_REQUEST_GET, coap_resource_callback_get); @@ -937,7 +932,7 @@ STATIC mp_obj_t mod_coap_init(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map // Only 1 context is supported currently if(initialized == false) { - MP_STATE_PORT(coap_ptr) = m_malloc(sizeof(mod_coap_obj_t)); + MP_STATE_PORT(coap_ptr) = m_new_obj(mod_coap_obj_t); coap_obj_ptr = MP_STATE_PORT(coap_ptr); coap_obj_ptr->context = NULL; coap_obj_ptr->resources = NULL; @@ -1235,19 +1230,21 @@ STATIC mp_obj_t mod_coap_send_request(mp_uint_t n_args, const mp_obj_t *pos_args //TODO: allocate the proper length size_t length = 300; unsigned char* path = malloc(length); - int segments = coap_split_path(coap_uri.path.s, coap_uri.path.length, path, &length); + // Need to use a different pointer because when the segments are composed the pointer itself is moved + unsigned char* path_segment = path; + int segments = coap_split_path(coap_uri.path.s, coap_uri.path.length, path_segment, &length); // Insert the segments as separate URI-Path options while (segments--) { - node = modcoap_new_option_node(COAP_OPTION_URI_PATH, COAP_OPT_LENGTH(path), COAP_OPT_VALUE(path)); + node = modcoap_new_option_node(COAP_OPTION_URI_PATH, COAP_OPT_LENGTH(path_segment), COAP_OPT_VALUE(path_segment)); if(node != NULL) { LL_APPEND(coap_obj_ptr->optlist, node); } - - path += COAP_OPT_SIZE(path); + // Move the path_segment pointer to the next segment + path_segment += COAP_OPT_SIZE(path_segment); } - + // Free up the memory using the pointer pointing to the beginning of the memory area free(path); // Put Content Format option if given @@ -1271,7 +1268,7 @@ STATIC mp_obj_t mod_coap_send_request(mp_uint_t n_args, const mp_obj_t *pos_args while(coap_obj_ptr->optlist != NULL) { next = coap_obj_ptr->optlist->next; coap_obj_ptr->optlist->next = NULL; - m_free(coap_obj_ptr->optlist); + free(coap_obj_ptr->optlist); coap_obj_ptr->optlist = next; } diff --git a/esp32/mods/modlora.c b/esp32/mods/modlora.c index cf63333192..162fee8325 100644 --- a/esp32/mods/modlora.c +++ b/esp32/mods/modlora.c @@ -1103,7 +1103,7 @@ static void TASK_LoRa (void *pvParameters) { mibReq.Type = MIB_NETWORK_ACTIVATION; mibReq.Param.NetworkActivation = ACTIVATION_TYPE_OTAA; LoRaMacMibSetRequestConfirm( &mibReq ); - + TimerStart( &TxNextActReqTimer ); mlmeReq.Type = MLME_JOIN; mlmeReq.Req.Join.DevEui = (uint8_t *)lora_obj.u.otaa.DevEui; @@ -1692,7 +1692,9 @@ static bool lora_tx_space (void) { /// \class LoRa - Semtech SX1272 radio driver static mp_obj_t lora_init_helper(lora_obj_t *self, const mp_arg_val_t *args) { lora_cmd_data_t cmd_data; - +#if defined(FIPY) || defined(LOPY4) + xSemaphoreGive(xLoRaSigfoxSem); +#endif cmd_data.info.init.stack_mode = args[0].u_int; lora_validate_mode (cmd_data.info.init.stack_mode); @@ -1974,6 +1976,7 @@ STATIC mp_obj_t lora_join(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t * timeout -= LORA_JOIN_WAIT_MS; } if (timeout <= 0) { + TimerStop( &TxNextActReqTimer ); nlr_raise(mp_obj_new_exception_msg(&mp_type_TimeoutError, "timed out")); } } @@ -1990,26 +1993,28 @@ STATIC mp_obj_t lora_join_multicast_group (mp_uint_t n_args, const mp_obj_t *pos { MP_QSTR_mcNwkKey, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, { MP_QSTR_mcAppKey, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, }; - + // parse args mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(args), allowed_args, args); - + mp_buffer_info_t bufinfo_0, bufinfo_1; mp_get_buffer_raise(args[1].u_obj, &bufinfo_0, MP_BUFFER_READ); mp_get_buffer_raise(args[2].u_obj, &bufinfo_1, MP_BUFFER_READ); - - MulticastParams_t *channelParam = m_new_obj(MulticastParams_t); + + MulticastParams_t *channelParam = heap_caps_malloc(sizeof(MulticastParams_t), MALLOC_CAP_SPIRAM); channelParam->Next = NULL; channelParam->DownLinkCounter = 0; channelParam->Address = args[0].u_int; memcpy(channelParam->NwkSKey, bufinfo_0.buf, sizeof(channelParam->NwkSKey)); memcpy(channelParam->AppSKey, bufinfo_1.buf, sizeof(channelParam->AppSKey)); - + if (LoRaMacMulticastChannelLink(channelParam) == LORAMAC_STATUS_OK) { return mp_const_true; } - + + // adding to the list failed, so free the memory again + free(channelParam); return mp_const_false; } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(lora_join_multicast_group_obj, 0, lora_join_multicast_group); @@ -2018,7 +2023,7 @@ STATIC mp_obj_t lora_leave_multicast_group (mp_obj_t self_in, mp_obj_t multicast uint32_t mcAddr = mp_obj_get_int(multicast_addr_obj); MulticastParams_t *channelParam = LoRaMacMulticastGetChannel(mcAddr); if (LoRaMacMulticastChannelUnlink(channelParam) == LORAMAC_STATUS_OK) { - m_del_obj(MulticastParams_t, channelParam); + free(channelParam); return mp_const_true; } return mp_const_false; diff --git a/esp32/mods/modlte.c b/esp32/mods/modlte.c index 5afdc7e9c7..7711813c41 100644 --- a/esp32/mods/modlte.c +++ b/esp32/mods/modlte.c @@ -280,8 +280,7 @@ static bool lte_check_attached(bool legacy) { mp_hal_delay_ms(LTE_RX_TIMEOUT_MIN_MS); lte_push_at_command("AT+CEREG?", LTE_RX_TIMEOUT_MIN_MS); } - if (((pos = strstr(modlte_rsp.data, "+CEREG: 1,1")) || (pos = strstr(modlte_rsp.data, "+CEREG: 1,5"))) - && (strlen(pos) >= 31) && (pos[30] == '7' || pos[30] == '9')) { + if ((pos = strstr(modlte_rsp.data, "+CEREG: 1,1")) || (pos = strstr(modlte_rsp.data, "+CEREG: 1,5"))) { attached = true; } } else { diff --git a/esp32/mods/modpycom.c b/esp32/mods/modpycom.c index f2c0ba71b4..8ef265607d 100644 --- a/esp32/mods/modpycom.c +++ b/esp32/mods/modpycom.c @@ -1013,6 +1013,53 @@ STATIC mp_obj_t mod_pycom_sigfox_info (size_t n_args, const mp_obj_t *pos_args, } STATIC MP_DEFINE_CONST_FUN_OBJ_KW(mod_pycom_sigfox_info_obj, 0, mod_pycom_sigfox_info); +// This function creates a 128 bit long UUID stored in a byte array in Little Endian order from an input String +STATIC mp_obj_t create_128bit_le_uuid_from_string(mp_obj_t uuid_in) { + + size_t length; + uint8_t new_uuid[16]; + uint8_t i, j; + + const char* uuid_char_in = mp_obj_str_get_data(uuid_in, &length); + // 1 character is stored on 1 byte because we received a String + // For 128 bit UUID maximum 32 characters long String can be accepted + if (length > 32) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "Input string must not be longer than 32 characters!")); + } + + // Pre-fill the whole array with 0 because the remaining/not given digits will be 0 + char uuid_char[32] = {0}; + memcpy(uuid_char, uuid_char_in, length); + + for(i = 0, j = 0; i < 32; i = i+2) { + + uint8_t lower_nibble = 0; + uint8_t upper_nibble = 0; + + if(uuid_char[i] > 0) { + upper_nibble = hex_from_char(uuid_char[i]); + } + + if(uuid_char[i+1] > 0) { + lower_nibble = hex_from_char(uuid_char[i+1]); + } + + if(lower_nibble == 16 || upper_nibble == 16) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "UUID must only contain hexadecimal digits!")); + } + + // Pack together the 4 bits digits into 1 byte + // Convert to Little Endian order because we expect that the digits of the input String follows the Natural Byte (Big Endian) order + new_uuid[15-j] = lower_nibble | (upper_nibble << 4); + j++; + } + + mp_obj_t new_uuid_mp = mp_obj_new_bytearray(16, new_uuid); + return new_uuid_mp; + +} +STATIC MP_DEFINE_CONST_FUN_OBJ_1(create_128bit_le_uuid_from_string_obj, create_128bit_le_uuid_from_string); + STATIC const mp_map_elem_t pycom_module_globals_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_pycom) }, { MP_OBJ_NEW_QSTR(MP_QSTR_heartbeat), (mp_obj_t)&mod_pycom_heartbeat_obj }, @@ -1039,6 +1086,8 @@ STATIC const mp_map_elem_t pycom_module_globals_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_wifi_pwd_sta), (mp_obj_t)&mod_pycom_wifi_pwd_sta_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_wifi_pwd_ap), (mp_obj_t)&mod_pycom_wifi_pwd_ap_obj }, { MP_OBJ_NEW_QSTR(MP_QSTR_wifi_mode_on_boot), (mp_obj_t)&mod_pycom_wifi_mode_obj }, + { MP_OBJ_NEW_QSTR(MP_QSTR_create_128bit_le_uuid_from_string), (mp_obj_t)&create_128bit_le_uuid_from_string_obj }, + #if defined(FIPY) || defined(LOPY4) || defined(SIPY) { MP_OBJ_NEW_QSTR(MP_QSTR_sigfox_info), (mp_obj_t)&mod_pycom_sigfox_info_obj }, diff --git a/esp32/mods/modwlan.c b/esp32/mods/modwlan.c index 4f64efa6fe..56f6f8b0ab 100644 --- a/esp32/mods/modwlan.c +++ b/esp32/mods/modwlan.c @@ -760,6 +760,28 @@ STATIC void wlan_do_connect (const char* ssid, const char* bssid, const wifi_aut goto os_error; } + /* Read certificates content (if they are available) for the WPA2 Enterprise authentication */ + if (auth == WIFI_AUTH_WPA2_ENTERPRISE) { + const char *wpa_cert = NULL; + if (wlan_wpa2_ent.ca_certs_path != NULL) { + wpa_cert = pycom_util_read_file(wlan_wpa2_ent.ca_certs_path, &wlan_obj.vstr_ca); + if(wpa_cert == NULL) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "certificate file not found")); + } + } + + if (wlan_wpa2_ent.client_key_path != NULL && wlan_wpa2_ent.client_cert_path != NULL) { + wpa_cert = pycom_util_read_file(wlan_wpa2_ent.client_key_path, &wlan_obj.vstr_key); + if(wpa_cert == NULL) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "device key certificate file not found")); + } + wpa_cert = pycom_util_read_file(wlan_wpa2_ent.client_cert_path, &wlan_obj.vstr_cert); + if(wpa_cert == NULL) { + nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "device client certificate file not found")); + } + } + } + // The certificate files are already read at this point because this function runs outside of GIL, and the file_read functions uses MicroPython APIs if (auth == WIFI_AUTH_WPA2_ENTERPRISE) { // CA Certificate is not mandatory diff --git a/esp32/mpthreadport.c b/esp32/mpthreadport.c index 4a5d3f4db1..798a340392 100644 --- a/esp32/mpthreadport.c +++ b/esp32/mpthreadport.c @@ -171,15 +171,15 @@ void mp_thread_create_ex(void *(*entry)(void*), void *arg, size_t *stack_size, i // allocate TCB, stack and linked-list node (must be outside thread_mutex lock) if (mp_chip_revision > 0) { // for revision 1 devices we allocate from the internal memory of the malloc heap - tcb = malloc(sizeof(StaticTask_t)); + tcb = heap_caps_malloc(sizeof(StaticTask_t), MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); if (!tcb) { goto memory_error; } - stack = malloc(*stack_size); + stack = heap_caps_malloc(*stack_size, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); if (!stack) { goto memory_error; } - th = malloc(sizeof(thread_t)); + th = heap_caps_malloc(sizeof(thread_t), MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); if (!th) { goto memory_error; } diff --git a/esp32/pycom_version.h b/esp32/pycom_version.h index 7f71743e51..3b1c57995d 100644 --- a/esp32/pycom_version.h +++ b/esp32/pycom_version.h @@ -10,14 +10,14 @@ #ifndef VERSION_H_ #define VERSION_H_ -#define SW_VERSION_NUMBER "1.20.2.r3" +#define SW_VERSION_NUMBER "1.20.2.r5" #define LORAWAN_VERSION_NUMBER "1.0.2" #define SIGFOX_VERSION_NUMBER "1.0.1" #if (VARIANT == PYBYTES) -#define PYBYTES_VERSION_NUMBER "1.6.1" +#define PYBYTES_VERSION_NUMBER "1.7.0" #endif #ifdef PYGATE_ENABLED diff --git a/esp32/util/mperror.c b/esp32/util/mperror.c index 3a2dfd214c..d36cce2647 100644 --- a/esp32/util/mperror.c +++ b/esp32/util/mperror.c @@ -131,16 +131,18 @@ bool mperror_heartbeat_signal (void) { mperror_heart_beat.do_disable = false; } else if (mperror_heart_beat.enabled) { if (!mperror_heart_beat.beating) { - if ((mperror_heart_beat.on_time = mp_hal_ticks_ms_non_blocking()) - mperror_heart_beat.off_time > MPERROR_HEARTBEAT_OFF_MS) { + if (mp_hal_ticks_ms_non_blocking() > mperror_heart_beat.off_time) { led_info.color.value = MPERROR_HEARTBEAT_COLOR; - led_set_color(&led_info, false, false); + led_set_color(&led_info, true, false); mperror_heart_beat.beating = true; + mperror_heart_beat.on_time = mp_hal_ticks_ms_non_blocking() + MPERROR_HEARTBEAT_ON_MS; } } else { - if ((mperror_heart_beat.off_time = mp_hal_ticks_ms_non_blocking()) - mperror_heart_beat.on_time > MPERROR_HEARTBEAT_ON_MS) { + if (mp_hal_ticks_ms_non_blocking() > mperror_heart_beat.on_time) { led_info.color.value = 0; - led_set_color(&led_info, false, false); + led_set_color(&led_info, true, false); mperror_heart_beat.beating = false; + mperror_heart_beat.off_time = mp_hal_ticks_ms_non_blocking() + MPERROR_HEARTBEAT_OFF_MS; } } } diff --git a/py/makeversionhdr.py b/py/makeversionhdr.py index 2ab99d89b4..0af0049870 100644 --- a/py/makeversionhdr.py +++ b/py/makeversionhdr.py @@ -57,11 +57,20 @@ def get_version_info_from_docs_conf(): return git_tag, "" return None +def get_version_info_from_pycom_version(): + with open(os.path.join(os.path.dirname(sys.argv[0]), "..", "esp32", "pycom_version.h")) as f: + for line in f: + if line.startswith("#define SW_VERSION_NUMBER"): + ver = line.strip().split('"')[1] + git_tag = "v" + ver + return git_tag, "" + return None + def make_version_header(filename): # Get version info using git, with fallback to docs/conf.py info = get_version_info_from_git() if info is None: - info = get_version_info_from_docs_conf() + info = get_version_info_from_pycom_version() git_tag, git_hash = info diff --git a/tests/esp32/fs_file.py b/tests/esp32/fs_file.py index ab647a0079..3924887bb2 100644 --- a/tests/esp32/fs_file.py +++ b/tests/esp32/fs_file.py @@ -74,6 +74,32 @@ def file_seek_test(path): print("{0}, f.tell(): {1}".format(path, f.tell())) f.close() +def file_double_close_test(path): + f = open(path) + # Nothing should be printed out + f.close() + f.close() + f.close() + +def file_operations_after_close_test(path): + f = open(path, "rw") + f.close() + try: + f.read() + except Exception as e: + print("{0}, Exception after f.read: {1}".format(path, e)) + try: + f.write("aaa") + except Exception as e: + print("{0}, Exception after f.write: {1}".format(path, e)) + try: + f.flush() + except Exception as e: + print("{0}, Exception after f.flush: {1}".format(path, e)) + # No exception should be dropped + f.seek(12) + f.tell() + sd = SD() sd_fat_fs = os.mkfat(sd) @@ -157,6 +183,14 @@ def file_seek_test(path): file_readline_test(f_path) file_readline_test(sd_path) +#Test multiple file.close +file_double_close_test(f_path) +file_double_close_test(sd_path) + +#Test other operations after file.close() +file_operations_after_close_test(f_path) +file_operations_after_close_test(sd_path) + os.remove(f_path) os.remove(sd_path) os.umount("/sd") diff --git a/tests/esp32/fs_file.py.exp b/tests/esp32/fs_file.py.exp index 42d8f43e3b..d671c7b9ec 100644 --- a/tests/esp32/fs_file.py.exp +++ b/tests/esp32/fs_file.py.exp @@ -66,3 +66,9 @@ bytearray(b'0123456789') /sd/t.txt, f.readline(10): 01234 /sd/t.txt, f.readlines(): ['01234\n', '56789'] +/flash/t.txt, Exception after f.read: [Errno 22] EINVAL +/flash/t.txt, Exception after f.write: [Errno 22] EINVAL +/flash/t.txt, Exception after f.flush: [Errno 22] EINVAL +/sd/t.txt, Exception after f.read: [Errno 22] EINVAL +/sd/t.txt, Exception after f.write: [Errno 22] EINVAL +/sd/t.txt, Exception after f.flush: [Errno 22] EINVAL