Wemos D1 mini (ESP8266) watchdog keeps resetting

Hi

I'm trying to multiplex a 24x7 LED matrix with an Wemos D1 mini (ESP8266).

I am using a 555 timer at 280Hz that triggers an interrupt to take care of the multiplexing. Much lower than that, and the persistence of vision illusion starts to break down.

I'm using 1 MIC5891 (7 rows) to source current and 3 HC595's (24 columns) to sink it. Data is transferred via SPI at 500kHz - any higher and data corruption becomes a real issue.

Power source is a standard USB 3.0 port (should allow up to 900 mA).

I'm using a TXS0108E to convert the 3.3V outputs of the ESP8266 to 5V for the shift registers.

The LED's have a max forward voltage of 3.2V. I'm driving them at 5V through 470 ohm resistors.

I'm using WiFiServer to transfer data via TCP. I doubt it's the issue though, because I get resets even without sending/receiving anything.

All IC's (D1 mini, 555, MIC5891, HC595's) have a 0.1 uF ceramic cap across VCC and GND. The TXS0108E I got off Amazon already had decoupling caps on-board. I also added a 470uF electrolytic across 5V and GND.

Behavior of the resets is erratic. Sometimes I get 3 in a row right after starting up the D1, sometimes it runs fine for 30 minutes. Average is probably 10-15 minutes before resetting.

Debug info looks like this:

15:15:30.228 ->  ets Jan  8 2013,rst cause:4, boot mode:(3,6)
15:15:30.228 -> 
15:15:30.228 -> wdt reset
15:15:30.228 -> load 0x4010f000, len 1384, room 16 
15:15:30.228 -> tail 8
15:15:30.228 -> chksum 0x2d
15:15:30.228 -> csum 0x2d
15:15:30.262 -> v8b899c12
15:15:30.262 -> ~ld

Just tested and I'm getting resets even with loop() empty and not using WiFiServer at all, so I can confirm that's not it.

Even just this:

#include <SPI.h>
#include <ESP8266WiFi.h>

//test pattern: "█░"
#define ROW_CNT 7
#define BYTE_CNT 3
uint8_t FRAME_BYTES_DEFAULT[] = {254, 15, 224, 254, 15, 224, 254, 15, 224, 254, 15, 224, 254, 15, 224, 254, 15, 224, 254, 15, 224, 127, 7, 240, 127, 7, 240, 127, 7, 240, 127, 7, 240, 127, 7, 240, 127, 7, 240, 127, 7, 240, 63, 131, 248, 63, 131, 248, 63, 131, 248, 63, 131, 248, 63, 131, 248, 63, 131, 248, 63, 131, 248, 31, 193, 252, 31, 193, 252, 31, 193, 252, 31, 193, 252, 31, 193, 252, 31, 193, 252, 31, 193, 252, 15, 224, 254, 15, 224, 254, 15, 224, 254, 15, 224, 254, 15, 224, 254, 15, 224, 254, 15, 224, 254, 7, 240, 127, 7, 240, 127, 7, 240, 127, 7, 240, 127, 7, 240, 127, 7, 240, 127, 7, 240, 127, 131, 248, 63, 131, 248, 63, 131, 248, 63, 131, 248, 63, 131, 248, 63, 131, 248, 63, 131, 248, 63, 193, 252, 31, 193, 252, 31, 193, 252, 31, 193, 252, 31, 193, 252, 31, 193, 252, 31, 193, 252, 31, 224, 254, 15, 224, 254, 15, 224, 254, 15, 224, 254, 15, 224, 254, 15, 224, 254, 15, 224, 254, 15, 240, 127, 7, 240, 127, 7, 240, 127, 7, 240, 127, 7, 240, 127, 7, 240, 127, 7, 240, 127, 7, 248, 63, 131, 248, 63, 131, 248, 63, 131, 248, 63, 131, 248, 63, 131, 248, 63, 131, 248, 63, 131, 252, 31, 193, 252, 31, 193, 252, 31, 193, 252, 31, 193, 252, 31, 193, 252, 31, 193, 252, 31, 193};
uint8_t* frame_bytes = FRAME_BYTES_DEFAULT;
uint16_t frame_cnt = sizeof(FRAME_BYTES_DEFAULT) / (ROW_CNT*BYTE_CNT);
#define LED_COL_STATES(frame, row, byte) (frame_bytes[frame*(ROW_CNT*BYTE_CNT) + row*BYTE_CNT + byte])

