Skip to content

Commit f0f2044

Browse files
committed
fixed subtle stack overflow issue that showed up as interrupt WDT idf panic or maximum recursion depth exceeded runtimerror, see Single.py for details
1 parent c3fdeb4 commit f0f2044

File tree

7 files changed

+59
-19
lines changed

7 files changed

+59
-19
lines changed

boot.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
#webrepl.start()
66
import micropython, machine, esp
77
micropython.alloc_emergency_exception_buf(100)
8-
micropython.opt_level(0)
8+
micropython.opt_level(3)
99
esp.osdebug(None)
1010

1111
#if machine.reset_cause() == machine.SOFT_RESET: #soft resets break i2c, until that's specifically taken care of when can just do that.

main.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ def handle_exception(loop, context):
2222
Single.Kernel = Kernel() # Constructor might create tasks
2323
#Single.Kernel.kernel_main_thread()
2424
import _thread
25+
_thread.stack_size(Single.MP_THREAD_STACK_SIZE)
2526
_thread.start_new_thread(Single.Kernel.kernel_main_thread, ())
2627
#_thread.exit() # letting repl run seems to crash things when disconnecting USB?
2728
#my_class.run_forever() # Non-terminating method

micropython/LILYGO_TTGO_TWATCH/sdkconfig.board

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ PTHREAD_DEFAULT_CORE_NO_AFFINITY=y
55
CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y
66
CONFIG_PARTITION_TABLE_CUSTOM=y
77
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv"
8+
CONFIG_ESP_INT_WDT_TIMEOUT_MS=1200
89

micropython/LILYGO_TTGO_TWATCH_S3/sdkconfig.board

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ PTHREAD_DEFAULT_CORE_NO_AFFINITY=y
1111
CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y
1212
CONFIG_PARTITION_TABLE_CUSTOM=y
1313
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv"
14+
CONFIG_ESP_INT_WDT_TIMEOUT_MS=1200
1415

system/Hardware.py

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import machine, micropython, json, time, _thread, network, os
77
import Logger
8-
import st7789, axp202, axp202_constants, ft6x36, pcf8563, bma423, adafruit_drv2605
8+
import st7789, axp202, axp202_constants, ft6x36, pcf8563, bma423, adafruit_drv2605, AXP2101
99
import TextMode
1010
import Events
1111
import esp, esp32
@@ -64,7 +64,7 @@ def init_st7789(self):
6464
if self.WatchVersion == WATCHV2:
6565
cs = machine.Pin(5, machine.Pin.OUT)
6666
dc = machine.Pin(27, machine.Pin.OUT)
67-
display_spi = machine.SPI(1,baudrate=40000000,sck=machine.Pin(18, machine.Pin.OUT),mosi=machine.Pin(19, machine.Pin.OUT)) # will only work with modded MPY to add flag for dummy bit, otherwise use baudrate 27000000, ESP32 limit is 80Mhz
67+
display_spi = machine.SPI(1,baudrate=80000000,sck=machine.Pin(18, machine.Pin.OUT),mosi=machine.Pin(19, machine.Pin.OUT)) # will only work with modded MPY to add flag for dummy bit, otherwise use baudrate 27000000, ESP32 limit is 80Mhz
6868
self.display = st7789.ST7789(display_spi, Hardware.DISPLAY_WIDTH, Hardware.DISPLAY_HEIGHT, cs=cs, dc=dc, backlight=machine.Pin(25, machine.Pin.OUT), rotation=2, buffer_size=Hardware.DISPLAY_WIDTH*Hardware.DISPLAY_HEIGHT*2,)
6969
elif self.WatchVersion == WATCHS3:
7070
cs = machine.Pin(12, machine.Pin.OUT)
@@ -144,6 +144,7 @@ def init_irq(self):
144144
self.irq_touch_present = False
145145
#self.irq_touch_time = 0
146146
#self.irq_touch_fired_release = True
147+
self.irq_feedback_present = False
147148

148149
def init_irq_s3(self):
149150
pin16 = machine.Pin(16, machine.Pin.IN) #irq touch
@@ -157,6 +158,7 @@ def init_irq_s3(self):
157158
self.irq_touch_buffer_pos1 = bytearray(4) #pre-allocation
158159
self.irq_touch_buffer_pos2 = bytearray(4)
159160
self.irq_touch_present = False
161+
self.irq_feedback_present = False
160162

