|  | @@ -15,6 +15,27 @@ typedef enum {
 | 
	
		
			
				|  |  |    WSL_PONG = 0x04,
 | 
	
		
			
				|  |  |  } WSLPacketType;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +static const uint8_t WSL_PONG_MSG[] = {WSL_MAGIC_BYTE_1, WSL_MAGIC_BYTE_2, WSLPacketType::WSL_PONG};
 | 
	
		
			
				|  |  | +static const size_t WSL_PONG_MSG_LEN = sizeof(WSL_PONG_MSG) / sizeof(WSL_PONG_MSG[0]);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static const uint8_t WSL_HEAD[] = {
 | 
	
		
			
				|  |  | +  WSL_MAGIC_BYTE_1,             // Magic Bytes
 | 
	
		
			
				|  |  | +  WSL_MAGIC_BYTE_2,             // Magic Bytes
 | 
	
		
			
				|  |  | +  WSLPacketType::WSL_WRITE_ROW, // Packet Type (1 byte)
 | 
	
		
			
				|  |  | +  0x00,                         // Reserved
 | 
	
		
			
				|  |  | +  0x00,                         // Reserved
 | 
	
		
			
				|  |  | +  0x00,                         // Reserved
 | 
	
		
			
				|  |  | +  0x00,                         // Reserved
 | 
	
		
			
				|  |  | +  0x00,                         // Padding
 | 
	
		
			
				|  |  | +  0x00,                         // Padding
 | 
	
		
			
				|  |  | +  0x00,                         // Padding
 | 
	
		
			
				|  |  | +  0x00,                         // Padding
 | 
	
		
			
				|  |  | +  0x00                          // Reserved
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +static const size_t WSL_HEAD_LEN = sizeof(WSL_HEAD) / sizeof(WSL_HEAD[0]);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static const size_t WSL_MSG_SIZE_LEN = sizeof(uint16_t);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  void WebSerialClass::setAuthentication(const String& username, const String& password){
 | 
	
		
			
				|  |  |    _username = username;
 | 
	
		
			
				|  |  |    _password = password;
 | 
	
	
		
			
				|  | @@ -48,6 +69,10 @@ void WebSerialClass::begin(AsyncWebServer *server, const char* url) {
 | 
	
		
			
				|  |  |      // if(type == WS_EVT_CONNECT){
 | 
	
		
			
				|  |  |      // } else if(type == WS_EVT_DISCONNECT){
 | 
	
		
			
				|  |  |      // } else if(type == WS_EVT_DATA){
 | 
	
		
			
				|  |  | +    if (type == WS_EVT_CONNECT) {
 | 
	
		
			
				|  |  | +      client->setCloseClientOnQueueFull(false);
 | 
	
		
			
				|  |  | +      return;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |      if(type == WS_EVT_DATA){
 | 
	
		
			
				|  |  |        // Detect magic bytes
 | 
	
		
			
				|  |  |        if (data[0] == WSL_MAGIC_BYTE_1 && data[1] == WSL_MAGIC_BYTE_2) {
 | 
	
	
		
			
				|  | @@ -60,8 +85,7 @@ void WebSerialClass::begin(AsyncWebServer *server, const char* url) {
 | 
	
		
			
				|  |  |            }
 | 
	
		
			
				|  |  |          } else if (data[2] == WSLPacketType::WSL_PING) {
 | 
	
		
			
				|  |  |            // Send pong
 | 
	
		
			
				|  |  | -          uint8_t pong[] = {WSL_MAGIC_BYTE_1, WSL_MAGIC_BYTE_2, WSLPacketType::WSL_PONG};
 | 
	
		
			
				|  |  | -          client->binary(pong, sizeof(pong) / sizeof(pong[0]));
 | 
	
		
			
				|  |  | +          client->binary(WSL_PONG_MSG, WSL_PONG_MSG_LEN);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |      }
 | 
	
	
		
			
				|  | @@ -76,14 +100,74 @@ void WebSerialClass::onMessage(WSLMessageHandler recv) {
 | 
	
		
			
				|  |  |    _recv = recv;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +void WebSerialClass::onMessage(WSLStringMessageHandler callback) {
 | 
	
		
			
				|  |  | +  _recv = [&](uint8_t *data, size_t len) {
 | 
	
		
			
				|  |  | +    if(data && len) {
 | 
	
		
			
				|  |  | +#ifdef ESP8266
 | 
	
		
			
				|  |  | +      String msg;
 | 
	
		
			
				|  |  | +      msg.reserve(len);
 | 
	
		
			
				|  |  | +      msg.concat((char*)data, len);
 | 
	
		
			
				|  |  | +      callback(msg);
 | 
	
		
			
				|  |  | +#else
 | 
	
		
			
				|  |  | +      callback(String((char*)data, len));
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  };
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  // Print func
 | 
	
		
			
				|  |  |  size_t WebSerialClass::write(uint8_t m) {
 | 
	
		
			
				|  |  | +  if (!_ws)
 | 
	
		
			
				|  |  | +    return 0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#ifdef WSL_HIGH_PERF
 | 
	
		
			
				|  |  | +  // We do not support non-buffered write on webserial for the HIGH_PERF version
 | 
	
		
			
				|  |  | +  // we fail with a stack trace allowing the user to change the code to use write(const uint8_t* buffer, size_t size) instead
 | 
	
		
			
				|  |  | +  if(!_initialBufferCapacity) {
 | 
	
		
			
				|  |  | +#ifdef ESP8266
 | 
	
		
			
				|  |  | +    ets_printf("Non-buffered write is not supported. Please use write(const uint8_t* buffer, size_t size) instead.");
 | 
	
		
			
				|  |  | +#else
 | 
	
		
			
				|  |  | +    log_e("Non-buffered write is not supported. Please use write(const uint8_t* buffer, size_t size) instead.");
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  | +    assert(false);
 | 
	
		
			
				|  |  | +    return 0;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +#endif // WSL_HIGH_PERF
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    write(&m, 1);
 | 
	
		
			
				|  |  |    return(1);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  // Println / Printf / Write func
 | 
	
		
			
				|  |  | -size_t WebSerialClass::write(uint8_t* buffer, size_t size) {
 | 
	
		
			
				|  |  | +size_t WebSerialClass::write(const uint8_t* buffer, size_t size) {
 | 
	
		
			
				|  |  | +  if (!_ws || size == 0)
 | 
	
		
			
				|  |  | +    return 0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#ifdef WSL_HIGH_PERF
 | 
	
		
			
				|  |  | +  // No buffer, send directly (i.e. use case for log streaming)
 | 
	
		
			
				|  |  | +  if (!_initialBufferCapacity) {
 | 
	
		
			
				|  |  | +    size = buffer[size - 1] == '\n' ? size - 1 : size;
 | 
	
		
			
				|  |  | +    _send(buffer, size);
 | 
	
		
			
				|  |  | +    return size;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // fill the buffer while sending data for each EOL
 | 
	
		
			
				|  |  | +  size_t start = 0, end = 0;
 | 
	
		
			
				|  |  | +  while (end < size) {
 | 
	
		
			
				|  |  | +    if (buffer[end] == '\n') {
 | 
	
		
			
				|  |  | +      if (end > start) {
 | 
	
		
			
				|  |  | +        _buffer.concat(reinterpret_cast<const char*>(buffer + start), end - start);
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +      _send(reinterpret_cast<const uint8_t*>(_buffer.c_str()), _buffer.length());
 | 
	
		
			
				|  |  | +      start = end + 1;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    end++;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  if (end > start) {
 | 
	
		
			
				|  |  | +    _buffer.concat(reinterpret_cast<const char*>(buffer + start), end - start);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  return size;
 | 
	
		
			
				|  |  | +#else
 | 
	
		
			
				|  |  |    loop();
 | 
	
		
			
				|  |  |    _wait_for_print_mutex();
 | 
	
		
			
				|  |  |    _print_buffer_mutex = true;
 | 
	
	
		
			
				|  | @@ -99,8 +183,33 @@ size_t WebSerialClass::write(uint8_t* buffer, size_t size) {
 | 
	
		
			
				|  |  |    _print_buffer_mutex = false;
 | 
	
		
			
				|  |  |    _last_print_buffer_write_time = micros();
 | 
	
		
			
				|  |  |    return(size);
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#ifdef WSL_HIGH_PERF
 | 
	
		
			
				|  |  | +void WebSerialClass::_send(const uint8_t* buffer, size_t size) {
 | 
	
		
			
				|  |  | +  if (_ws && size > 0) {
 | 
	
		
			
				|  |  | +    _ws->cleanupClients(WSL_MAX_WS_CLIENTS);
 | 
	
		
			
				|  |  | +    if (_ws->count()) {
 | 
	
		
			
				|  |  | +      if (size > UINT16_MAX)
 | 
	
		
			
				|  |  | +        size = UINT16_MAX;
 | 
	
		
			
				|  |  | +      AsyncWebSocketMessageBuffer* wsbuffer = _ws->makeBuffer(WSL_HEAD_LEN + WSL_MSG_SIZE_LEN + size);
 | 
	
		
			
				|  |  | +      _write_row_packet(wsbuffer->get(), buffer, size);
 | 
	
		
			
				|  |  | +      _ws->binaryAll(wsbuffer);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // if buffer grew too much, free it, otherwise clear it
 | 
	
		
			
				|  |  | +  if (_initialBufferCapacity) {
 | 
	
		
			
				|  |  | +    if (_buffer.length() > _initialBufferCapacity) {
 | 
	
		
			
				|  |  | +      setBuffer(_initialBufferCapacity);
 | 
	
		
			
				|  |  | +    } else {
 | 
	
		
			
				|  |  | +      _buffer.clear();
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +#else // WSL_HIGH_PERF
 | 
	
		
			
				|  |  |  void WebSerialClass::_wait_for_global_mutex() {
 | 
	
		
			
				|  |  |    // Wait for mutex to be released
 | 
	
		
			
				|  |  |    if (_buffer_mutex) {
 | 
	
	
		
			
				|  | @@ -124,40 +233,6 @@ bool WebSerialClass::_has_enough_space(size_t size) {
 | 
	
		
			
				|  |  |    return (_buffer_offset + WSL_CALC_LOG_PACKET_SIZE(size) > WSL_BUFFER_SIZE);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -size_t WebSerialClass::_write_row_packet(__unused uint64_t reserved1, __unused uint8_t reserved2, const uint8_t *payload, const size_t payload_size) {
 | 
	
		
			
				|  |  | -  size_t header_size = 0;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  // Write Magic Bytes
 | 
	
		
			
				|  |  | -  _buffer[_buffer_offset + header_size++] = WSL_MAGIC_BYTE_1;
 | 
	
		
			
				|  |  | -  _buffer[_buffer_offset + header_size++] = WSL_MAGIC_BYTE_2;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  // Packet Type (1 byte)
 | 
	
		
			
				|  |  | -  _buffer[_buffer_offset + header_size++] = WSLPacketType::WSL_WRITE_ROW;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  // Reserved (8 bytes)
 | 
	
		
			
				|  |  | -  _buffer[_buffer_offset + header_size++] = 0x00;
 | 
	
		
			
				|  |  | -  _buffer[_buffer_offset + header_size++] = 0x00;
 | 
	
		
			
				|  |  | -  _buffer[_buffer_offset + header_size++] = 0x00;
 | 
	
		
			
				|  |  | -  _buffer[_buffer_offset + header_size++] = 0x00;
 | 
	
		
			
				|  |  | -  _buffer[_buffer_offset + header_size++] = 0x00;
 | 
	
		
			
				|  |  | -  _buffer[_buffer_offset + header_size++] = 0x00;
 | 
	
		
			
				|  |  | -  _buffer[_buffer_offset + header_size++] = 0x00;
 | 
	
		
			
				|  |  | -  _buffer[_buffer_offset + header_size++] = 0x00;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  // Reserved (1 byte)
 | 
	
		
			
				|  |  | -  _buffer[_buffer_offset + header_size++] = 0x00;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  // Message Length (2 bytes)
 | 
	
		
			
				|  |  | -  memset(_buffer + _buffer_offset + header_size, (uint16_t)payload_size, sizeof((uint16_t)payload_size));
 | 
	
		
			
				|  |  | -  header_size += sizeof((uint16_t)payload_size);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  // Set Message
 | 
	
		
			
				|  |  | -  memcpy(_buffer + _buffer_offset + header_size, payload, payload_size);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  // Return total packet size
 | 
	
		
			
				|  |  | -  return header_size + payload_size;
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  size_t WebSerialClass::_write_row(uint8_t *data, size_t len) {
 | 
	
		
			
				|  |  |    // Split the logData into multiple packets
 | 
	
		
			
				|  |  |    size_t remaining_size = len;
 | 
	
	
		
			
				|  | @@ -178,7 +253,7 @@ size_t WebSerialClass::_write_row(uint8_t *data, size_t len) {
 | 
	
		
			
				|  |  |      _buffer_mutex = true;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      // Write Packet to Buffer
 | 
	
		
			
				|  |  | -    _buffer_offset += _write_row_packet(0, 0, current_ptr, packet_size);
 | 
	
		
			
				|  |  | +    _buffer_offset += _write_row_packet(_buffer, current_ptr, packet_size);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      // Unlock Mutex
 | 
	
		
			
				|  |  |      _buffer_mutex = false;
 | 
	
	
		
			
				|  | @@ -223,11 +298,13 @@ void WebSerialClass::_flush_global_buffer() {
 | 
	
		
			
				|  |  |      _buffer_mutex = false;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  | +#endif // WSL_HIGH_PERF
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  void WebSerialClass::loop() {
 | 
	
		
			
				|  |  | +#ifndef WSL_HIGH_PERF
 | 
	
		
			
				|  |  |    if ((unsigned long)(millis() - _last_cleanup_time) > WSL_CLEANUP_TIME_MS) {
 | 
	
		
			
				|  |  |      _last_cleanup_time = millis();
 | 
	
		
			
				|  |  | -    _ws->cleanupClients();
 | 
	
		
			
				|  |  | +    _ws->cleanupClients(WSL_MAX_WS_CLIENTS);
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    // If FLUSH_TIME ms has been passed since last packet time, flush logs
 | 
	
	
		
			
				|  | @@ -243,6 +320,30 @@ void WebSerialClass::loop() {
 | 
	
		
			
				|  |  |        _flush_global_buffer();
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | +#endif // WSL_HIGH_PERF
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +void WebSerialClass::setBuffer(size_t initialCapacity) {
 | 
	
		
			
				|  |  | +#ifdef WSL_HIGH_PERF
 | 
	
		
			
				|  |  | +  assert(initialCapacity <= UINT16_MAX);
 | 
	
		
			
				|  |  | +  _initialBufferCapacity = initialCapacity;
 | 
	
		
			
				|  |  | +  _buffer = String();
 | 
	
		
			
				|  |  | +  _buffer.reserve(initialCapacity);
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +size_t WebSerialClass::_write_row_packet(uint8_t* dest, const uint8_t *payload, size_t payload_size) {
 | 
	
		
			
				|  |  | +  // sanity check to ensure the payload size is within the hard limit
 | 
	
		
			
				|  |  | +  if(payload_size > UINT16_MAX)
 | 
	
		
			
				|  |  | +    payload_size = UINT16_MAX;
 | 
	
		
			
				|  |  | +  // Write header
 | 
	
		
			
				|  |  | +  memmove(dest, WSL_HEAD, WSL_HEAD_LEN);
 | 
	
		
			
				|  |  | +  // Message Length (2 bytes)
 | 
	
		
			
				|  |  | +  memset(dest + WSL_HEAD_LEN, static_cast<uint16_t>(payload_size), WSL_MSG_SIZE_LEN);
 | 
	
		
			
				|  |  | +  // Set Message
 | 
	
		
			
				|  |  | +  memmove(dest + WSL_HEAD_LEN + WSL_MSG_SIZE_LEN, payload, payload_size);
 | 
	
		
			
				|  |  | +  // Return total packet size
 | 
	
		
			
				|  |  | +  return WSL_HEAD_LEN + WSL_MSG_SIZE_LEN + payload_size;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  WebSerialClass WebSerial;
 |