Page 1 of 1

Lock up problems when sending long data streams over WiFi

Posted: Sun Jul 07, 2019 2:55 am
by emulationisfun
Hi, I'm having an issue where my code stops responding when I'm sending long streams of data. I'm brand new to ESP32, just got a Heltec Wifi Kit 32 today. I have lots of Arduino and Teensy experience, so not new to this type of thing in general.

Specifically, I wrote an 8086 PC emulator for this, and I have a VNC/RFB server in the code to allow a client to connect and use the emulator. This worked great on an Arduino Due with an Ethernet shield. I'm using the same RFB server code, just slightly changed to use the WiFiServer/WiFiClient instead. Are there differences between these APIs that I need to look out for? They look almost identical.

It actually has no problem with the initial VNC connection and first full screen update, but then it starts going haywire during incremental updates after that. Again, everything worked great on the Ethernet shield. It was very stable. Could it be a memory issue? Something about how the ESP32 is handling the transmit packet buffering? Although I seem to have 100+ KB free.

This is the VNC/RFB code. rfbInit() gets called once at startup, and rfbRun() is called frequently from the main loop. Thanks to anybody that can help. I'm going to be working at this some more tonight, but maybe somebody here will see some obvious mistake that I'm missing.

Code: Select all

#include <WiFi.h>
#include "emu.h"
#include "font.h"

#define RFB_STATE_WAIT_VERSION        0
#define RFB_STATE_WAIT_SECURITY       1
#define RFB_STATE_WAIT_SECURITY_2     2
#define RFB_STATE_WAIT_CLIENT_INIT    3
#define RFB_STATE_OPERATIONAL         4
#define RFB_STATE_IDLE                5

#define WIDTH   640
#define HEIGHT  400

#define RFB_NAME  "Fake86 for ESP32"

#define RFB_OUT_BUFFER_SIZE   128

WiFiServer wfserver;
WiFiClient wfclient;

uint8_t clientMsgLen[7] = { 20, 1, 0, 10, 8, 6, 0 };
uint8_t rfbState = RFB_STATE_IDLE, rfbLastPin = HIGH;
uint8_t rfbBuf[256], palettecga[16];
uint8_t rfbOutBuf[RFB_OUT_BUFFER_SIZE];
uint32_t rfbLastMS = 0, rfbCurMS = 0, rfbPinMS = 0, rfbOutPos = 0;
uint16_t rfbKnownRects = 0;
uint32_t rfbWrites[64];
uint8_t rfbWriteCount = 0;
uint8_t xcurs, ycurs;
void rfbParseIncoming();

void serverRun() {
  WiFiClient newClient = wfserver.accept();
  if (newClient) {
    if (wfclient) wfclient.stop();
    wfclient = newClient;
    //wfclient.setNoDelay(true);
    wfclient.print("RFB 003.008\n");
    rfbState = RFB_STATE_WAIT_VERSION;
    rfbWriteCount = 0;
  }
}

void rfbDiscardRx() {
  while(wfclient.available()) {
    wfclient.read();
  }
}

void rfbFlush() {
  if (rfbOutPos == 0) return;
  wfclient.write(rfbOutBuf, rfbOutPos);
  rfbOutPos = 0;
}

void rfbOutByte(uint8_t value) {
  if (rfbOutPos == RFB_OUT_BUFFER_SIZE) rfbFlush();
  rfbOutBuf[rfbOutPos++] = value;
}

void rfbOutString(char *str) {
  uint16_t i = 0;
  uint8_t curchar;
  while (curchar = str[i++]) { //asignment, not comparison. abort loop upon finding null terminator.
    rfbOutByte(curchar);
  }
}

void rfbSend32(uint32_t value) {
  rfbOutByte((uint8_t)(value >> 24));
  rfbOutByte((uint8_t)(value >> 16));
  rfbOutByte((uint8_t)(value >> 8));
  rfbOutByte((uint8_t)(value & 255));
}

void rfbSend16(uint16_t value) {
  rfbOutByte((uint8_t)(value >> 8));
  rfbOutByte((uint8_t)(value & 255));
}

void rfbSend8(uint8_t value) {
  rfbOutByte(value);
}

uint32_t rfbRead32() {
  uint32_t ret;
  ret = (uint32_t)wfclient.read() << 24;
  ret |= (uint32_t)wfclient.read() << 16;
  ret |= (uint32_t)wfclient.read() << 8;
  ret |= (uint32_t)wfclient.read();
  return ret;
}