161163
def __init__(self):
162164
self.WatchVersion = 0 # V1
@@ -254,7 +256,10 @@ def __init__(self):
254256
self.vibrator = machine.Pin(4, machine.Pin.OUT)
255257
else:
256258
self.vibration_controller = adafruit_drv2605.DRV2605(sensor_i2c)
257-
self.vibration_controller.mode = adafruit_drv2605.MODE_REALTIME # i really cant be bothered lol drv looks like it has nice functions but i have other priorities rn
259+
self.vibration_controller._write_u8(0x01, 0b10000000) # reset
260+
time.sleep_ms(10)
261+
self.vibration_controller = adafruit_drv2605.DRV2605(sensor_i2c)
262+
self.vibration_controller.mode = adafruit_drv2605.MODE_INTTRIG # i really cant be bothered lol drv looks like it has nice functions but i have other priorities rn
258263
self.vibrator = None
259264

260265
machine.freq(80000000) #todo: set to user value (give a slider with choice between 240, 160, and 80 mhz?)
@@ -293,7 +298,10 @@ def lightsleep(self, time_ms, force = False, callback = None):
293298
self.display.sleep_mode(True)
294299
self.pmu.disablePower(axp202_constants.AXP202_LDO2)
295300
if self.WatchVersion == WATCHV2:
296-
self.pmu.disablePower(axp202_constants.AXP202_LDO3) # shutdown full LCD in case of V2 watch
301+
self.touch.power_mode = 3 # permannet sleep until reset
302+
# self.pmu.disablePower(axp202_constants.AXP202_LDO3) # shutdown full LCD in case of V2 watch
303+
if self.WatchVersion == WATCHV2 or self.WatchVersion == WATCHS3:
304+
self.vibration_controller._write_u8(0x01, 0b01000000) # standby
297305
self.pmu.disablePower(axp202_constants.AXP202_LDO4)
298306
self.pmu.disablePower(axp202_constants.AXP202_DCDC2)
299307
self.pmu.clearIRQ()
@@ -318,11 +326,16 @@ def lightsleep(self, time_ms, force = False, callback = None):
318326
self.pmu.setDC3Voltage(Hardware.Vc3V3)
319327
self.pmu.enablePower(axp202_constants.AXP202_LDO2)
320328
if self.WatchVersion == WATCHV2:
321-
self.pmu.setLDO3Voltage(3300)
322-
self.pmu.enablePower(axp202_constants.AXP202_LDO3)
323-
time.sleep_ms(20) # wait for potaaytoes to be warmed up
329+
self.pmu.disablePower(axp202_constants.AXP202_EXTEN); # reset touch
330+
time.sleep_ms(15)
331+
self.pmu.enablePower(axp202_constants.AXP202_EXTEN);
332+
# self.pmu.setLDO3Voltage(3300)
333+
# self.pmu.enablePower(axp202_constants.AXP202_LDO3)
334+
#time.sleep_ms(20) # wait for potaaytoes to be warmed up
324335
self.init_ft6336()
325-
self.init_st7789()
336+
#self.init_st7789()
337+
if self.WatchVersion == WATCHV2 or self.WatchVersion == WATCHS3:
338+
self.vibration_controller._write_u8(0x01, 0b00000000) #exit standby
326339
self.display.sleep_mode(False)
327340
self.display.on()
328341
machine.freq(80000000) # no more fast, todo: see other place where this is
@@ -333,25 +346,28 @@ def blit_buffer_rgb565(self, array):
333346
self.display.blit_buffer(array, 0, 0, Hardware.DISPLAY_WIDTH, Hardware.DISPLAY_HEIGHT) # O(1) for the whole render pipeline with that, but quite slow... but not much more than even a simple direct draw
334347
# seems like to get more speed would need to do quite a lot on the C side of things
335348

336-
def feedback1(self):
349+
def feedback1(self, _ = None):
337350
if self.vibrator:
338351
self.vibrator.on()
352+
machine.Timer(-1, mode=machine.Timer.ONE_SHOT, period=20, callback=self.feedback_frame)
339353
elif self.vibration_controller:
340-
self.vibration_controller.realtime_value = 127
341-
machine.Timer(3, mode=machine.Timer.ONE_SHOT, period=20, callback=self.feedback_frame)
354+
self.vibration_controller.sequence[0] = adafruit_drv2605.Effect(2)
355+
self.vibration_controller.play()
356+
357+
342358

