Last active
June 10, 2021 11:21
-
-
Save BBBSnowball/6a13c51fc4b89f0b65f18fe7342bc350 to your computer and use it in GitHub Desktop.
serial monitor for ESP8266, run "pio run -t upload" on ctrl-t ctrl-u, decode stacktraces, paths are hardcoded for platformio with nodemcuv2 board
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import serial | |
from serial.tools import miniterm | |
import subprocess, sys, re, os | |
class ESPMiniterm(miniterm.Miniterm): | |
def handle_menu_key(self, c): | |
if c == '\x15': # ctrl-u | |
self.upload_file() | |
else: | |
super(ESPMiniterm, self).handle_menu_key(c) | |
# This will be called for menukey + ctrl-u. | |
def upload_file(self): | |
self._stop_reader() | |
self.serial.close() | |
while True: | |
code = subprocess.call(["pio","run","-t","upload"]) | |
if code == 0: | |
break | |
print "returncode: %d, press ctrl+u to try again, any other key to continue" % code | |
try: | |
c = self.console.getkey() | |
if c == self.menu_character: | |
c = self.console.getkey() | |
except KeyboardInterrupt: | |
c = '\x03' | |
if c != '\x15': | |
break | |
self.serial.open() | |
self._start_reader() | |
class ESPStackDecoder(miniterm.Transform): | |
# https://github.com/esp8266/Arduino/blob/283eb97cd3f6dac691e8350a612021f1e1ccd8e3/doc/exception_causes.rst | |
EXCEPTION_CAUSES = { | |
0: "IllegalInstructionCause", | |
1: "SyscallCause", | |
2: "InstructionFetchErrorCause", | |
3: "LoadStoreErrorCause", | |
4: "Level1InterruptCause", | |
5: "AllocaCause", | |
6: "IntegerDivideByZeroCause", | |
7: "Reserved for Tensilica", | |
8: "PrivilegedCause", | |
9: "LoadStoreAlignmentCause", | |
10: "Reserved for Tensilica", | |
11: "Reserved for Tensilica", | |
12: "InstrPIFDataErrorCause", | |
13: "LoadStorePIFDataErrorCause", | |
14: "InstrPIFAddrErrorCause", | |
15: "LoadStorePIFAddrErrorCause", | |
16: "InstTLBMissCause", | |
17: "InstTLBMultiHitCause", | |
18: "InstFetchPrivilegeCause", | |
19: "Reserved for Tensilica", | |
20: "InstFetchProhibitedCause", | |
21: "Reserved for Tensilica", | |
22: "Reserved for Tensilica", | |
23: "Reserved for Tensilica", | |
24: "LoadStoreTLBMissCause", | |
25: "LoadStoreTLBMultiHitCause", | |
26: "LoadStorePrivilegeCause", | |
27: "Reserved for Tensilica", | |
28: "LoadProhibitedCause", | |
29: "StoreProhibitedCause", | |
30: "Reserved for Tensilica", | |
31: "Reserved for Tensilica", | |
32: "CoprocessornDisabled", | |
33: "CoprocessornDisabled", | |
34: "CoprocessornDisabled", | |
35: "CoprocessornDisabled", | |
36: "CoprocessornDisabled", | |
37: "CoprocessornDisabled", | |
38: "CoprocessornDisabled", | |
39: "CoprocessornDisabled", | |
} | |
def __init__(self): | |
miniterm.Transform.__init__(self) | |
self.rxbuf = "" | |
self.in_stack = False | |
def rx(self, text): | |
self.rxbuf += text | |
if '\r' in self.rxbuf or '\n' in self.rxbuf: | |
#lines = re.split("[\\r\\n]+", self.rxbuf) | |
#self.rxbuf = lines[-1] | |
#lines = lines[0:-1] | |
#for line in lines: | |
# info = self.handle_line(line) | |
# if info and info != "": | |
# #TODO insert at the right position | |
# text += info | |
prev = 0 | |
infos = [] | |
for m in re.finditer("(\\r\\n?|\\n)[\\r\\n]*", self.rxbuf): | |
info = self.handle_line(self.rxbuf[prev:m.start()]) | |
if info: | |
infos.append([m.end(1) - (len(self.rxbuf)-len(text)), | |
re.sub("\\r\\n?|\\n", "\r\n", info)]) | |
prev = m.end() | |
infos.reverse() | |
for info in infos: | |
p = max(0, info[0]) | |
text = text[0:p] + info[1] + text[p:] | |
self.rxbuf = self.rxbuf[prev:] | |
return text | |
def handle_line(self, line): | |
m = re.search("Exception \\(([0-9]*)\\):\s*$", line) | |
if m: | |
if int(m.group(1)) in self.EXCEPTION_CAUSES: | |
return " -> Exception cause: %s\n" % (self.EXCEPTION_CAUSES[int(m.group(1))],) | |
else: | |
return " -> Exception cause: ???\n" | |
m = re.match("^epc1=0x([0-9a-fA-F]{8,8}) ", line) | |
if m: | |
#return "address: %s\n" % (m.group(1),) | |
return self.addr2line([m.group(1)]) | |
if line == ">>>stack>>>": | |
self.in_stack = True | |
elif line == "<<<stack<<<" or ":" not in line: | |
self.in_stack = False | |
if self.in_stack: | |
info = "" | |
addrs = [] | |
for m in re.finditer(" (40[0-2][0-9a-fA-F]{5,5})\\b", line): | |
#info += "address: %s\n" % (m.group(1),) | |
addrs.append("0x" + m.group(1)) | |
return self.addr2line(addrs) | |
def addr2line(self, addrs): | |
if len(addrs) == 0: | |
return "" | |
output = subprocess.check_output([os.environ["HOME"] + "/.platformio/packages/toolchain-xtensa/bin/xtensa-lx106-elf-addr2line", "-aipfC", "-e", ".pioenvs/nodemcuv2/firmware.elf"] + addrs) | |
return output | |
miniterm.TRANSFORMATIONS['espstackdecoder'] = ESPStackDecoder | |
#miniterm.main(default_port="/dev/ttyUSB0", default_baudrate=115200) | |
serial_instance = serial.serial_for_url( | |
"/dev/ttyUSB0", | |
115200, | |
do_not_open = True) | |
if not hasattr(serial_instance, 'cancel_read'): | |
# enable timeout for alive flag polling if cancel_read is not available | |
serial_instance.timeout = 1 | |
serial_instance.open() | |
miniterm = ESPMiniterm( | |
serial_instance, | |
echo=False, | |
eol='crlf', | |
filters=["espstackdecoder"]) | |
miniterm.exit_character = unichr(0x03) #unichr(0x1d) | |
miniterm.menu_character = unichr(0x14) | |
miniterm.raw = False | |
miniterm.set_rx_encoding('UTF-8') | |
miniterm.set_tx_encoding('UTF-8') | |
miniterm.start() | |
try: | |
miniterm.join(True) | |
except KeyboardInterrupt: | |
pass | |
sys.stderr.write("\n--- exit ---\n") | |
miniterm.join() | |
miniterm.close() |
how to use is not clear...
Open a Terminal on project directory (where .platformio exists) and run "python PATHTO/espminiterm.py". This will start to output the Serial console. And when an Exception occurs, this just prints the Stack Trace.
Maybe you have to adapt the line 130. I'm using:
output = subprocess.check_output([os.environ["HOME"] + "/.platformio/packages/toolchain-xtensa/bin/xtensa-lx106-elf-addr2line", "-aipfC", "-e", ".pio/build/nodemcuv2/firmware.elf"] + addrs)
is this for python2?
Now in new versions of Platformio, you have this already built-in. Check in this page https://docs.platformio.org/core/userguide/device/cmd_monitor.html. It's esp8266_exception_decoder filter
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi, I'm not sure how to "embed" the script in pio env. I'm working mostly on shell. in which command do you reference espminiterm.py and how?