mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-07 16:29:51 +03:00
initial commit
This commit is contained in:
@@ -17,8 +17,10 @@ import os
|
||||
# 3fffffb0: feefeffe feefeffe 3ffe8558 40100b01
|
||||
# <<<stack<<<
|
||||
|
||||
call(['python', 'scripts/decoder.py ', '-s', '-e', os.getcwd()+"/.pio/build/debug/firmware_debug_1_9_1b3.elf", 'scripts/stackdmp.txt'])
|
||||
call(['python', 'scripts/decoder.py ', '-s', '-e', os.getcwd()+"/.pio/build/esp12e/firmware.elf", 'scripts/stackdmp.txt'])
|
||||
|
||||
# example for linux:
|
||||
# % cd EMS-ESP
|
||||
# % python scripts/decoder_linux.py -s -e .pio/build/debug/firmware_d1_mini.elf scripts/stackdmp.txt
|
||||
# % python scripts/decoder_linux.py -s -e .pio/build/esp12e/firmware.elf scripts/stackdmp.txt
|
||||
|
||||
# python decoder_linux.py -s -e ../.pio/build/esp8266-debug/firmware.elf stackdmp.txt
|
||||
|
||||
187
scripts/build.sh
187
scripts/build.sh
@@ -1,187 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
### Functions
|
||||
|
||||
is_git() {
|
||||
command -v git >/dev/null 2>&1 || return 1
|
||||
command git rev-parse >/dev/null 2>&1 || return 1
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
stat_bytes() {
|
||||
filesize=`du -k "$1" | cut -f1;`
|
||||
echo 'size:' $filesize 'bytes'
|
||||
}
|
||||
|
||||
# Available environments
|
||||
list_envs() {
|
||||
grep env: platformio.ini | sed 's/\[env:\(.*\)\]/\1/g'
|
||||
}
|
||||
|
||||
print_available() {
|
||||
echo "--------------------------------------------------------------"
|
||||
echo "Available environments:"
|
||||
for environment in $available; do
|
||||
echo "-> $environment"
|
||||
done
|
||||
}
|
||||
|
||||
print_environments() {
|
||||
echo "--------------------------------------------------------------"
|
||||
echo "Current environments:"
|
||||
for environment in $environments; do
|
||||
echo "-> $environment"
|
||||
done
|
||||
}
|
||||
|
||||
set_default_environments() {
|
||||
# Hook to build in parallel when using travis
|
||||
if [[ "${TRAVIS_BUILD_STAGE_NAME}" = "Release" ]] && ${par_build}; then
|
||||
environments=$(echo ${available} | \
|
||||
awk -v par_thread=${par_thread} -v par_total_threads=${par_total_threads} \
|
||||
'{ for (i = 1; i <= NF; i++) if (++j % par_total_threads == par_thread ) print $i; }')
|
||||
return
|
||||
fi
|
||||
|
||||
# Only build travis target
|
||||
if [[ "${TRAVIS_BUILD_STAGE_NAME}" = "Test" ]]; then
|
||||
environments=$travis
|
||||
return
|
||||
fi
|
||||
|
||||
# Fallback to all available environments
|
||||
environments=$available
|
||||
}
|
||||
|
||||
build_webui() {
|
||||
cd ./tools/webfilesbuilder
|
||||
|
||||
# Build system uses gulpscript.js to build web interface
|
||||
if [ ! -e node_modules/gulp/bin/gulp.js ]; then
|
||||
echo "--------------------------------------------------------------"
|
||||
echo "Installing dependencies..."
|
||||
npm ci
|
||||
fi
|
||||
|
||||
# Recreate web interface - "node ./tools/webfilesbuilder/node_modules/gulp/bin/gulp.js --cwd ./tools/webfilesbuilder"
|
||||
echo "--------------------------------------------------------------"
|
||||
echo "Building web interface..."
|
||||
node node_modules/gulp/bin/gulp.js || exit
|
||||
|
||||
cd ../..
|
||||
}
|
||||
|
||||
build_environments() {
|
||||
echo "--------------------------------------------------------------"
|
||||
echo "Building firmware images..."
|
||||
# don't move to firmware folder until Travis fixed (see https://github.com/travis-ci/dpl/issues/846#issuecomment-547157406)
|
||||
# mkdir -p $destination
|
||||
|
||||
for environment in $environments; do
|
||||
echo "* EMS-ESP-$version-$environment.bin"
|
||||
platformio run --silent --environment $environment || exit 1
|
||||
stat_bytes .pio/build/$environment/firmware.bin
|
||||
# mv .pio/build/$environment/firmware.bin $destination/EMS-ESP-$version-$environment.bin
|
||||
# mv .pio/build/$environment/firmware.bin EMS-ESP-$version-$environment.bin
|
||||
mv .pio/build/$environment/firmware.bin EMS-ESP-dev-$environment.bin
|
||||
done
|
||||
echo "--------------------------------------------------------------"
|
||||
}
|
||||
|
||||
|
||||
####### MAIN
|
||||
|
||||
destination=firmware
|
||||
version_file=./src/version.h
|
||||
version=$(grep -E '^#define APP_VERSION' $version_file | awk '{print $3}' | sed 's/"//g')
|
||||
|
||||
if ${TRAVIS:-false}; then
|
||||
git_revision=${TRAVIS_COMMIT::7}
|
||||
git_tag=${TRAVIS_TAG}
|
||||
elif is_git; then
|
||||
git_revision=$(git rev-parse --short HEAD)
|
||||
git_tag=$(git tag --contains HEAD)
|
||||
else
|
||||
git_revision=unknown
|
||||
git_tag=
|
||||
fi
|
||||
|
||||
echo $git_tag
|
||||
|
||||
if [[ -n $git_tag ]]; then
|
||||
new_version=${version/-*}
|
||||
sed -i -e "s@$version@$new_version@" $version_file
|
||||
version=$new_version
|
||||
trap "git checkout -- $version_file" EXIT
|
||||
fi
|
||||
|
||||
par_build=false
|
||||
par_thread=${BUILDER_THREAD:-0}
|
||||
par_total_threads=${BUILDER_TOTAL_THREADS:-4}
|
||||
if [ ${par_thread} -ne ${par_thread} -o \
|
||||
${par_total_threads} -ne ${par_total_threads} ]; then
|
||||
echo "Parallel threads should be a number."
|
||||
exit
|
||||
fi
|
||||
if [ ${par_thread} -ge ${par_total_threads} ]; then
|
||||
echo "Current thread is greater than total threads. Doesn't make sense"
|
||||
exit
|
||||
fi
|
||||
|
||||
# travis platformio target is used for nightly Test
|
||||
travis=$(list_envs | grep travis | sort)
|
||||
|
||||
# get all taregts, excluding travis and debug
|
||||
available=$(list_envs | grep -Ev -- 'travis|debug|release' | sort)
|
||||
|
||||
export PLATFORMIO_BUILD_FLAGS="${PLATFORMIO_BUILD_FLAGS}"
|
||||
|
||||
# get command line Parameters
|
||||
# l prints environments
|
||||
# 2 does parallel builds
|
||||
# d uses next arg as destination folder
|
||||
while getopts "lpd:" opt; do
|
||||
case $opt in
|
||||
l)
|
||||
print_available
|
||||
exit
|
||||
;;
|
||||
p)
|
||||
par_build=true
|
||||
;;
|
||||
d)
|
||||
destination=$OPTARG
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
shift $((OPTIND-1))
|
||||
|
||||
# Welcome message
|
||||
echo "--------------------------------------------------------------"
|
||||
echo "EMS-ESP FIRMWARE BUILDER"
|
||||
echo "Building for version ${version}" ${git_revision:+($git_revision)}
|
||||
|
||||
# Environments to build
|
||||
environments=$@
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
set_default_environments
|
||||
fi
|
||||
|
||||
if ${CI:-false}; then
|
||||
print_environments
|
||||
fi
|
||||
|
||||
# for debugging
|
||||
echo "* git_revision = $git_revision"
|
||||
echo "* git_tag = $git_tag"
|
||||
echo "* TRAVIS_COMMIT = $TRAVIS_COMMIT"
|
||||
echo "* TRAVIS_TAG = $TRAVIS_TAG"
|
||||
echo "* TRAVIS_BRANCH = $TRAVIS_BRANCH"
|
||||
echo "* TRAVIS_BUILD_STAGE_NAME = $TRAVIS_BUILD_STAGE_NAME"
|
||||
|
||||
build_webui
|
||||
build_environments
|
||||
@@ -290,7 +290,7 @@ if __name__ == "__main__":
|
||||
file = open(args.file, "r")
|
||||
|
||||
addr2line = os.path.join(os.path.abspath(os.path.expanduser(args.tool)),
|
||||
"bin/xtensa-" + PLATFORMS[args.platform] + "-elf-addr2line")
|
||||
"bin/xtensa-" + PLATFORMS[args.platform] + "-elf-addr2line.exe")
|
||||
if not os.path.exists(addr2line):
|
||||
print("ERROR: addr2line not found (" + addr2line + ")")
|
||||
|
||||
|
||||
343
scripts/espota.py
Normal file
343
scripts/espota.py
Normal file
@@ -0,0 +1,343 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Original espota.py by Ivan Grokhotkov:
|
||||
# https://gist.github.com/igrr/d35ab8446922179dc58c
|
||||
#
|
||||
# Modified since 2015-09-18 from Pascal Gollor (https://github.com/pgollor)
|
||||
# Modified since 2015-11-09 from Hristo Gochkov (https://github.com/me-no-dev)
|
||||
# Modified since 2016-01-03 from Matthew O'Gorman (https://githumb.com/mogorman)
|
||||
#
|
||||
# This script will push an OTA update to the ESP
|
||||
# use it like: python3 espota.py -i <ESP_IP_address> -I <Host_IP_address> -p <ESP_port> -P <Host_port> [-a password] -f <sketch.bin>
|
||||
# Or to upload SPIFFS image:
|
||||
# python3 espota.py -i <ESP_IP_address> -I <Host_IP_address> -p <ESP_port> -P <HOST_port> [-a password] -s -f <spiffs.bin>
|
||||
#
|
||||
# Changes
|
||||
# 2015-09-18:
|
||||
# - Add option parser.
|
||||
# - Add logging.
|
||||
# - Send command to controller to differ between flashing and transmitting SPIFFS image.
|
||||
#
|
||||
# Changes
|
||||
# 2015-11-09:
|
||||
# - Added digest authentication
|
||||
# - Enhanced error tracking and reporting
|
||||
#
|
||||
# Changes
|
||||
# 2016-01-03:
|
||||
# - Added more options to parser.
|
||||
#
|
||||
|
||||
from __future__ import print_function
|
||||
import socket
|
||||
import sys
|
||||
import os
|
||||
import optparse
|
||||
import logging
|
||||
import hashlib
|
||||
import random
|
||||
|
||||
# Commands
|
||||
FLASH = 0
|
||||
SPIFFS = 100
|
||||
AUTH = 200
|
||||
PROGRESS = False
|
||||
# update_progress() : Displays or updates a console progress bar
|
||||
## Accepts a float between 0 and 1. Any int will be converted to a float.
|
||||
## A value under 0 represents a 'halt'.
|
||||
## A value at 1 or bigger represents 100%
|
||||
def update_progress(progress):
|
||||
if (PROGRESS):
|
||||
barLength = 60 # Modify this to change the length of the progress bar
|
||||
status = ""
|
||||
if isinstance(progress, int):
|
||||
progress = float(progress)
|
||||
if not isinstance(progress, float):
|
||||
progress = 0
|
||||
status = "error: progress var must be float\r\n"
|
||||
if progress < 0:
|
||||
progress = 0
|
||||
status = "Halt...\r\n"
|
||||
if progress >= 1:
|
||||
progress = 1
|
||||
status = "Done...\r\n"
|
||||
block = int(round(barLength*progress))
|
||||
text = "\rUploading: [{0}] {1}% {2}".format( "="*block + " "*(barLength-block), int(progress*100), status)
|
||||
sys.stderr.write(text)
|
||||
sys.stderr.flush()
|
||||
else:
|
||||
sys.stderr.write('.')
|
||||
sys.stderr.flush()
|
||||
|
||||
def serve(remoteAddr, localAddr, remotePort, localPort, password, filename, command = FLASH):
|
||||
# Create a TCP/IP socket
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
server_address = (localAddr, localPort)
|
||||
logging.info('Starting on %s:%s', str(server_address[0]), str(server_address[1]))
|
||||
try:
|
||||
sock.bind(server_address)
|
||||
sock.listen(1)
|
||||
except:
|
||||
logging.error("Listen Failed")
|
||||
return 1
|
||||
|
||||
# Check whether Signed Update is used.
|
||||
if ( os.path.isfile(filename + '.signed') ):
|
||||
filename = filename + '.signed'
|
||||
file_check_msg = 'Detected Signed Update. %s will be uploaded instead.' % (filename)
|
||||
sys.stderr.write(file_check_msg + '\n')
|
||||
sys.stderr.flush()
|
||||
logging.info(file_check_msg)
|
||||
|
||||
content_size = os.path.getsize(filename)
|
||||
f = open(filename,'rb')
|
||||
file_md5 = hashlib.md5(f.read()).hexdigest()
|
||||
f.close()
|
||||
logging.info('Upload size: %d', content_size)
|
||||
message = '%d %d %d %s\n' % (command, localPort, content_size, file_md5)
|
||||
|
||||
# Wait for a connection
|
||||
logging.info('Sending invitation to: %s', remoteAddr)
|
||||
sock2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
remote_address = (remoteAddr, int(remotePort))
|
||||
sent = sock2.sendto(message.encode(), remote_address)
|
||||
sock2.settimeout(10)
|
||||
try:
|
||||
data = sock2.recv(128).decode()
|
||||
except:
|
||||
logging.error('No Answer')
|
||||
sock2.close()
|
||||
return 1
|
||||
if (data != "OK"):
|
||||
if(data.startswith('AUTH')):
|
||||
nonce = data.split()[1]
|
||||
cnonce_text = '%s%u%s%s' % (filename, content_size, file_md5, remoteAddr)
|
||||
cnonce = hashlib.md5(cnonce_text.encode()).hexdigest()
|
||||
passmd5 = hashlib.md5(password.encode()).hexdigest()
|
||||
result_text = '%s:%s:%s' % (passmd5 ,nonce, cnonce)
|
||||
result = hashlib.md5(result_text.encode()).hexdigest()
|
||||
sys.stderr.write('Authenticating...')
|
||||
sys.stderr.flush()
|
||||
message = '%d %s %s\n' % (AUTH, cnonce, result)
|
||||
sock2.sendto(message.encode(), remote_address)
|
||||
sock2.settimeout(10)
|
||||
try:
|
||||
data = sock2.recv(32).decode()
|
||||
except:
|
||||
sys.stderr.write('FAIL\n')
|
||||
logging.error('No Answer to our Authentication')
|
||||
sock2.close()
|
||||
return 1
|
||||
if (data != "OK"):
|
||||
sys.stderr.write('FAIL\n')
|
||||
logging.error('%s', data)
|
||||
sock2.close()
|
||||
sys.exit(1)
|
||||
return 1
|
||||
sys.stderr.write('OK\n')
|
||||
else:
|
||||
logging.error('Bad Answer: %s', data)
|
||||
sock2.close()
|
||||
return 1
|
||||
sock2.close()
|
||||
|
||||
logging.info('Waiting for device...')
|
||||
try:
|
||||
sock.settimeout(10)
|
||||
connection, client_address = sock.accept()
|
||||
sock.settimeout(None)
|
||||
connection.settimeout(None)
|
||||
except:
|
||||
logging.error('No response from device')
|
||||
sock.close()
|
||||
return 1
|
||||
|
||||
received_ok = False
|
||||
|
||||
try:
|
||||
f = open(filename, "rb")
|
||||
if (PROGRESS):
|
||||
update_progress(0)
|
||||
else:
|
||||
sys.stderr.write('Uploading')
|
||||
sys.stderr.flush()
|
||||
offset = 0
|
||||
while True:
|
||||
chunk = f.read(1460)
|
||||
if not chunk: break
|
||||
offset += len(chunk)
|
||||
update_progress(offset/float(content_size))
|
||||
connection.settimeout(10)
|
||||
try:
|
||||
connection.sendall(chunk)
|
||||
if connection.recv(32).decode().find('O') >= 0:
|
||||
# connection will receive only digits or 'OK'
|
||||
received_ok = True
|
||||
except:
|
||||
sys.stderr.write('\n')
|
||||
logging.error('Error Uploading')
|
||||
connection.close()
|
||||
f.close()
|
||||
sock.close()
|
||||
return 1
|
||||
|
||||
sys.stderr.write('\n')
|
||||
logging.info('Waiting for result...')
|
||||
# libraries/ArduinoOTA/ArduinoOTA.cpp L311 L320
|
||||
# only sends digits or 'OK'. We must not not close
|
||||
# the connection before receiving the 'O' of 'OK'
|
||||
try:
|
||||
connection.settimeout(60)
|
||||
received_ok = False
|
||||
received_error = False
|
||||
while not (received_ok or received_error):
|
||||
reply = connection.recv(64).decode()
|
||||
# Look for either the "E" in ERROR or the "O" in OK response
|
||||
# Check for "E" first, since both strings contain "O"
|
||||
if reply.find('E') >= 0:
|
||||
sys.stderr.write('\n')
|
||||
logging.error('%s', reply)
|
||||
received_error = True
|
||||
elif reply.find('O') >= 0:
|
||||
logging.info('Result: OK')
|
||||
received_ok = True
|
||||
connection.close()
|
||||
f.close()
|
||||
sock.close()
|
||||
if received_ok:
|
||||
return 0
|
||||
return 1
|
||||
except:
|
||||
logging.error('No Result!')
|
||||
connection.close()
|
||||
f.close()
|
||||
sock.close()
|
||||
return 1
|
||||
|
||||
finally:
|
||||
connection.close()
|
||||
f.close()
|
||||
|
||||
sock.close()
|
||||
return 1
|
||||
# end serve
|
||||
|
||||
|
||||
def parser(unparsed_args):
|
||||
parser = optparse.OptionParser(
|
||||
usage = "%prog [options]",
|
||||
description = "Transmit image over the air to the esp8266 module with OTA support."
|
||||
)
|
||||
|
||||
# destination ip and port
|
||||
group = optparse.OptionGroup(parser, "Destination")
|
||||
group.add_option("-i", "--ip",
|
||||
dest = "esp_ip",
|
||||
action = "store",
|
||||
help = "ESP8266 IP Address.",
|
||||
default = False
|
||||
)
|
||||
group.add_option("-I", "--host_ip",
|
||||
dest = "host_ip",
|
||||
action = "store",
|
||||
help = "Host IP Address.",
|
||||
default = "0.0.0.0"
|
||||
)
|
||||
group.add_option("-p", "--port",
|
||||
dest = "esp_port",
|
||||
type = "int",
|
||||
help = "ESP8266 ota Port. Default 8266",
|
||||
default = 8266
|
||||
)
|
||||
group.add_option("-P", "--host_port",
|
||||
dest = "host_port",
|
||||
type = "int",
|
||||
help = "Host server ota Port. Default random 10000-60000",
|
||||
default = random.randint(10000,60000)
|
||||
)
|
||||
parser.add_option_group(group)
|
||||
|
||||
# auth
|
||||
group = optparse.OptionGroup(parser, "Authentication")
|
||||
group.add_option("-a", "--auth",
|
||||
dest = "auth",
|
||||
help = "Set authentication password.",
|
||||
action = "store",
|
||||
default = ""
|
||||
)
|
||||
parser.add_option_group(group)
|
||||
|
||||
# image
|
||||
group = optparse.OptionGroup(parser, "Image")
|
||||
group.add_option("-f", "--file",
|
||||
dest = "image",
|
||||
help = "Image file.",
|
||||
metavar="FILE",
|
||||
default = None
|
||||
)
|
||||
group.add_option("-s", "--spiffs",
|
||||
dest = "spiffs",
|
||||
action = "store_true",
|
||||
help = "Use this option to transmit a SPIFFS image and do not flash the module.",
|
||||
default = False
|
||||
)
|
||||
parser.add_option_group(group)
|
||||
|
||||
# output group
|
||||
group = optparse.OptionGroup(parser, "Output")
|
||||
group.add_option("-d", "--debug",
|
||||
dest = "debug",
|
||||
help = "Show debug output. And override loglevel with debug.",
|
||||
action = "store_true",
|
||||
default = False
|
||||
)
|
||||
group.add_option("-r", "--progress",
|
||||
dest = "progress",
|
||||
help = "Show progress output. Does not work for ArduinoIDE",
|
||||
action = "store_true",
|
||||
default = False
|
||||
)
|
||||
parser.add_option_group(group)
|
||||
|
||||
(options, args) = parser.parse_args(unparsed_args)
|
||||
|
||||
return options
|
||||
# end parser
|
||||
|
||||
|
||||
def main(args):
|
||||
# get options
|
||||
options = parser(args)
|
||||
|
||||
# adapt log level
|
||||
loglevel = logging.WARNING
|
||||
if (options.debug):
|
||||
loglevel = logging.DEBUG
|
||||
# end if
|
||||
|
||||
# logging
|
||||
logging.basicConfig(level = loglevel, format = '%(asctime)-8s [%(levelname)s]: %(message)s', datefmt = '%H:%M:%S')
|
||||
|
||||
logging.debug("Options: %s", str(options))
|
||||
|
||||
# check options
|
||||
global PROGRESS
|
||||
PROGRESS = options.progress
|
||||
if (not options.esp_ip or not options.image):
|
||||
logging.critical("Not enough arguments.")
|
||||
|
||||
return 1
|
||||
# end if
|
||||
|
||||
command = FLASH
|
||||
if (options.spiffs):
|
||||
command = SPIFFS
|
||||
# end if
|
||||
|
||||
return serve(options.esp_ip, options.host_ip, options.esp_port, options.host_port, options.auth, options.image, command)
|
||||
# end main
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main(sys.argv))
|
||||
# end if
|
||||
383
scripts/memanalyzer.py
Normal file
383
scripts/memanalyzer.py
Normal file
@@ -0,0 +1,383 @@
|
||||
#!/usr/bin/env python3
|
||||
# pylint: disable=C0301,C0114,C0116,W0511
|
||||
# coding=utf-8
|
||||
# -------------------------------------------------------------------------------
|
||||
# based on ESPurna module memory analyser by xose.perez@gmail.com
|
||||
#
|
||||
# Rewritten for python-3 and changed to use "size" instead of "objdump"
|
||||
# Based on https://github.com/esp8266/Arduino/pull/6525
|
||||
# by Maxim Prokhorov <prokhorov.max@outlook.com>
|
||||
#
|
||||
# Based on:
|
||||
# https://github.com/letscontrolit/ESPEasy/blob/mega/memanalyzer.py
|
||||
# by psy0rz <edwin@datux.nl>
|
||||
# https://raw.githubusercontent.com/SmingHub/Sming/develop/tools/memanalyzer.py
|
||||
# by Slavey Karadzhov <slav@attachix.com>
|
||||
# https://github.com/Sermus/ESP8266_memory_analyzer
|
||||
# by Andrey Filimonov
|
||||
#
|
||||
# -------------------------------------------------------------------------------
|
||||
#
|
||||
# When using Windows with non-default installation at the C:\.platformio,
|
||||
# you would need to specify toolchain path manually. For example:
|
||||
#
|
||||
# $ py -3 scripts\memanalyzer.py --toolchain-prefix C:\.platformio\packages\toolchain-xtensa\bin\xtensa-lx106-elf- <args>
|
||||
#
|
||||
# You could also change the path to platformio binary in a similar fashion:
|
||||
# $ py -3 scripts\memanalyzer.py --platformio-prefix C:\Users\Max\platformio-penv\Scripts\
|
||||
#
|
||||
# -------------------------------------------------------------------------------
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from collections import OrderedDict
|
||||
from subprocess import getstatusoutput
|
||||
|
||||
__version__ = (0, 3)
|
||||
|
||||
# -------------------------------------------------------------------------------
|
||||
|
||||
TOTAL_IRAM = 32786
|
||||
TOTAL_DRAM = 81920
|
||||
|
||||
DEFAULT_ENV = "esp12e"
|
||||
TOOLCHAIN_PREFIX = "~/.platformio/packages/toolchain-xtensa/bin/xtensa-lx106-elf-"
|
||||
PLATFORMIO_PREFIX = ""
|
||||
SECTIONS = OrderedDict(
|
||||
[
|
||||
(".data", "Initialized Data (RAM)"),
|
||||
(".rodata", "ReadOnly Data (RAM)"),
|
||||
(".bss", "Uninitialized Data (RAM)"),
|
||||
(".text", "Cached Code (IRAM)"),
|
||||
(".irom0.text", "Uncached Code (SPI)"),
|
||||
]
|
||||
)
|
||||
DESCRIPTION = "Memory Analyzer v{}".format(
|
||||
".".join(str(x) for x in __version__)
|
||||
)
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def size_binary_path(prefix):
|
||||
return "{}size".format(os.path.expanduser(prefix))
|
||||
|
||||
|
||||
def file_size(file):
|
||||
try:
|
||||
return os.stat(file).st_size
|
||||
except OSError:
|
||||
return 0
|
||||
|
||||
|
||||
def analyse_memory(size, elf_file):
|
||||
proc = subprocess.Popen(
|
||||
[size, "-A", elf_file], stdout=subprocess.PIPE, universal_newlines=True
|
||||
)
|
||||
lines = proc.stdout.readlines()
|
||||
|
||||
values = {}
|
||||
|
||||
for line in lines:
|
||||
words = line.split()
|
||||
for name in SECTIONS.keys():
|
||||
if line.startswith(name):
|
||||
value = values.setdefault(name, 0)
|
||||
value += int(words[1])
|
||||
values[name] = value
|
||||
break
|
||||
|
||||
return values
|
||||
|
||||
|
||||
def run(prefix, env, modules, debug):
|
||||
flags = " ".join("-D{}_SUPPORT={:d}".format(k, v) for k, v in modules.items())
|
||||
|
||||
os_env = os.environ.copy()
|
||||
os_env["PLATFORMIO_SRC_BUILD_FLAGS"] = flags
|
||||
os_env["PLATFORMIO_BUILD_CACHE_DIR"] = "test/pio_cache"
|
||||
|
||||
if debug:
|
||||
print("Selected flags: {}".format(flags))
|
||||
|
||||
command = [os.path.join(prefix, "platformio"), "run"]
|
||||
if not debug:
|
||||
command.append("--silent")
|
||||
command.extend(["--environment", env])
|
||||
|
||||
output = None if debug else subprocess.DEVNULL
|
||||
|
||||
|
||||
try:
|
||||
subprocess.check_call(
|
||||
command, shell=False, env=os_env, stdout=output, stderr=output
|
||||
)
|
||||
except subprocess.CalledProcessError:
|
||||
print(" - Command failed: {}".format(command))
|
||||
print(" - Selected flags: {}".format(flags))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def get_available_modules():
|
||||
modules = []
|
||||
for line in open("lib/framework/services.h"):
|
||||
match = re.search(r"(\w*)_SUPPORT", line)
|
||||
if match:
|
||||
modules.append((match.group(1), 0))
|
||||
modules.sort(key=lambda item: item[0])
|
||||
|
||||
return OrderedDict(modules)
|
||||
|
||||
|
||||
def parse_commandline_args():
|
||||
parser = argparse.ArgumentParser(
|
||||
description=DESCRIPTION, formatter_class=argparse.ArgumentDefaultsHelpFormatter
|
||||
)
|
||||
parser.add_argument(
|
||||
"-e", "--environment", help="platformio environment to use", default=DEFAULT_ENV
|
||||
)
|
||||
parser.add_argument(
|
||||
"--toolchain-prefix",
|
||||
help="where to find the xtensa toolchain binaries",
|
||||
default=TOOLCHAIN_PREFIX,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--platformio-prefix",
|
||||
help="where to find the platformio executable",
|
||||
default=PLATFORMIO_PREFIX,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-c",
|
||||
"--core",
|
||||
help="use core as base configuration instead of default",
|
||||
action="store_true",
|
||||
default=False,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-l",
|
||||
"--list",
|
||||
help="list available modules",
|
||||
action="store_true",
|
||||
default=False,
|
||||
)
|
||||
parser.add_argument("-d", "--debug", action="store_true", default=False)
|
||||
parser.add_argument(
|
||||
"modules", nargs="*", help="Modules to test (use ALL to test them all)"
|
||||
)
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def size_binary_exists(args):
|
||||
|
||||
status, _ = getstatusoutput(size_binary_path(args.toolchain_prefix))
|
||||
if status != 1:
|
||||
print("size not found, please check that the --toolchain-prefix is correct")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def get_modules(args):
|
||||
|
||||
# Load list of all modules
|
||||
available_modules = get_available_modules()
|
||||
if args.list:
|
||||
print("List of available modules:\n")
|
||||
for module in available_modules:
|
||||
print("* " + module)
|
||||
print()
|
||||
sys.exit(0)
|
||||
|
||||
modules = []
|
||||
if args.modules:
|
||||
if "ALL" in args.modules:
|
||||
modules.extend(available_modules.keys())
|
||||
else:
|
||||
modules.extend(args.modules)
|
||||
modules.sort()
|
||||
|
||||
# Check test modules exist
|
||||
for module in modules:
|
||||
if module not in available_modules:
|
||||
print("Module {} not found".format(module))
|
||||
sys.exit(2)
|
||||
|
||||
# Either use all of modules or specified subset
|
||||
if args.core:
|
||||
modules = available_modules
|
||||
else:
|
||||
modules = OrderedDict((x, 0) for x in modules)
|
||||
|
||||
configuration = "CORE" if args.core else "DEFAULT"
|
||||
|
||||
return configuration, modules
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------------
|
||||
|
||||
|
||||
class Analyser:
|
||||
"""Run platformio and print info about the resulting binary."""
|
||||
|
||||
OUTPUT_FORMAT = "{:<20}|{:<15}|{:<15}|{:<15}|{:<15}|{:<15}|{:<15}|{:<15}"
|
||||
DELIMETERS = OUTPUT_FORMAT.format(
|
||||
"-" * 20, "-" * 15, "-" * 15, "-" * 15, "-" * 15, "-" * 15, "-" * 15, "-" * 15
|
||||
)
|
||||
FIRMWARE_FORMAT = ".pio/build/{env}/firmware.{suffix}"
|
||||
|
||||
class _Enable:
|
||||
def __init__(self, analyser, module=None):
|
||||
self.analyser = analyser
|
||||
self.module = module
|
||||
|
||||
def __enter__(self):
|
||||
if not self.module:
|
||||
for name in self.analyser.modules:
|
||||
self.analyser.modules[name] = 1
|
||||
else:
|
||||
self.analyser.modules[self.module] = 1
|
||||
return self.analyser
|
||||
|
||||
def __exit__(self, *args, **kwargs):
|
||||
if not self.module:
|
||||
for name in self.analyser.modules:
|
||||
self.analyser.modules[name] = 0
|
||||
else:
|
||||
self.analyser.modules[self.module] = 0
|
||||
|
||||
analyser = None
|
||||
module = None
|
||||
|
||||
def __init__(self, args, modules):
|
||||
self._debug = args.debug
|
||||
self._platformio_prefix = args.platformio_prefix
|
||||
self._toolchain_prefix = args.toolchain_prefix
|
||||
self._environment = args.environment
|
||||
self.modules = modules
|
||||
self.baseline = None
|
||||
|
||||
def enable(self, module=None):
|
||||
return self._Enable(self, module)
|
||||
|
||||
def print(self, *args):
|
||||
print(self.OUTPUT_FORMAT.format(*args))
|
||||
|
||||
def print_delimiters(self):
|
||||
print(self.DELIMETERS)
|
||||
|
||||
def begin(self, name):
|
||||
self.print(
|
||||
"Module",
|
||||
"Cache IRAM",
|
||||
"Init RAM",
|
||||
"R.O. RAM",
|
||||
"Uninit RAM",
|
||||
"Available RAM",
|
||||
"Flash ROM",
|
||||
"Binary size",
|
||||
)
|
||||
self.print(
|
||||
"",
|
||||
".text + .text1",
|
||||
".data",
|
||||
".rodata",
|
||||
".bss",
|
||||
"heap + stack",
|
||||
".irom0.text",
|
||||
"",
|
||||
)
|
||||
self.print_delimiters()
|
||||
self.baseline = self.run()
|
||||
self.print_values(name, self.baseline)
|
||||
|
||||
def print_values(self, header, values):
|
||||
self.print(
|
||||
header,
|
||||
values[".text"],
|
||||
values[".data"],
|
||||
values[".rodata"],
|
||||
values[".bss"],
|
||||
values["free"],
|
||||
values[".irom0.text"],
|
||||
values["size"],
|
||||
)
|
||||
|
||||
def print_compare(self, header, values):
|
||||
self.print(
|
||||
header,
|
||||
values[".text"] - self.baseline[".text"],
|
||||
values[".data"] - self.baseline[".data"],
|
||||
values[".rodata"] - self.baseline[".rodata"],
|
||||
values[".bss"] - self.baseline[".bss"],
|
||||
values["free"] - self.baseline["free"],
|
||||
values[".irom0.text"] - self.baseline[".irom0.text"],
|
||||
values["size"] - self.baseline["size"],
|
||||
)
|
||||
|
||||
def run(self):
|
||||
run(self._platformio_prefix, self._environment, self.modules, self._debug)
|
||||
|
||||
elf_path = self.FIRMWARE_FORMAT.format(env=self._environment, suffix="elf")
|
||||
bin_path = self.FIRMWARE_FORMAT.format(env=self._environment, suffix="bin")
|
||||
|
||||
values = analyse_memory(
|
||||
size_binary_path(self._toolchain_prefix), elf_path
|
||||
)
|
||||
|
||||
free = 80 * 1024 - values[".data"] - values[".rodata"] - values[".bss"]
|
||||
free = free + (16 - free % 16)
|
||||
values["free"] = free
|
||||
|
||||
values["size"] = file_size(bin_path)
|
||||
|
||||
return values
|
||||
|
||||
|
||||
def main(args):
|
||||
|
||||
# Check xtensa-lx106-elf-size is in the path
|
||||
size_binary_exists(args)
|
||||
|
||||
# Which modules to test?
|
||||
configuration, modules = get_modules(args)
|
||||
|
||||
# print_values init message
|
||||
print('Selected environment "{}"'.format(args.environment), end="")
|
||||
if modules:
|
||||
print(" with modules: {}".format(" ".join(modules.keys())))
|
||||
else:
|
||||
print()
|
||||
|
||||
print()
|
||||
print("Analyzing {} configuration".format(configuration))
|
||||
print()
|
||||
|
||||
# Build the core without any modules to get base memory usage
|
||||
analyser = Analyser(args, modules)
|
||||
analyser.begin(configuration)
|
||||
|
||||
# Test each module separately
|
||||
results = {}
|
||||
for module in analyser.modules:
|
||||
with analyser.enable(module):
|
||||
results[module] = analyser.run()
|
||||
analyser.print_compare(module, results[module])
|
||||
|
||||
# Test all modules
|
||||
if analyser.modules:
|
||||
|
||||
with analyser.enable():
|
||||
total = analyser.run()
|
||||
|
||||
analyser.print_delimiters()
|
||||
if len(analyser.modules) > 1:
|
||||
analyser.print_compare("ALL MODULES", total)
|
||||
|
||||
analyser.print_values("TOTAL", total)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(parse_commandline_args())
|
||||
4
scripts/ota.sh
Normal file
4
scripts/ota.sh
Normal file
@@ -0,0 +1,4 @@
|
||||
# python espota.py -i 10.10.10.189 --port 8267 --auth neo -f ../.pio/build/debug/firmware.bin
|
||||
|
||||
python espota.py --debug --progress -i 10.10.10.100 -f ../.pio/build/debug/firmware.bin
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
from subprocess import call
|
||||
import os
|
||||
import re
|
||||
Import("env")
|
||||
|
||||
def build_web():
|
||||
print("** Building web...")
|
||||
env.Execute(
|
||||
"node ./tools/webfilesbuilder/node_modules/gulp/bin/gulp.js --silent --cwd ./tools/webfilesbuilder")
|
||||
|
||||
def code_check(source, target, env):
|
||||
print("** Starting cppcheck...")
|
||||
call(["cppcheck", os.getcwd()+"/.", "--force", "--enable=all"])
|
||||
print("\n** Finished cppcheck...\n")
|
||||
print("\n** Starting cpplint...")
|
||||
call(["cpplint", "--extensions=ino,cpp,h", "--filter=-legal/copyright,-build/include,-whitespace",
|
||||
"--linelength=120", "--recursive", "src", "lib/myESP"])
|
||||
print("\n** Finished cpplint...")
|
||||
|
||||
|
||||
# build web files
|
||||
build_web()
|
||||
|
||||
# extract application details
|
||||
bag = {}
|
||||
exprs = [
|
||||
(re.compile(r'^#define APP_VERSION\s+"(\S+)"'), 'app_version'),
|
||||
(re.compile(r'^#define APP_NAME\s+"(\S+)"'), 'app_name'),
|
||||
(re.compile(r'^#define APP_HOSTNAME\s+"(\S+)"'), 'app_hostname')
|
||||
]
|
||||
with open('./src/version.h', 'r') as f:
|
||||
for l in f.readlines():
|
||||
for expr, var in exprs:
|
||||
m = expr.match(l)
|
||||
if m and len(m.groups()) > 0:
|
||||
bag[var] = m.group(1)
|
||||
|
||||
with open('./src/ems-esp.cpp', 'r') as f:
|
||||
for l in f.readlines():
|
||||
for expr, var in exprs:
|
||||
m = expr.match(l)
|
||||
if m and len(m.groups()) > 0:
|
||||
bag[var] = m.group(1)
|
||||
|
||||
app_version = bag.get('app_version')
|
||||
app_name = bag.get('app_name')
|
||||
app_hostname = bag.get('app_hostname')
|
||||
board = env['BOARD']
|
||||
branch = env['PIOENV']
|
||||
|
||||
# build filename, replacing . with _ for the version
|
||||
env.Replace(PROGNAME=app_name + "-" +
|
||||
app_version.replace(".", "_") + "-" + board)
|
||||
@@ -1,34 +0,0 @@
|
||||
>>>stack>>>
|
||||
3ffffdc0: 3fff472c 00000001 3ffec811 40224020
|
||||
3ffffdd0: 3fff472c 00000019 3fff8acd 0000001b
|
||||
3ffffde0: 3fff4a5c 0000072a 0000072a 402113c8
|
||||
3ffffdf0: 3ffec4b5 3fff44d8 3fff8ab4 402117f0
|
||||
3ffffe00: 3fff8ab4 3fff44d8 3fff472c 4022d20a
|
||||
3ffffe10: 00000000 40209737 3fff472c 4022d1f8
|
||||
3ffffe20: 3ffec4b5 3fff44d8 3fff8ab4 00000003
|
||||
3ffffe30: 00000002 3fff4884 3fff44d8 40246fb8
|
||||
3ffffe40: 3fff8ca0 ff000000 00004000 4020fb00
|
||||
3ffffe50: 3fff8ca0 ff000000 3fff44d8 4024f9d5
|
||||
3ffffe60: 4020d0d0 3fff4884 3fff44d8 00000003
|
||||
3ffffe70: 00000002 3fff4884 3fff44d8 4020aa5c
|
||||
3ffffe80: 45455b20 4d4f5250 4545205d 4d4f5250
|
||||
3ffffe90: 63655320 20726f74 6c6f6f70 7a697320
|
||||
3ffffea0: 73692065 202c3420 20646e61 75206e69
|
||||
3ffffeb0: 61206573 203a6572 39313031 31303120
|
||||
3ffffec0: 30312038 31203731 20363130 00000000
|
||||
3ffffed0: 6f626552 6120746f 72657466 63757320
|
||||
3ffffee0: 73736563 206c7566 2041544f 61647075
|
||||
3ffffef0: 36006574 00000000 00000000 00000000
|
||||
3fffff00: 00000000 00000007 3ffe8304 40227977
|
||||
3fffff10: 0000000d 00000001 3fff44d8 3fff47b0
|
||||
3fffff20: 3fff8e64 00000001 3fff44d8 4020c9e1
|
||||
3fffff30: 0000006d 3fff4740 0000000d 4021813e
|
||||
3fffff40: 0000006d 3fff4740 3fff472c 3fff47b0
|
||||
3fffff50: 0000000d 3fff472c 3fff44d8 4020cfbd
|
||||
3fffff60: 0000000d 3fff4a1c 3ffe97b8 3fff49c0
|
||||
3fffff70: 3fff44d8 3fff2adc 3fff44c8 3fff49c0
|
||||
3fffff80: 3fffdad0 3fff2adc 3fff44d8 4020d090
|
||||
3fffff90: 3fffdad0 00000000 3fff2adc 40205cbc
|
||||
3fffffa0: 3fffdad0 00000000 3fff4990 4020f088
|
||||
3fffffb0: feefeffe feefeffe 3ffe97b8 401006f1
|
||||
<<<stack<<<
|
||||
Reference in New Issue
Block a user