343-
def feedback2(self):
359+
360+
def feedback2(self, _ = None):
344361
if self.vibrator:
345362
self.vibrator.on()
363+
machine.Timer(-1, mode=machine.Timer.ONE_SHOT, period=50, callback=self.feedback_frame)
346364
elif self.vibration_controller:
347-
self.vibration_controller.realtime_value = 127
348-
machine.Timer(3, mode=machine.Timer.ONE_SHOT, period=50, callback=self.feedback_frame)
365+
self.vibration_controller.sequence[0] = adafruit_drv2605.Effect(1)
366+
self.vibration_controller.play()
349367

350368
def feedback_frame(self, _):
351369
if self.vibrator:
352370
self.vibrator.off()
353-
elif self.vibration_controller:
354-
self.vibration_controller.realtime_value = 0
355371

356372
def sync_ntp(self):
357373
try:
@@ -459,6 +475,7 @@ def irq_touch_process(self, pin):
459475
Single.Kernel.event(Events.GestureEvent(3))
460476

461477
Single.Kernel.event(Events.ReleaseEvent(float(x) / float(Hardware.DISPLAY_WIDTH), float(y) / float(Hardware.DISPLAY_HEIGHT)))
478+
#micropython.schedule(self.feedback1(), "bruh") # why? idk. It's giving recursion errors where it shouldnt
462479
self.feedback1()
463480

464481
def fucky_wucky(self, e): # try to print exception to display

system/Kernel.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ def process_events(self):
7474
def runProgram(self, name, arg):
7575
if __debug__:
7676
print("in runProgram for ", name)
77+
_thread.stack_size(Single.MP_THREAD_STACK_SIZE) # do that before EVERY new thread, see Single.py for explanation
7778
_thread.start_new_thread(self.runProgram2, (name, arg))
7879

7980

@@ -173,8 +174,8 @@ def kernel_main_thread(self): # thread 0
173174
try:
174175
Logger.addOutput(print)
175176
Logger.log("Welcome to WPOS2")
176-
_thread.stack_size(64*1024)
177-
Logger.log("Thread stack size is: " + str(_thread.stack_size()))
177+
_thread.stack_size(Single.MP_THREAD_STACK_SIZE) # does not actually sets stack size
178+
Logger.log("Thread stack size is: " + str(_thread.stack_size())) # because this sets it back to 4K
178179
Logger.process()
179180
Logger.log("Thread " + str(_thread.get_ident()) + " is Kernel Thread")
180181
Logger.log("with stack size: " + str(_thread.stack_size()))
@@ -188,6 +189,7 @@ def kernel_main_thread(self): # thread 0
188189
Single.Hardware = self.hardware
189190
self.framebuffer_array = bytearray(240 * 240 * 2) # 2 byte per pixel)
190191
self.framebuffer = oframebuf.WPFrameBuffer(self.framebuffer_array, 240, 240, framebuf.RGB565)
192+
_thread.stack_size(Single.MP_THREAD_STACK_SIZE)
191193
_thread.start_new_thread(self.render_thread, ())
192194
self._lock.release()
193195
self.event(Events.RunEvent("home"))

system/Single.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,21 @@
1515
DEFAULT_TEXT_RATIO = 30 #characters per line, 30 for 240x240 screen with 8 pixel large font
1616
DEFAULT_TEXT_RATIO_INV = 1.0/DEFAULT_TEXT_RATIO #expensive maths
1717
DEFAULT_TEXT_RATIO_INV_2 = DEFAULT_TEXT_RATIO_INV/2.0
18+
19+
MP_THREAD_STACK_SIZE = 16 * 1024 # bruh
20+
21+
'''
22+
it refuses to do more than 64*1024, but it's only 64 kb and we still hit bs recursion errors in non-recursive code...
23+
24+
Turns out it was really dumb:
25+
https://github.com/orgs/micropython/discussions/10614
26+
"The impression that _thread.stack_size() would not work came from the behavior that calling it without arguments reports the previously set value but at the same time sets the stack size silently back to 4K.
27+
So print('New Stack Size=', _thread.stack_size()) prints nicely the previously set value but makes this setting invalid.
28+
Taking that into account, the place where e.g. _thread.stack_size(8*1024) is called seems to be not critical anymore."
29+
30+
Comments say this is *intended*
31+
32+
33+
Fixing that in my code immediatly caused wifi driver to run out of memory ('unknown error 0x0101') so it's working
34+
35+
Also I was testing on the V2, which has 4MB of ram instead of 8MB and made the error easy to hit'''

0 commit comments

Comments
 (0)