#define P_TIMER D1

#define P_ENABLE D6
#define P_CLOCK D5
#define P_DATA D7
#define P_LATCH D2


#define ANL_OUT_MAX 1023 // for Wemos D1 mini

inline uint16_t f2anl(float val){return round(val * ANL_OUT_MAX);}
inline uint16_t anl_inv(uint16_t val){return ANL_OUT_MAX - val;}

void sleep(uint32_t ms){
  uint32_t ts_start = micros();
  while(micros()-ts_start < ms*1000){
    delayMicroseconds(10);
    yield();
  }
}

uint8_t row = 0;
uint16_t frame = 0;
uint8_t frame_ctr = 0;
uint8_t frame_interval = 50;
uint16_t brightness = anl_inv(f2anl(0.5));


ICACHE_RAM_ATTR void refresh(){
  // connections: rows>cols[0-7]>cols[8-15]>cols[16-23]
  
  digitalWrite(P_ENABLE, HIGH);
  SPI.transfer(LED_COL_STATES(frame, row, 0));
  SPI.transfer(LED_COL_STATES(frame, row, 1));
  SPI.transfer(LED_COL_STATES(frame, row, 2));
  SPI.transfer(0b00000001 << row);
  digitalWrite(P_LATCH, HIGH);
  digitalWrite(P_LATCH, LOW);
  analogWrite(P_ENABLE, brightness);

  if(++row == ROW_CNT) row = 0;
  if(++frame_ctr == frame_interval){
    frame_ctr = 0;
    if(++frame == frame_cnt) frame = 0;
  }
}

void setup(){
  pinMode(P_LATCH, OUTPUT); pinMode(P_ENABLE, OUTPUT);
  pinMode(P_TIMER, INPUT);
  digitalWrite(P_LATCH, LOW);
  SPI.begin();
  SPI.beginTransaction(SPISettings(500000, MSBFIRST, SPI_MODE0)); // 500kHz;   MSBFIRST -> start w/ bit 8;    SPI_MODE0 -> CPOL=0, CPHA=0
  SPI.endTransaction(); // does not revert settings applied in beginTransaction
  
  analogWriteFreq(40000); //40kHz
  WiFi.begin("id", "pass");
  while(WiFi.status() != WL_CONNECTED) sleep(500);
  attachInterrupt(digitalPinToInterrupt(P_TIMER), refresh, RISING);
}

void loop(){
}

is causing resets:

15:46:22.369 ->  ets Jan  8 2013,rst cause:2, boot mode:(3,6)
15:46:22.403 -> 
15:46:22.403 -> load 0x4010f000, len 1384, room 16 
15:46:22.403 -> tail 8
15:46:22.403 -> chksum 0x2d
15:46:22.403 -> csum 0x2d
15:46:22.403 -> v8b899c12
15:46:22.403 -> ~ld
15:47:09.263 -> 
15:47:09.263 ->  ets Jan  8 2013,rst cause:4, boot mode:(3,6)
15:47:09.263 -> 
15:47:09.263 -> wdt reset
15:47:09.298 -> load 0x4010f000, len 1384, room 16 
15:47:09.298 -> tail 8
15:47:09.298 -> chksum 0x2d
15:47:09.298 -> csum 0x2d
15:47:09.298 -> v8b899c12
15:47:09.298 -> ~ld

Update:

I put ESP.wdtFeed() at the top of refresh() and called ESP.wdtDisable() in setup. Didn't fix it.