uint16_t rfbRead16() {
  uint16_t ret;
  ret |= (uint32_t)wfclient.read() << 8;
  ret |= (uint32_t)wfclient.read();
  return ret;
}

uint8_t rfbRead8() {
  return (uint8_t)wfclient.read();
}

uint8_t normalize(uint8_t value) {
  switch (value) {
    case 0: case 9: case 10: case 13: case 26: case 255: //all of these are blank on screen
      value = 32;
      break;
  }
  return value;
}

void rfbDrawChar80(uint16_t xpos, uint16_t ypos, uint16_t w, uint16_t h) {
  uint16_t x, y, xchar, ychar, cursstart, cursend;
  uint8_t curpixel, curchar, attr;
  uint32_t addr32;
  cursstart = (crt_controller[0x0A] & 31) * 2;
  cursend = (crt_controller[0x0B] & 31) * 2;
  rfbSend16(xpos);
  rfbSend16(ypos);
  rfbSend16(w);
  rfbSend16(h);
  rfbSend32(0); //raw
  for (y=ypos; y<(ypos+h); y++) {
    for (x=xpos; x<(xpos+w); x++) {
      ychar = y / 16;
      xchar = x / 8;
      addr32 = ychar*160 + xchar*2;
      curchar = VRAM_read(addr32);
      attr = VRAM_read(addr32+1);
      curpixel = (ROM_READ(font, curchar*8+((y&15)>>1))>>(x&7))&1;
      curpixel = curpixel ? attr & 15 : attr >> 4;
      if (((x>>3) == xcurs) && ((y>>4) == ycurs)) {
        if (((y & 15) >= cursstart) && ((y & 15) <= cursend)) {
          curpixel = attr & 15;
        }
      }
      rfbSend8(palettecga[curpixel]);
    }
  }
}

void rfbChar80(uint32_t addr32) {
  uint16_t xchar, ychar, curchar, attr, xpos, ypos, xcopy, ycopy, i;
  uint8_t doCopyRect = 0;
  
  if (rfbState != RFB_STATE_OPERATIONAL) return;
  if (addr32 >= 4000) return;
  
  addr32 &= 0xFFFFE;
  ychar = addr32 / 160;
  xchar = (addr32 % 160) >> 1;
  ypos = ychar * 16;
  xpos = xchar * 8;
  curchar = normalize(VRAM_read(addr32));
  attr = VRAM_read(addr32+1);
  
  rfbSend8(0);
  rfbSend8(0);
  rfbSend16(1);
  if (doCopyRect == 0) {
    rfbDrawChar80(xpos, ypos, 8, 16);
  } else {
    rfbSend16(xpos);
    rfbSend16(ypos);
    rfbSend16(8);
    rfbSend16(16);
    rfbSend32(1); //copy rect
    rfbSend16(xcopy);
    rfbSend16(ycopy);
  }
}

void rfbDrawChar40(uint16_t xpos, uint16_t ypos, uint16_t w, uint16_t h) {
  uint16_t x, y, xchar, ychar, cursstart, cursend;
  uint8_t curpixel, curchar, attr;
  uint32_t addr32;
  cursstart = (crt_controller[0x0A] & 31) * 2;
  cursend = (crt_controller[0x0B] & 31) * 2;
  rfbSend16(xpos);
  rfbSend16(ypos);
  rfbSend16(w);
  rfbSend16(h);
  rfbSend32(0); //raw
  for (y=ypos; y<(ypos+h); y++) {
    for (x=xpos; x<(xpos+w); x++) {
      ychar = y / 16;
      xchar = x / 16;
      addr32 = ychar*80 + xchar*2;
      curchar = VRAM_read(addr32);
      attr = VRAM_read(addr32+1);
      curpixel = (ROM_READ(font, curchar*8+((y&15)>>1))>>((x>>1)&7))&1;
      curpixel = curpixel ? attr & 15 : attr >> 4;
      if (((x>>3) == xcurs) && ((y>>4) == ycurs)) {
        if (((y & 15) >= cursstart) && ((y & 15) <= cursend)) {
          curpixel = attr & 15;
        }
      }
      rfbSend8(palettecga[curpixel]);
    }
  }
}

void rfbChar40(uint32_t addr32) {
  uint16_t xchar, ychar, curchar, attr, xpos, ypos, xcopy, ycopy, i;
  uint8_t doCopyRect = 0;
  
  if (rfbState != RFB_STATE_OPERATIONAL) return;
  if (addr32 >= 4000) return;
  
  addr32 &= 0xFFFFE;
  ychar = addr32 / 80;
  xchar = (addr32 % 80) >> 1;
  ypos = ychar * 16;
  xpos = xchar * 16;
  curchar = normalize(VRAM_read(addr32));
  attr = VRAM_read(addr32+1);

  rfbSend8(0);
  rfbSend8(0);
  rfbSend16(1);
  if (doCopyRect == 0) {
    rfbDrawChar40(xpos, ypos, 16, 16);
  } else {
    rfbSend16(xpos);
    rfbSend16(ypos);
    rfbSend16(16);
    rfbSend16(16);
    rfbSend32(1); //copy rect
    rfbSend16(xcopy);
    rfbSend16(ycopy);
  }
  //rfbFlush();
}

void rfbDrawGfxLo(uint16_t xpos, uint16_t ypos, uint16_t w, uint16_t h) {
  uint16_t x, y, xchar, ychar;
  uint8_t curpixel, curchar, attr, usepal, intensity;
  uint32_t addr32;
  usepal = (portram[0x3D9]>>5) & 1;
  intensity = ((portram[0x3D9]>>4) & 1) << 3;
  rfbSend16(xpos);
  rfbSend16(ypos);
  rfbSend16(w);
  rfbSend16(h);
  rfbSend32(0); //raw
  for (y=ypos; y<(ypos+h); y++) {
    for (x=xpos; x<(xpos+w); x++) {
      ychar = y >> 1;
      xchar = x >> 1;
      addr32 = (ychar & 1) * 8192 + (ychar >> 1) * 80 + (xchar >> 2);
      curchar = VRAM_read(addr32);
      switch (xchar & 3) {
        case 3: curpixel = curchar & 3; break;
        case 2: curpixel = (curchar >> 2) & 3; break;
        case 1: curpixel = (curchar >> 4) & 3; break;
        case 0: curpixel = (curchar >> 6) & 3; break;
      }
      curpixel = (curpixel << 1) + usepal + intensity;
      if (curpixel == (usepal + intensity)) curpixel = 0;
      rfbSend8(palettecga[curpixel]);
    }
  }
}

void rfbGfxLo(uint32_t addr32) {
  uint16_t xchar, ychar, curchar, attr, xpos, ypos, xcopy, ycopy, i;
  uint8_t doCopyRect = 0;
  
  if (rfbState != RFB_STATE_OPERATIONAL) return;
  if ((addr32 >= 8000) && (addr32 < 8192)) return;
  if (addr32 >= 16192) return;
  
  if (addr32 >= 8192) {
    addr32 -= 8192;
    ypos = ((addr32 / 80) << 2) + 2;
    xpos = (addr32 % 80) << 3;
  } else {
    ypos = (addr32 / 80) << 2;
    xpos = (addr32 % 80) << 3;
  }
  
  rfbSend8(0);
  rfbSend8(0);
  rfbSend16(1);
  rfbDrawGfxLo(xpos, ypos, 8, 2);
}

void rfbDrawGfxHi(uint16_t xpos, uint16_t ypos, uint16_t w, uint16_t h) {
  uint16_t x, y, xchar, ychar;
  uint8_t curpixel, curchar;
  uint32_t addr32;
  rfbSend16(xpos);
  rfbSend16(ypos);
  rfbSend16(w);
  rfbSend16(h);
  rfbSend32(0); //raw
  for (y=ypos; y<(ypos+h); y++) {
    for (x=xpos; x<(xpos+w); x++) {
      ychar = y >> 1;
      xchar = x;
      addr32 = (ychar & 1) * 8192 + (ychar >> 1) * 80 + (xchar >> 3);
      curchar = VRAM_read(addr32);
      curpixel = ((curchar >> (7-(x&7))) & 1) ? 15 : 0;
      rfbSend8(palettecga[curpixel]);
    }
  }
}

void rfbGfxHi(uint32_t addr32) {
  uint16_t xchar, ychar, curchar, attr, xpos, ypos, xcopy, ycopy, i;
  uint8_t doCopyRect = 0;
  
  if (rfbState != RFB_STATE_OPERATIONAL) return;
  if ((addr32 >= 8000) && (addr32 < 8192)) return;
  if (addr32 >= 16192) return;
  
  if (addr32 >= 8192) {
    addr32 -= 8192;
    ypos = ((addr32 / 80) << 2) + 2;
    xpos = (addr32 % 80) << 3;
  } else {
    ypos = (addr32 / 80) << 2;
    xpos = (addr32 % 80) << 3;
  }
  
  rfbSend8(0);
  rfbSend8(0);
  rfbSend16(1);
  rfbDrawGfxHi(xpos, ypos, 8, 2);
}

void rfbFlushWrites() {
  uint8_t i;
  if (rfbWriteCount == 0) {
    rfbFlush();
    return;
  }
  switch (vidmode) {
    case 0:
    case 1:
      for (i=0; i<rfbWriteCount; i++) {
        rfbChar40(rfbWrites[i]);
      }
      break;
    case 2:
    case 3:
      for (i=0; i<rfbWriteCount; i++) {
        rfbChar80(rfbWrites[i]);
      }
      break;
    case 4:
    case 5:
      for (i=0; i<rfbWriteCount; i++) {
        rfbGfxLo(rfbWrites[i]);
      }
      break;
    case 6:
      for (i=0; i<rfbWriteCount; i++) {
        rfbGfxHi(rfbWrites[i]);
      }
      break;
  }
  rfbFlush();
  rfbWriteCount = 0;
}

void rfbWrite(uint32_t addr32) {
  uint8_t i;
  if (rfbWriteCount == 64) rfbFlushWrites();
  if (vidmode < 4) addr32 &= 0xFFFFE;
  for (i=0; i<rfbWriteCount; i++) {
    if (rfbWrites[i] == addr32) return;
  }
  rfbWrites[rfbWriteCount++] = addr32;
}

void rfbParseIncoming() {
  uint8_t cmd;
  uint32_t len;
  if (rfbState != RFB_STATE_OPERATIONAL) return;
    while ((wfclient.available() > 0) && wfclient.connected()) {
      cmd = wfclient.peek();
      switch (cmd) {
        case 2: //setencodings
        {
          uint16_t i;
          rfbRead16();
          len = rfbRead16();
          for (i=0; i<len; i++) {
            rfbRead32();
          }
          break;
        }

        case 4: //keyevent
        {
          uint8_t isdown, scancode;
          uint32_t keysym;
          //SerDev.println("Key Event");
          rfbRead8();
          isdown = rfbRead8();
          rfbRead16();
          keysym = rfbRead32() & 0xFFFF;
          scancode = translatescancode(keysym);
          if (scancode != 0xFF) {
            portram[0x60] = scancode;
            if (!isdown) portram[0x60] |= 0x80;
            portram[0x64] |= 2;
            doirq(1);
          }
          break;
        }

        case 6: //clientcuttext
        {
          uint32_t i;
          rfbRead32();
          len = rfbRead32();
          for (i=0; i<len; i++) {
            rfbRead8();
          }
          break;
        }

        default:
        {
          uint8_t i;
          for (i=0; i<clientMsgLen[cmd]; i++) {
            rfbRead8();
          }
          break;
        }
      }
    }
}

void rfbWholeScreen() {
  rfbSend8(0);
  rfbSend8(0);
  rfbSend16(1);
  switch (vidmode) {
    case 0:
    case 1:
      rfbDrawChar40(0, 0, 640, 400);
      break;
    case 2:
    case 3:
      rfbDrawChar80(0, 0, 640, 400);
      break;
    case 4:
    case 5:
      rfbDrawGfxLo(0, 0, 640, 400);
      break;
    case 6:
      rfbDrawGfxHi(0, 0, 640, 400);
      break;
  }
  rfbFlush();
}

