Skip to content
This repository was archived by the owner on Jan 20, 2025. It is now read-only.

Sending a 50 byte long binary WebSocket frame crashes ESP32 #921

Closed
brandonros opened this issue Jan 17, 2021 · 5 comments
Closed

Sending a 50 byte long binary WebSocket frame crashes ESP32 #921

brandonros opened this issue Jan 17, 2021 · 5 comments
Labels

Comments

@brandonros
Copy link

I've tried everything from pinning functions to different cores via tasks, adding a scheduler, doing non-async, doing async.

Here is the code:

#define _TASK_TIMEOUT

#include <can_common.h> // https://github.com/collin80/can_common.git
#include <esp32_can.h> // https://github.com/collin80/esp32_can.git + https://github.com/collin80/due_can.git
#include <iso-tp.h> // https://github.com/collin80/iso-tp.git
#include <AsyncTCP.h> // https://github.com/me-no-dev/AsyncTCP.git
#include <ESPAsyncWebServer.h> // https://github.com/me-no-dev/ESPAsyncWebServer.git
#include <TaskScheduler.h> // https://github.com/arkhipenko/TaskScheduler.git
#include <WiFi.h> // arduino-esp32

// CAN / ISO-TP
IsoTp isotp(&CAN0);
struct Message_t rxMsg;
struct Message_t txMsg;
// web server
AsyncWebServer server(8080);
AsyncWebSocket ws("/ws");
AsyncWebSocketClient* webSocketClient = NULL;
uint8_t *websocketMsgBuffer = NULL;
// task
Scheduler scheduler;
void canTask();
void onTask1Disable();
Task task1(TASK_MILLISECOND, TASK_FOREVER, &canTask, &scheduler, true, NULL, &onTask1Disable);

void blink() {
  digitalWrite(GPIO_NUM_2, HIGH);
  delay(50);
  digitalWrite(GPIO_NUM_2, LOW);
  delay(50);
}

uint32_t bytesToUint32(uint8_t *bytes) {
  return (bytes[3] << 24) +
     (bytes[2] << 16) + 
     (bytes[1] << 8) + 
     bytes[0];
}

void resetMessage(struct Message_t *msg) {
  msg->tp_state = ISOTP_IDLE;
  msg->len = 0;
  msg->seq_id = 1;
  msg->fc_status = ISOTP_FC_CTS;
  msg->blocksize = 0;
  msg->min_sep_time = 0;
}

void onSocketEvent(AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* payload, size_t length) {
  if (type == WS_EVT_CONNECT) {
    webSocketClient = client;
    Serial.println("webSocketClient connected");
  } else if (type == WS_EVT_DISCONNECT) {
    webSocketClient = NULL;
    Serial.println("webSocketClient disconnected");
  } else if (type == WS_EVT_DATA) {
    resetMessage(&txMsg);
    txMsg.len = length - 8;
    txMsg.tx_id = bytesToUint32(payload);
    txMsg.rx_id = bytesToUint32(payload + 4);
    memcpy(txMsg.Buffer, payload + 8, txMsg.len);
    isotp.send(&txMsg);
    Serial.printf("socket -> isotp %d\n", txMsg.len);
    blink();
  }
}

void serveIndex(AsyncWebServerRequest *request) {
  String index = R"EOF(
    <!doctype html>
    <html>
      <head>
      </head>
      <body>
        <p>Hello, world!</p>
        <script type="text/javascript">
          const buf2hex = (buf) => Array.prototype.map.call(new Uint8Array(buf), x => ('00' + x.toString(16)).slice(-2)).join('')
          const uint32ToBytes = (value) => [value & 0xFF, (value >> 8) & 0xFF, (value >> 16) & 0xFF, (value >> 24) & 0xFF]

          const socket = new WebSocket(`ws://${window.location.hostname}:${window.location.port}/ws`)
          socket.onopen = () => {
            console.log('opened')
          }
          socket.onmessage = async (event) => {
            if (event.data instanceof Blob) {
              const arrayBuffer = await event.data.arrayBuffer()
              console.log(buf2hex(arrayBuffer))
            }
          }
          socket.onclose = (event) => console.log(event)
          socket.onerror = (event) => console.log(event)

          const testerPresent = () => {
            const requestArbitrationId = 0x000007e5
            const replyArbitrationId = 0x000007ed
            const pdu = [].concat([0x3E, 0x00])
            const payload = new Uint8Array([].concat(uint32ToBytes(requestArbitrationId), uint32ToBytes(replyArbitrationId), pdu))
            socket.send(payload)
          }

          const transferData = (chunkSize) => {
            const requestArbitrationId = 0x000007e5
            const replyArbitrationId = 0x000007ed
            const chunk = new Array(chunkSize).fill(0xFF)
            const pdu = [].concat([0x36, 0x01], chunk)
            const payload = new Uint8Array([].concat(uint32ToBytes(requestArbitrationId), uint32ToBytes(replyArbitrationId), pdu))
            socket.send(payload)
          }
        </script>
      </body>
    </html>
  )EOF";
  request->send(200, "text/html", index);
  Serial.println("serve index");
  blink();
}

