2017-06-05 16:02:39 +03:00
|
|
|
# Copyright (c) 2014-present PlatformIO <contact@platformio.org>
|
2015-11-18 17:16:17 +02:00
|
|
|
#
|
|
|
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
# you may not use this file except in compliance with the License.
|
|
|
|
|
# You may obtain a copy of the License at
|
|
|
|
|
#
|
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
#
|
|
|
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
# See the License for the specific language governing permissions and
|
|
|
|
|
# limitations under the License.
|
2015-03-04 21:21:10 +02:00
|
|
|
|
2015-11-30 01:11:57 +02:00
|
|
|
from __future__ import absolute_import
|
|
|
|
|
|
2018-06-04 14:09:48 +03:00
|
|
|
import re
|
2016-08-26 14:39:23 +03:00
|
|
|
import sys
|
2016-12-03 17:59:15 +02:00
|
|
|
from fnmatch import fnmatch
|
2016-06-11 00:02:36 +03:00
|
|
|
from os import environ
|
2015-11-30 01:11:57 +02:00
|
|
|
from os.path import isfile, join
|
2015-03-13 00:02:31 +02:00
|
|
|
from shutil import copyfile
|
2015-03-04 21:21:10 +02:00
|
|
|
from time import sleep
|
|
|
|
|
|
2019-05-07 21:16:42 +03:00
|
|
|
from SCons.Script import ARGUMENTS # pylint: disable=import-error
|
2017-05-30 14:06:25 +03:00
|
|
|
from serial import Serial, SerialException
|
2015-03-04 21:21:10 +02:00
|
|
|
|
2018-10-12 19:35:58 +03:00
|
|
|
from platformio import exception, util
|
2019-05-10 17:26:10 +03:00
|
|
|
from platformio.compat import WINDOWS
|
2015-12-28 20:35:56 +02:00
|
|
|
|
2018-06-04 14:09:48 +03:00
|
|
|
# pylint: disable=unused-argument
|
|
|
|
|
|
2015-03-04 21:21:10 +02:00
|
|
|
|
|
|
|
|
def FlushSerialBuffer(env, port):
|
|
|
|
|
s = Serial(env.subst(port))
|
|
|
|
|
s.flushInput()
|
|
|
|
|
s.setDTR(False)
|
|
|
|
|
s.setRTS(False)
|
|
|
|
|
sleep(0.1)
|
|
|
|
|
s.setDTR(True)
|
|
|
|
|
s.setRTS(True)
|
|
|
|
|
s.close()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def TouchSerialPort(env, port, baudrate):
|
2016-06-15 16:02:04 +03:00
|
|
|
port = env.subst(port)
|
2018-10-27 20:22:11 +03:00
|
|
|
print("Forcing reset using %dbps open/close on port %s" % (baudrate, port))
|
2016-07-11 19:12:10 +03:00
|
|
|
try:
|
|
|
|
|
s = Serial(port=port, baudrate=baudrate)
|
|
|
|
|
s.setDTR(False)
|
|
|
|
|
s.close()
|
2018-06-04 14:09:48 +03:00
|
|
|
except: # pylint: disable=bare-except
|
2016-07-11 19:12:10 +03:00
|
|
|
pass
|
2017-08-01 00:33:51 +03:00
|
|
|
sleep(0.4) # DO NOT REMOVE THAT (required by SAM-BA based boards)
|
2015-03-04 21:21:10 +02:00
|
|
|
|
|
|
|
|
|
2016-06-03 00:42:52 +03:00
|
|
|
def WaitForNewSerialPort(env, before):
|
2018-10-27 20:22:11 +03:00
|
|
|
print("Waiting for the new upload port...")
|
2016-06-03 18:35:47 +03:00
|
|
|
prev_port = env.subst("$UPLOAD_PORT")
|
2015-03-04 21:21:10 +02:00
|
|
|
new_port = None
|
|
|
|
|
elapsed = 0
|
2017-06-24 01:29:53 +03:00
|
|
|
before = [p['port'] for p in before]
|
2016-06-03 18:35:47 +03:00
|
|
|
while elapsed < 5 and new_port is None:
|
2017-12-18 21:31:49 +02:00
|
|
|
now = [p['port'] for p in util.get_serial_ports()]
|
2016-06-03 18:35:47 +03:00
|
|
|
for p in now:
|
|
|
|
|
if p not in before:
|
2017-06-24 01:29:53 +03:00
|
|
|
new_port = p
|
2016-06-03 18:35:47 +03:00
|
|
|
break
|
2015-03-04 21:21:10 +02:00
|
|
|
before = now
|
|
|
|
|
sleep(0.25)
|
|
|
|
|
elapsed += 0.25
|
|
|
|
|
|
2016-06-03 18:35:47 +03:00
|
|
|
if not new_port:
|
|
|
|
|
for p in now:
|
2017-06-24 01:29:53 +03:00
|
|
|
if prev_port == p:
|
|
|
|
|
new_port = p
|
2016-06-03 18:35:47 +03:00
|
|
|
break
|
|
|
|
|
|
2017-05-30 14:06:25 +03:00
|
|
|
try:
|
2017-05-30 19:42:32 +03:00
|
|
|
s = Serial(new_port)
|
|
|
|
|
s.close()
|
2017-05-30 14:06:25 +03:00
|
|
|
except SerialException:
|
|
|
|
|
sleep(1)
|
|
|
|
|
|
2015-03-04 21:21:10 +02:00
|
|
|
if not new_port:
|
2016-08-26 14:39:23 +03:00
|
|
|
sys.stderr.write("Error: Couldn't find a board on the selected port. "
|
|
|
|
|
"Check that you have the correct port selected. "
|
|
|
|
|
"If it is correct, try pressing the board's reset "
|
|
|
|
|
"button after initiating the upload.\n")
|
|
|
|
|
env.Exit(1)
|
2015-03-04 21:21:10 +02:00
|
|
|
|
|
|
|
|
return new_port
|
|
|
|
|
|
|
|
|
|
|
2018-06-04 14:09:48 +03:00
|
|
|
def AutodetectUploadPort(*args, **kwargs):
|
2016-06-11 15:12:27 +03:00
|
|
|
env = args[0]
|
2015-03-09 12:27:54 +02:00
|
|
|
|
2016-12-03 17:59:15 +02:00
|
|
|
def _get_pattern():
|
|
|
|
|
if "UPLOAD_PORT" not in env:
|
|
|
|
|
return None
|
|
|
|
|
if set(["*", "?", "[", "]"]) & set(env['UPLOAD_PORT']):
|
|
|
|
|
return env['UPLOAD_PORT']
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
def _is_match_pattern(port):
|
|
|
|
|
pattern = _get_pattern()
|
|
|
|
|
if not pattern:
|
|
|
|
|
return True
|
|
|
|
|
return fnmatch(port, pattern)
|
|
|
|
|
|
2016-06-08 13:47:03 +03:00
|
|
|
def _look_for_mbed_disk():
|
2017-05-11 14:49:44 +03:00
|
|
|
msdlabels = ("mbed", "nucleo", "frdm", "microbit")
|
2017-12-18 21:31:49 +02:00
|
|
|
for item in util.get_logical_devices():
|
2017-12-27 16:02:36 +02:00
|
|
|
if item['path'].startswith("/net") or not _is_match_pattern(
|
|
|
|
|
item['path']):
|
2016-12-03 17:59:15 +02:00
|
|
|
continue
|
2017-04-14 18:05:15 +03:00
|
|
|
mbed_pages = [
|
2017-12-27 16:02:36 +02:00
|
|
|
join(item['path'], n) for n in ("mbed.htm", "mbed.html")
|
2017-04-14 18:05:15 +03:00
|
|
|
]
|
2018-01-18 22:04:43 +02:00
|
|
|
if any(isfile(p) for p in mbed_pages):
|
2017-12-27 16:02:36 +02:00
|
|
|
return item['path']
|
2017-11-07 00:10:48 +02:00
|
|
|
if item['name'] \
|
2018-01-18 22:04:43 +02:00
|
|
|
and any(l in item['name'].lower() for l in msdlabels):
|
2017-12-27 16:02:36 +02:00
|
|
|
return item['path']
|
2016-06-08 13:47:03 +03:00
|
|
|
return None
|
2016-06-03 20:57:20 +03:00
|
|
|
|
2016-06-08 13:47:03 +03:00
|
|
|
def _look_for_serial_port():
|
|
|
|
|
port = None
|
2016-05-26 19:43:36 +03:00
|
|
|
board_hwids = []
|
2017-11-07 00:10:48 +02:00
|
|
|
upload_protocol = env.subst("$UPLOAD_PROTOCOL")
|
2016-05-26 19:43:36 +03:00
|
|
|
if "BOARD" in env and "build.hwids" in env.BoardConfig():
|
|
|
|
|
board_hwids = env.BoardConfig().get("build.hwids")
|
2017-12-18 21:31:49 +02:00
|
|
|
for item in util.get_serial_ports(filter_hwid=True):
|
2016-12-03 17:59:15 +02:00
|
|
|
if not _is_match_pattern(item['port']):
|
|
|
|
|
continue
|
2016-06-08 13:47:03 +03:00
|
|
|
port = item['port']
|
2018-04-23 17:30:05 +03:00
|
|
|
if upload_protocol.startswith("blackmagic"):
|
2019-05-10 17:26:10 +03:00
|
|
|
if WINDOWS and port.startswith("COM") and len(port) > 4:
|
2018-04-23 17:30:05 +03:00
|
|
|
port = "\\\\.\\%s" % port
|
|
|
|
|
if "GDB" in item['description']:
|
|
|
|
|
return port
|
2016-05-26 19:43:36 +03:00
|
|
|
for hwid in board_hwids:
|
|
|
|
|
hwid_str = ("%s:%s" % (hwid[0], hwid[1])).replace("0x", "")
|
|
|
|
|
if hwid_str in item['hwid']:
|
2016-06-08 13:47:03 +03:00
|
|
|
return port
|
|
|
|
|
return port
|
2015-03-04 21:21:10 +02:00
|
|
|
|
2016-12-03 17:59:15 +02:00
|
|
|
if "UPLOAD_PORT" in env and not _get_pattern():
|
2018-10-27 20:22:11 +03:00
|
|
|
print(env.subst("Use manually specified: $UPLOAD_PORT"))
|
2016-06-08 13:47:03 +03:00
|
|
|
return
|
|
|
|
|
|
2018-01-27 00:18:32 +02:00
|
|
|
if (env.subst("$UPLOAD_PROTOCOL") == "mbed"
|
|
|
|
|
or ("mbed" in env.subst("$PIOFRAMEWORK")
|
|
|
|
|
and not env.subst("$UPLOAD_PROTOCOL"))):
|
2016-06-08 13:47:03 +03:00
|
|
|
env.Replace(UPLOAD_PORT=_look_for_mbed_disk())
|
|
|
|
|
else:
|
2018-10-12 19:35:58 +03:00
|
|
|
try:
|
|
|
|
|
util.ensure_udev_rules()
|
|
|
|
|
except exception.InvalidUdevRules as e:
|
|
|
|
|
sys.stderr.write("\n%s\n\n" % e)
|
2016-06-08 13:47:03 +03:00
|
|
|
env.Replace(UPLOAD_PORT=_look_for_serial_port())
|
|
|
|
|
|
|
|
|
|
if env.subst("$UPLOAD_PORT"):
|
2018-10-27 20:22:11 +03:00
|
|
|
print(env.subst("Auto-detected: $UPLOAD_PORT"))
|
2015-06-08 23:02:05 +03:00
|
|
|
else:
|
2016-08-26 14:39:23 +03:00
|
|
|
sys.stderr.write(
|
|
|
|
|
"Error: Please specify `upload_port` for environment or use "
|
|
|
|
|
"global `--upload-port` option.\n"
|
2016-08-27 19:30:38 +03:00
|
|
|
"For some development platforms it can be a USB flash "
|
2016-08-26 14:39:23 +03:00
|
|
|
"drive (i.e. /media/<user>/<device name>)\n")
|
|
|
|
|
env.Exit(1)
|
2015-03-04 21:21:10 +02:00
|
|
|
|
|
|
|
|
|
2018-06-04 14:09:48 +03:00
|
|
|
def UploadToDisk(_, target, source, env):
|
2016-08-27 19:30:38 +03:00
|
|
|
assert "UPLOAD_PORT" in env
|
2016-06-01 23:33:15 +03:00
|
|
|
progname = env.subst("$PROGNAME")
|
2015-11-02 23:03:05 +02:00
|
|
|
for ext in ("bin", "hex"):
|
2016-06-01 23:33:15 +03:00
|
|
|
fpath = join(env.subst("$BUILD_DIR"), "%s.%s" % (progname, ext))
|
2015-11-02 23:03:05 +02:00
|
|
|
if not isfile(fpath):
|
|
|
|
|
continue
|
2016-10-31 20:05:34 +02:00
|
|
|
copyfile(fpath,
|
|
|
|
|
join(env.subst("$UPLOAD_PORT"), "%s.%s" % (progname, ext)))
|
2018-10-27 20:22:11 +03:00
|
|
|
print("Firmware has been successfully uploaded.\n"
|
|
|
|
|
"(Some boards may require manual hard reset)")
|
2015-03-13 00:02:31 +02:00
|
|
|
|
|
|
|
|
|
2018-06-04 14:09:48 +03:00
|
|
|
def CheckUploadSize(_, target, source, env):
|
|
|
|
|
check_conditions = [
|
|
|
|
|
env.get("BOARD"),
|
|
|
|
|
env.get("SIZETOOL") or env.get("SIZECHECKCMD")
|
2016-10-31 20:05:34 +02:00
|
|
|
]
|
2018-06-04 14:09:48 +03:00
|
|
|
if not all(check_conditions):
|
|
|
|
|
return
|
|
|
|
|
program_max_size = int(env.BoardConfig().get("upload.maximum_size", 0))
|
|
|
|
|
data_max_size = int(env.BoardConfig().get("upload.maximum_ram_size", 0))
|
|
|
|
|
if program_max_size == 0:
|
2016-06-11 00:02:36 +03:00
|
|
|
return
|
|
|
|
|
|
2018-06-04 14:09:48 +03:00
|
|
|
def _configure_defaults():
|
|
|
|
|
env.Replace(
|
|
|
|
|
SIZECHECKCMD="$SIZETOOL -B -d $SOURCES",
|
|
|
|
|
SIZEPROGREGEXP=r"^(\d+)\s+(\d+)\s+\d+\s",
|
|
|
|
|
SIZEDATAREGEXP=r"^\d+\s+(\d+)\s+(\d+)\s+\d+")
|
2016-06-11 00:02:36 +03:00
|
|
|
|
2018-06-04 14:09:48 +03:00
|
|
|
def _get_size_output():
|
|
|
|
|
cmd = env.get("SIZECHECKCMD")
|
|
|
|
|
if not cmd:
|
|
|
|
|
return None
|
|
|
|
|
if not isinstance(cmd, list):
|
|
|
|
|
cmd = cmd.split()
|
|
|
|
|
cmd = [arg.replace("$SOURCES", str(source[0])) for arg in cmd if arg]
|
|
|
|
|
sysenv = environ.copy()
|
|
|
|
|
sysenv['PATH'] = str(env['ENV']['PATH'])
|
|
|
|
|
result = util.exec_command(env.subst(cmd), env=sysenv)
|
|
|
|
|
if result['returncode'] != 0:
|
|
|
|
|
return None
|
|
|
|
|
return result['out'].strip()
|
|
|
|
|
|
|
|
|
|
def _calculate_size(output, pattern):
|
|
|
|
|
if not output or not pattern:
|
|
|
|
|
return -1
|
|
|
|
|
size = 0
|
|
|
|
|
regexp = re.compile(pattern)
|
|
|
|
|
for line in output.split("\n"):
|
|
|
|
|
line = line.strip()
|
|
|
|
|
if not line:
|
|
|
|
|
continue
|
|
|
|
|
match = regexp.search(line)
|
|
|
|
|
if not match:
|
|
|
|
|
continue
|
|
|
|
|
size += sum(int(value) for value in match.groups())
|
|
|
|
|
return size
|
|
|
|
|
|
|
|
|
|
def _format_availale_bytes(value, total):
|
|
|
|
|
percent_raw = float(value) / float(total)
|
|
|
|
|
blocks_per_progress = 10
|
|
|
|
|
used_blocks = int(round(blocks_per_progress * percent_raw))
|
|
|
|
|
if used_blocks > blocks_per_progress:
|
|
|
|
|
used_blocks = blocks_per_progress
|
|
|
|
|
return "[{:{}}] {: 6.1%} (used {:d} bytes from {:d} bytes)".format(
|
|
|
|
|
"=" * used_blocks, blocks_per_progress, percent_raw, value, total)
|
|
|
|
|
|
|
|
|
|
if not env.get("SIZECHECKCMD") and not env.get("SIZEPROGREGEXP"):
|
|
|
|
|
_configure_defaults()
|
|
|
|
|
output = _get_size_output()
|
|
|
|
|
program_size = _calculate_size(output, env.get("SIZEPROGREGEXP"))
|
|
|
|
|
data_size = _calculate_size(output, env.get("SIZEDATAREGEXP"))
|
|
|
|
|
|
2018-10-27 20:22:11 +03:00
|
|
|
print("Memory Usage -> http://bit.ly/pio-memory-usage")
|
2018-06-04 14:09:48 +03:00
|
|
|
if data_max_size and data_size > -1:
|
2018-10-27 20:22:11 +03:00
|
|
|
print("DATA: %s" % _format_availale_bytes(data_size, data_max_size))
|
2018-06-04 14:09:48 +03:00
|
|
|
if program_size > -1:
|
2018-10-27 20:22:11 +03:00
|
|
|
print("PROGRAM: %s" % _format_availale_bytes(program_size,
|
|
|
|
|
program_max_size))
|
2018-06-04 14:09:48 +03:00
|
|
|
if int(ARGUMENTS.get("PIOVERBOSE", 0)):
|
2018-10-27 20:22:11 +03:00
|
|
|
print(output)
|
2018-06-04 14:09:48 +03:00
|
|
|
|
|
|
|
|
# raise error
|
2018-06-10 01:56:48 +03:00
|
|
|
# if data_max_size and data_size > data_max_size:
|
|
|
|
|
# sys.stderr.write(
|
|
|
|
|
# "Error: The data size (%d bytes) is greater "
|
|
|
|
|
# "than maximum allowed (%s bytes)\n" % (data_size, data_max_size))
|
|
|
|
|
# env.Exit(1)
|
2018-06-04 14:09:48 +03:00
|
|
|
if program_size > program_max_size:
|
2016-08-26 14:39:23 +03:00
|
|
|
sys.stderr.write("Error: The program size (%d bytes) is greater "
|
2018-06-04 14:09:48 +03:00
|
|
|
"than maximum allowed (%s bytes)\n" %
|
|
|
|
|
(program_size, program_max_size))
|
2016-08-26 14:39:23 +03:00
|
|
|
env.Exit(1)
|
2016-06-11 00:02:36 +03:00
|
|
|
|
|
|
|
|
|
2018-01-27 00:18:32 +02:00
|
|
|
def PrintUploadInfo(env):
|
2018-01-27 01:16:37 +02:00
|
|
|
configured = env.subst("$UPLOAD_PROTOCOL")
|
2018-02-09 01:23:02 +02:00
|
|
|
available = [configured] if configured else []
|
2018-01-27 01:16:37 +02:00
|
|
|
if "BOARD" in env:
|
2018-02-09 01:23:02 +02:00
|
|
|
available.extend(env.BoardConfig().get("upload", {}).get(
|
|
|
|
|
"protocols", []))
|
2018-01-30 23:31:21 +02:00
|
|
|
if available:
|
2018-10-27 20:22:11 +03:00
|
|
|
print("AVAILABLE: %s" % ", ".join(sorted(set(available))))
|
2018-01-30 23:31:21 +02:00
|
|
|
if configured:
|
2018-10-27 20:22:11 +03:00
|
|
|
print("CURRENT: upload_protocol = %s" % configured)
|
2018-01-27 00:18:32 +02:00
|
|
|
|
|
|
|
|
|
2015-03-04 21:21:10 +02:00
|
|
|
def exists(_):
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def generate(env):
|
|
|
|
|
env.AddMethod(FlushSerialBuffer)
|
|
|
|
|
env.AddMethod(TouchSerialPort)
|
|
|
|
|
env.AddMethod(WaitForNewSerialPort)
|
|
|
|
|
env.AddMethod(AutodetectUploadPort)
|
2015-03-13 00:02:31 +02:00
|
|
|
env.AddMethod(UploadToDisk)
|
2016-06-11 00:02:36 +03:00
|
|
|
env.AddMethod(CheckUploadSize)
|
2018-01-27 00:18:32 +02:00
|
|
|
env.AddMethod(PrintUploadInfo)
|
2015-03-04 21:21:10 +02:00
|
|
|
return env
|