void rfbRun() {
  uint8_t curPin, cmd;
  uint32_t len;

  serverRun();

  if (!wfclient.connected()) {
    rfbState = RFB_STATE_IDLE;
    wfclient.stop();
  }
  switch (rfbState) {
    case RFB_STATE_WAIT_VERSION:
      if (wfclient.available() >= 12) {
        wfclient.readBytes(rfbBuf, 12);
        if ((rfbBuf[0] == 'R') && (rfbBuf[1] == 'F') && (rfbBuf[2] == 'B')) {
          rfbSend8(1);
          rfbSend8(1); //no authentication
          rfbState = RFB_STATE_WAIT_SECURITY_2;
        } else {
          rfbDiscardRx();
        }
      }
      break;

    case RFB_STATE_WAIT_SECURITY:
      if (wfclient.available() >= 1) {
        uint8_t i;
        wfclient.readBytes(rfbBuf, 1);
        for (i=0; i<16; i++) {
          rfbSend8(random(256));
        }
        rfbState = RFB_STATE_WAIT_SECURITY_2;
      }
      break;
      
    case RFB_STATE_WAIT_SECURITY_2:
      rfbSend32(0); //security handshake successful
      rfbState = RFB_STATE_WAIT_CLIENT_INIT;
      break;
      
    case RFB_STATE_WAIT_CLIENT_INIT:
      if (wfclient.available() >= 1) {
        uint16_t i;
        wfclient.readBytes(rfbBuf, 1);
        rfbOutByte((uint8_t)(WIDTH >> 8));
        rfbOutByte((uint8_t)(WIDTH & 255));
        rfbOutByte((uint8_t)(HEIGHT >> 8));
        rfbOutByte((uint8_t)(HEIGHT & 255));
        rfbOutByte((uint8_t)8); //bpp
        rfbOutByte((uint8_t)8); //depth
        rfbOutByte((uint8_t)0); //big endian flag
        rfbOutByte((uint8_t)1); //true color flag
        rfbSend16(3); //red max
        rfbSend16(7); //green max
        rfbSend16(3); //blue max
        rfbSend8(0); //red shift
        rfbSend8(2); //green shift
        rfbSend8(6); //blue shift
        rfbOutByte((uint8_t)0); //padding
        rfbOutByte((uint8_t)0); //padding
        rfbOutByte((uint8_t)0); //padding
        rfbOutByte((uint8_t)0);
        rfbOutByte((uint8_t)0);
        rfbOutByte((uint8_t)0);
        rfbOutByte(strlen(RFB_NAME));
        rfbOutString(RFB_NAME);
        

        rfbState = RFB_STATE_OPERATIONAL;
        rfbKnownRects = 0;
        rfbWholeScreen();
        rfbKnownRects = 4000;
        rfbWriteCount = 0;
      }
      break;

    case RFB_STATE_OPERATIONAL:
      rfbParseIncoming();
      break;

    case RFB_STATE_IDLE:
      break;
  }
  rfbFlushWrites();
}

void setcursor(uint16_t addr) {
  uint16_t oldaddr;
  oldaddr = ycurs * 160 + xcurs * 2;
  ycurs = addr / 80;
  xcurs = addr % 80;
  if (vidmode < 4) {
    rfbWrite(oldaddr);
    rfbWrite(ycurs * 160 + xcurs * 2);
  }
}

uint32_t rgb(uint32_t r, uint32_t g, uint32_t b) {
  return (r>>6) | ((g>>5)<<2) | ((b>>6)<<6);
}

void rfbInit() {
  palettecga[0] = 0;
  palettecga[1] = rgb (0, 0, 0xAA);
  palettecga[2] = rgb (0, 0xAA, 0);
  palettecga[3] = rgb (0, 0xAA, 0xAA);
  palettecga[4] = rgb (0xAA, 0, 0);
  palettecga[5] = rgb (0xAA, 0, 0xAA);
  palettecga[6] = rgb (0xAA, 0x55, 0);
  palettecga[7] = rgb (0xAA, 0xAA, 0xAA);
  palettecga[8] = rgb (0x55, 0x55, 0x55);
  palettecga[9] = rgb (0x55, 0x55, 0xFF);
  palettecga[10] = rgb (0x55, 0xFF, 0x55);
  palettecga[11] = rgb (0x55, 0xFF, 0xFF);
  palettecga[12] = rgb (0xFF, 0x55, 0x55);
  palettecga[13] = rgb (0xFF, 0x55, 0xFF);
  palettecga[14] = rgb (0xFF, 0xFF, 0x55);
  palettecga[15] = rgb (0xFF, 0xFF, 0xFF);

  WiFi.begin("REDACTED", "REDACTED");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    SerDev.print(".");
  }
  SerDev.println(WiFi.localIP());
  wfserver.begin(5900);
}