void onTask1Disable() {
  task1.enable();
}

void canTask() {
  // ISOTP recv
  if (CAN0.rx_avail()) {
    // receive replies from the reply arbitration ID
    uint32_t requestArbitrationId = 0x7E5;
    uint32_t replyArbitrationId = 0x7ED;
    resetMessage(&rxMsg);
    rxMsg.rx_id = replyArbitrationId;
    rxMsg.tx_id = requestArbitrationId;
    isotp.receive(&rxMsg);
    // send to socket if connected
    if (webSocketClient != NULL) {
      uint32_t bigEndianRxId = htonl(rxMsg.rx_id);
      uint32_t bigEndianTxId = htonl(rxMsg.tx_id);
      memcpy(websocketMsgBuffer, &bigEndianRxId, 4);
      memcpy(websocketMsgBuffer + 4, &bigEndianTxId, 4);
      memcpy(websocketMsgBuffer + 8, rxMsg.Buffer, rxMsg.len);
      webSocketClient->binary(websocketMsgBuffer, rxMsg.len + 8);
      Serial.printf("isotp -> socket %d\n", rxMsg.len);
    }
    blink();
  }
}

void setup() {
  // serial
  Serial.begin(115200);
  // wifi
  WiFi.softAP("ssid", "password");
  // LED
  pinMode(GPIO_NUM_2, OUTPUT);
  // web server
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) { serveIndex(request); });
  ws.onEvent(onSocketEvent);
  server.addHandler(&ws);
  server.begin();
  // enable CAN transceiver
  pinMode(GPIO_NUM_16, OUTPUT);
  digitalWrite(GPIO_NUM_16, LOW); 
  // set RXD=GPIO4, TXD=GPIO5
  CAN0.setCANPins(GPIO_NUM_4, GPIO_NUM_5);
  CAN0.begin(500000);
  CAN0.watchFor();
  // buffers
  websocketMsgBuffer = (uint8_t*)calloc(8192, sizeof(uint8_t));
  rxMsg.Buffer = (uint8_t*)calloc(MAX_MSGBUF, sizeof(uint8_t));
  txMsg.Buffer = (uint8_t*)calloc(MAX_MSGBUF, sizeof(uint8_t));
  // tasks
  task1.setTimeout(100 * TASK_MILLISECOND);
  scheduler.enableAll();
}

void loop() {
  // tasks
  scheduler.execute();
}

I load http://192.168.4.1:8080/ in a web browser, pop the console, then do transferData(50) and I get this:

E (22813) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
E (22813) task_wdt:  - async_tcp (CPU 0/1)
E (22813) task_wdt: Tasks currently running:
E (22813) task_wdt: CPU 0: IDLE0
E (22813) task_wdt: CPU 1: async_tcp
E (22813) task_wdt: Aborting.
abort() was called at PC 0x400e032f on core 0

Backtrace: 0x4008c6ec:0x3ffbe170 0x4008c91d:0x3ffbe190 0x400e032f:0x3ffbe1b0 0x400849f9:0x3ffbe1d0 0x401480af:0x3ffbc130 0x400e16df:0x3ffbc150 0x4008a619:0x3ffbc170 0x40088e35:0x3ffbc190

Rebooting...

Oddly enough, transferData(40) seems to work ok. I've already tried doing testerPresent() at different intervals (which is a 2 byte message). At 100ms, it is ok. At 50ms, the device crashes. Would love to hear some tips on how to tweak this. I quadruple checked the code for performance tweaks, not really seeing anything obvious.

@brandonros
Copy link
Author

@collin80 I hate to tag you but it's mainly your CAN library forks (which you turned issues off on). Curious if you have any tips.

@collin80
Copy link

The esp32_can repo certainly has "issues" enabled and I don't know of any reason you shouldn't be able to create an issue there. I checked and nothing seems out of place. If you believe it is crashing within that library or one of my other libraries then a decode of the crashdump might help. Otherwise, the issue you found in reference to deadlocks of the async_tcp task seems like a good avenue to pursue.

@brandonros
Copy link
Author

brandonros commented Jan 18, 2021

@collin80 It's your iso-tp repo that has issues turned off, sorry. If I turn off isotp.send(&txMsg);, I'm able to partially receive ~2.6kb of a 4.1kb WebSocket frame. This is for sure not your issue Collin. Sorry to have looped you in.

@stale
Copy link

stale bot commented Mar 19, 2021

[STALE_SET] This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 14 days if no further activity occurs. Thank you for your contributions.

@stale stale bot added the stale label Mar 19, 2021
@stale
Copy link

stale bot commented Jun 2, 2021

[STALE_DEL] This stale issue has been automatically closed. Thank you for your contributions.

@stale stale bot closed this as completed Jun 2, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

2 participants