diff --git a/platformio/builder/tools/pioupload.py b/platformio/builder/tools/pioupload.py index 224d0b90f8..b871cce212 100644 --- a/platformio/builder/tools/pioupload.py +++ b/platformio/builder/tools/pioupload.py @@ -218,16 +218,27 @@ def _format_availale_bytes(value, total): if int(ARGUMENTS.get("PIOVERBOSE", 0)): print(output) + memory_exceeded = False + ram_free = ARGUMENTS.get("RAMFREE", 0) if data_max_size and data_size > data_max_size: + memory_exceeded = True sys.stderr.write( - "Warning! The data size (%d bytes) is greater " - "than maximum allowed (%s bytes)\n" % (data_size, data_max_size) + "Error: Pre-allocated RAM usage (%d bytes) is greater " + "than RAM available (%d bytes)\n" % (data_size, data_max_size) + ) + elif ramfree and data_max_size and data_size + ram_free > data_max_size: + memory_exceeded = True + sys.stderr.write( + "Error: Pre-allocated RAM usage (%d bytes of %d total bytes) results in " + "less RAM remaining than minimum required (%d bytes)\n" % (data_size, data_max_size, ram_free) ) if program_size > program_max_size: + memory_exceeded = True sys.stderr.write( - "Error: The program size (%d bytes) is greater " - "than maximum allowed (%s bytes)\n" % (program_size, program_max_size) + "Error: Flash usage (%d bytes) is greater " + "than Flash available (%d bytes)\n" % (program_size, program_max_size) ) + if memory_exceeded: env.Exit(1) diff --git a/platformio/platform/_run.py b/platformio/platform/_run.py index 2912371dc9..f4f5a6d92a 100644 --- a/platformio/platform/_run.py +++ b/platformio/platform/_run.py @@ -80,6 +80,7 @@ def _run_scons(self, variables, targets, jobs): os.path.join(fs.get_source_dir(), "builder", "main.py"), ] args.append("PIOVERBOSE=%d" % int(self.verbose)) + args.append("RAMFREE=%d" % int(self.ramfree)) # pylint: disable=protected-access args.append("ISATTY=%d" % int(click._compat.isatty(sys.stdout))) # encode and append variables diff --git a/platformio/run/cli.py b/platformio/run/cli.py index 1ed84a48b0..457ab68992 100644 --- a/platformio/run/cli.py +++ b/platformio/run/cli.py @@ -75,6 +75,17 @@ @click.option("--list-targets", is_flag=True) @click.option("-s", "--silent", is_flag=True) @click.option("-v", "--verbose", is_flag=True) +@click.option( + "-r", + "--ramfree", + type=int, + default=0, + help=( + "Specify the minimum number of bytes of RAM that must remain after pre-allocation. " + "Insufficient free ram can lead to crashes, reboots etc. when memory " + "becomes exhausted during dynamic allocation or stack usage." + ), +) @click.pass_context def cli( ctx, @@ -90,6 +101,7 @@ def cli( list_targets, silent, verbose, + ramfree, ): app.set_session_var("custom_project_conf", project_conf) @@ -154,6 +166,7 @@ def cli( is_test_running, silent, verbose, + ramfree, ) ) command_failed = any(r.get("succeeded") is False for r in results) @@ -186,6 +199,7 @@ def process_env( is_test_running, silent, verbose, + ramfree, ): if not is_test_running and not silent: print_processing_header(name, config, verbose) @@ -205,6 +219,7 @@ def process_env( program_args, silent, verbose, + ramfree, ).process() if result["succeeded"] and "monitor" in targets and "nobuild" not in targets: