diff --git a/src/Makefile b/src/Makefile index 57330e5a9..d05ea03bc 100755 --- a/src/Makefile +++ b/src/Makefile @@ -127,6 +127,7 @@ FIRM_OBJ += $(addprefix $(BUILD)/$(OMV_DIR)/, \ fb_alloc.o \ umm_malloc.o \ ff_wrapper.o \ + ini.o \ framebuffer.o \ array.o \ usbdbg.o \ diff --git a/src/omv/Makefile b/src/omv/Makefile index 11aaeb0c2..63584ccfb 100644 --- a/src/omv/Makefile +++ b/src/omv/Makefile @@ -5,6 +5,7 @@ SRCS += $(addprefix , \ fb_alloc.c \ umm_malloc.c \ ff_wrapper.c \ + ini.c \ framebuffer.c \ array.c \ usbdbg.c \ diff --git a/src/omv/ini.c b/src/omv/ini.c new file mode 100644 index 000000000..46c105fd3 --- /dev/null +++ b/src/omv/ini.c @@ -0,0 +1,549 @@ +/* This file is part of the OpenMV project. + * Copyright (c) 2013-2018 Ibrahim Abdelkader & Kwabena W. Agyeman + * This work is licensed under the MIT license, see the file LICENSE for details. + */ + +#include +#include "ini.h" + +/*------------------------------------------------------------------------- + _isppace.c - part of ctype.h + + Written By - Sandeep Dutta . sandeep.dutta@usa.net (1999) + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +char ini_isspace (unsigned char c) +{ + if ( c == ' ' + || c == '\f' + || c == '\n' + || c == '\r' + || c == '\t' + || c == '\v' ) + return 1; + + return 0; +} + +/* + * atoi.c -- + * + * Source code for the "atoi" library procedure. + * + * Copyright 1988 Regents of the University of California + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies. The University of California + * makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without + * express or implied warranty. + */ + +/* + *---------------------------------------------------------------------- + * + * atoi -- + * + * Convert an ASCII string into an integer. + * + * Results: + * The return value is the integer equivalent of string. If there + * are no decimal digits in string, then 0 is returned. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +ini_atoi(string) + const char *string; /* String of ASCII digits, possibly + * preceded by white space. For bases + * greater than 10, either lower- or + * upper-case digits may be used. + */ +{ + register int result = 0; + register unsigned int digit; + int sign; + + /* + * Skip any leading blanks. + */ + + while (ini_isspace(*string)) { + string += 1; + } + + /* + * Check for a sign. + */ + + if (*string == '-') { + sign = 1; + string += 1; + } else { + sign = 0; + if (*string == '+') { + string += 1; + } + } + + for ( ; ; string += 1) { + digit = *string - '0'; + if ((digit < 0) || (digit > 9)) { + if (!ini_isspace(*string)) { + result = 0; + } + break; + } + result = (10*result) + digit; + } + + if (sign) { + return -result; + } + return result; +} + +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copy src to dst, truncating or null-padding to always copy n bytes. + * Return dst. + */ +char * +strncpy(char *dst, const char *src, size_t n) +{ + if (n != 0) { + char *d = dst; + const char *s = src; + do { + if ((*d++ = *s++) == 0) { + /* NUL pad the remaining n-1 bytes */ + while (--n != 0) + *d++ = 0; + break; + } + } while (--n != 0); + } + return (dst); +} + +/* + * Copyright (c) 2000-2002 Opsycon AB (www.opsycon.se) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Opsycon AB. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/** ini_fgetc(fp) -- get char from stream */ + +int +ini_fgetc(FIL *fp) +{ + char c; + UINT b; + + if (f_read (fp, &c, 1, &b) != FR_OK || b != 1) + return (EOF); + return (c); +} + +/* + * Copyright (c) 2000-2002 Opsycon AB (www.opsycon.se) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Opsycon AB. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/** char *ini_fgets(dst,max,fp) -- get string from stream */ + +char * +ini_fgets(char *dst, int max, FIL *fp) +{ + int c; + char *p; + + /* get max bytes or upto a newline */ + + for (p = dst, max--; max > 0; max--) { + if ((c = ini_fgetc (fp)) == EOF) + break; + *p++ = c; + if (c == '\n') + break; + } + *p = 0; + if (p == dst || c == EOF) + return NULL; + return (p); +} + +/* inih -- simple .INI file parser + +inih is released under the New BSD license (see LICENSE.txt). Go to the project +home page for more info: + +https://github.com/benhoyt/inih + +*/ + +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include + +#if !INI_USE_STACK +#include +#endif + +#define MAX_SECTION 50 +#define MAX_NAME 50 + +/* Used by ini_parse_string() to keep track of string parsing state. */ +typedef struct { + const char* ptr; + size_t num_left; +} ini_parse_string_ctx; + +/* Strip whitespace chars off end of given string, in place. Return s. */ +static char* rstrip(char* s) +{ + char* p = s + strlen(s); + while (p > s && ini_isspace((unsigned char)(*--p))) + *p = '\0'; + return s; +} + +/* Return pointer to first non-whitespace char in given string. */ +static char* lskip(const char* s) +{ + while (*s && ini_isspace((unsigned char)(*s))) + s++; + return (char*)s; +} + +/* Return pointer to first char (of chars) or inline comment in given string, + or pointer to null at end of string if neither found. Inline comment must + be prefixed by a whitespace character to register as a comment. */ +static char* find_chars_or_comment(const char* s, const char* chars) +{ +#if INI_ALLOW_INLINE_COMMENTS + int was_space = 0; + while (*s && (!chars || !strchr(chars, *s)) && + !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) { + was_space = ini_isspace((unsigned char)(*s)); + s++; + } +#else + while (*s && (!chars || !strchr(chars, *s))) { + s++; + } +#endif + return (char*)s; +} + +/* Version of strncpy that ensures dest (size bytes) is null-terminated. */ +static char* strncpy0(char* dest, const char* src, size_t size) +{ + strncpy(dest, src, size); + dest[size - 1] = '\0'; + return dest; +} + +/* See documentation in header file. */ +int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, + void* user) +{ + /* Uses a fair bit of stack (use heap instead if you need to) */ +#if INI_USE_STACK + char line[INI_MAX_LINE]; + int max_line = INI_MAX_LINE; +#else + char* line; + int max_line = INI_INITIAL_ALLOC; +#endif +#if INI_ALLOW_REALLOC + char* new_line; + int offset; +#endif + char section[MAX_SECTION] = ""; + char prev_name[MAX_NAME] = ""; + + char* start; + char* end; + char* name; + char* value; + int lineno = 0; + int error = 0; + +#if !INI_USE_STACK + line = (char*)malloc(INI_INITIAL_ALLOC); + if (!line) { + return -2; + } +#endif + +#if INI_HANDLER_LINENO +#define HANDLER(u, s, n, v) handler(u, s, n, v, lineno) +#else +#define HANDLER(u, s, n, v) handler(u, s, n, v) +#endif + + /* Scan through stream line by line */ + while (reader(line, max_line, stream) != NULL) { +#if INI_ALLOW_REALLOC + offset = strlen(line); + while (offset == max_line - 1 && line[offset - 1] != '\n') { + max_line *= 2; + if (max_line > INI_MAX_LINE) + max_line = INI_MAX_LINE; + new_line = realloc(line, max_line); + if (!new_line) { + free(line); + return -2; + } + line = new_line; + if (reader(line + offset, max_line - offset, stream) == NULL) + break; + if (max_line >= INI_MAX_LINE) + break; + offset += strlen(line + offset); + } +#endif + + lineno++; + + start = line; +#if INI_ALLOW_BOM + if (lineno == 1 && (unsigned char)start[0] == 0xEF && + (unsigned char)start[1] == 0xBB && + (unsigned char)start[2] == 0xBF) { + start += 3; + } +#endif + start = lskip(rstrip(start)); + + if (*start == ';' || *start == '#') { + /* Per Python configparser, allow both ; and # comments at the + start of a line */ + } +#if INI_ALLOW_MULTILINE + else if (*prev_name && *start && start > line) { + /* Non-blank line with leading whitespace, treat as continuation + of previous name's value (as per Python configparser). */ + if (!HANDLER(user, section, prev_name, start) && !error) + error = lineno; + } +#endif + else if (*start == '[') { + /* A "[section]" line */ + end = find_chars_or_comment(start + 1, "]"); + if (*end == ']') { + *end = '\0'; + strncpy0(section, start + 1, sizeof(section)); + *prev_name = '\0'; + } + else if (!error) { + /* No ']' found on section line */ + error = lineno; + } + } + else if (*start) { + /* Not a comment, must be a name[=:]value pair */ + end = find_chars_or_comment(start, "=:"); + if (*end == '=' || *end == ':') { + *end = '\0'; + name = rstrip(start); + value = end + 1; +#if INI_ALLOW_INLINE_COMMENTS + end = find_chars_or_comment(value, NULL); + if (*end) + *end = '\0'; +#endif + value = lskip(value); + rstrip(value); + + /* Valid name[=:]value pair found, call handler */ + strncpy0(prev_name, name, sizeof(prev_name)); + if (!HANDLER(user, section, name, value) && !error) + error = lineno; + } + else if (!error) { + /* No '=' or ':' found on name[=:]value line */ + error = lineno; + } + } + +#if INI_STOP_ON_FIRST_ERROR + if (error) + break; +#endif + } + +#if !INI_USE_STACK + free(line); +#endif + + return error; +} + +/* See documentation in header file. */ +int ini_parse_file(FIL* file, ini_handler handler, void* user) +{ + return ini_parse_stream((ini_reader)ini_fgets, file, handler, user); +} + +/* See documentation in header file. */ +int ini_parse(FATFS *fs, const char* filename, ini_handler handler, void* user) +{ + FIL file; + int error; + + FRESULT res = f_open(fs, &file, filename, FA_READ | FA_OPEN_EXISTING); + if (res != FR_OK) + return -1; + error = ini_parse_file(&file, handler, user); + res = f_close(&file); + if (res != FR_OK) + return -1; + return error; +} + +/* An ini_reader function to read the next line from a string buffer. This + is the ini_fgets() equivalent used by ini_parse_string(). */ +static char* ini_reader_string(char* str, int num, void* stream) { + ini_parse_string_ctx* ctx = (ini_parse_string_ctx*)stream; + const char* ctx_ptr = ctx->ptr; + size_t ctx_num_left = ctx->num_left; + char* strp = str; + char c; + + if (ctx_num_left == 0 || num < 2) + return NULL; + + while (num > 1 && ctx_num_left != 0) { + c = *ctx_ptr++; + ctx_num_left--; + *strp++ = c; + if (c == '\n') + break; + num--; + } + + *strp = '\0'; + ctx->ptr = ctx_ptr; + ctx->num_left = ctx_num_left; + return str; +} + +/* See documentation in header file. */ +int ini_parse_string(const char* string, ini_handler handler, void* user) { + ini_parse_string_ctx ctx; + + ctx.ptr = string; + ctx.num_left = strlen(string); + return ini_parse_stream((ini_reader)ini_reader_string, &ctx, handler, + user); +} diff --git a/src/omv/ini.h b/src/omv/ini.h new file mode 100644 index 000000000..c83411735 --- /dev/null +++ b/src/omv/ini.h @@ -0,0 +1,131 @@ +/* This file is part of the OpenMV project. + * Copyright (c) 2013-2018 Ibrahim Abdelkader & Kwabena W. Agyeman + * This work is licensed under the MIT license, see the file LICENSE for details. + */ + +/* inih -- simple .INI file parser + +inih is released under the New BSD license (see LICENSE.txt). Go to the project +home page for more info: + +https://github.com/benhoyt/inih + +*/ + +#ifndef __INI_H__ +#define __INI_H__ + +int ini_atoi(const char *string); + +/* Make this header file easier to include in C++ code */ +#ifdef __cplusplus +extern "C" { +#endif + +#include "ff_wrapper.h" + +/* Nonzero if ini_handler callback should accept lineno parameter. */ +#ifndef INI_HANDLER_LINENO +#define INI_HANDLER_LINENO 0 +#endif + +/* Typedef for prototype of handler function. */ +#if INI_HANDLER_LINENO +typedef int (*ini_handler)(void* user, const char* section, + const char* name, const char* value, + int lineno); +#else +typedef int (*ini_handler)(void* user, const char* section, + const char* name, const char* value); +#endif + +/* Typedef for prototype of fgets-style reader function. */ +typedef char* (*ini_reader)(char* str, int num, void* stream); + +/* Parse given INI-style file. May have [section]s, name=value pairs + (whitespace stripped), and comments starting with ';' (semicolon). Section + is "" if name=value pair parsed before any section heading. name:value + pairs are also supported as a concession to Python's configparser. + + For each name=value pair parsed, call handler function with given user + pointer as well as section, name, and value (data only valid for duration + of handler call). Handler should return nonzero on success, zero on error. + + Returns 0 on success, line number of first error on parse error (doesn't + stop on first error), -1 on file open error, or -2 on memory allocation + error (only when INI_USE_STACK is zero). +*/ +int ini_parse(FATFS *fs, const char* filename, ini_handler handler, void* user); + +/* Same as ini_parse(), but takes a FIL* instead of filename. This doesn't + close the file when it's finished -- the caller must do that. */ +int ini_parse_file(FIL* file, ini_handler handler, void* user); + +/* Same as ini_parse(), but takes an ini_reader function pointer instead of + filename. Used for implementing custom or string-based I/O (see also + ini_parse_string). */ +int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, + void* user); + +/* Same as ini_parse(), but takes a zero-terminated string with the INI data +instead of a file. Useful for parsing INI data from a network socket or +already in memory. */ +int ini_parse_string(const char* string, ini_handler handler, void* user); + +/* Nonzero to allow multi-line value parsing, in the style of Python's + configparser. If allowed, ini_parse() will call the handler with the same + name for each subsequent line parsed. */ +#ifndef INI_ALLOW_MULTILINE +#define INI_ALLOW_MULTILINE 1 +#endif + +/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of + the file. See http://code.google.com/p/inih/issues/detail?id=21 */ +#ifndef INI_ALLOW_BOM +#define INI_ALLOW_BOM 1 +#endif + +/* Nonzero to allow inline comments (with valid inline comment characters + specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match + Python 3.2+ configparser behaviour. */ +#ifndef INI_ALLOW_INLINE_COMMENTS +#define INI_ALLOW_INLINE_COMMENTS 1 +#endif +#ifndef INI_INLINE_COMMENT_PREFIXES +#define INI_INLINE_COMMENT_PREFIXES ";" +#endif + +/* Nonzero to use stack for line buffer, zero to use heap (malloc/free). */ +#ifndef INI_USE_STACK +#define INI_USE_STACK 1 +#endif + +/* Maximum line length for any line in INI file (stack or heap). Note that + this must be 3 more than the longest line (due to '\r', '\n', and '\0'). */ +#ifndef INI_MAX_LINE +#define INI_MAX_LINE 200 +#endif + +/* Nonzero to allow heap line buffer to grow via realloc(), zero for a + fixed-size buffer of INI_MAX_LINE bytes. Only applies if INI_USE_STACK is + zero. */ +#ifndef INI_ALLOW_REALLOC +#define INI_ALLOW_REALLOC 0 +#endif + +/* Initial size in bytes for heap line buffer. Only applies if INI_USE_STACK + is zero. */ +#ifndef INI_INITIAL_ALLOC +#define INI_INITIAL_ALLOC 200 +#endif + +/* Stop parsing on first error (default is to keep parsing). */ +#ifndef INI_STOP_ON_FIRST_ERROR +#define INI_STOP_ON_FIRST_ERROR 0 +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __INI_H__ */ diff --git a/src/omv/main.c b/src/omv/main.c index 946bf9dd1..4c7f6df3e 100644 --- a/src/omv/main.c +++ b/src/omv/main.c @@ -70,6 +70,8 @@ #include "framebuffer.h" +#include "ini.h" + int errno; extern char _vfs_buf; static fs_user_mount_t *vfs_fat = (fs_user_mount_t *) &_vfs_buf; @@ -269,6 +271,57 @@ void NORETURN __stack_chk_fail(void) } #endif +static bool ini_handler_callback_is_true(const char *value) +{ + int i = ini_atoi(value); + if (i) return true; + if (strlen(value) != 4) return false; + if ((value[0] != 'T') && (value[0] != 't')) return false; + if ((value[1] != 'R') && (value[1] != 'r')) return false; + if ((value[2] != 'U') && (value[2] != 'u')) return false; + if ((value[3] != 'E') && (value[3] != 'e')) return false; + return true; +} + +int ini_handler_callback(void *user, const char *section, const char *name, const char *value) +{ + user = user; + + #define MATCH(s, n) ((strcmp(section, (s)) == 0) && (strcmp(name, (n)) == 0)) + + if (MATCH("BootSettings", "REPLUart")) { + if (ini_handler_callback_is_true(value)) { + mp_obj_t args[2] = { + MP_OBJ_NEW_SMALL_INT(3), // UART Port + MP_OBJ_NEW_SMALL_INT(115200) // Baud Rate + }; + + MP_STATE_PORT(pyb_stdio_uart) = pyb_uart_type.make_new((mp_obj_t) &pyb_uart_type, MP_ARRAY_SIZE(args), 0, args); + } + } else { + return 0; + } + + return 1; + + #undef MATCH +} + +FRESULT apply_settings(const char *path) +{ + nlr_buf_t nlr; + FRESULT f_res = f_stat(&vfs_fat->fatfs, path, NULL); + + if (f_res == FR_OK) { + if (nlr_push(&nlr) == 0) { + ini_parse(&vfs_fat->fatfs, path, ini_handler_callback, NULL); + nlr_pop(); + } + } + + return f_res; +} + FRESULT exec_boot_script(const char *path, bool selftest, bool interruptible) { nlr_buf_t nlr; @@ -378,6 +431,7 @@ soft_reset: i2c_init0(); spi_init0(); uart_init0(); + MP_STATE_PORT(pyb_stdio_uart) = NULL; // need to zero dac_init(); pyb_usb_init0(); sensor_init0(); @@ -474,6 +528,7 @@ soft_reset: // Run boot script(s) if (first_soft_reset) { exec_boot_script("/selftest.py", true, false); + apply_settings("/openmv.config"); exec_boot_script("/main.py", false, true); }