WebSerial.h 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. /*
  2. __ __ _ ____ _ _
  3. \ \ / /__| |__/ ___| ___ _ __(_) __ _| |
  4. \ \ /\ / / _ \ '_ \___ \ / _ \ '__| |/ _` | |
  5. \ V V / __/ |_) |__) | __/ | | | (_| | |
  6. \_/\_/ \___|_.__/____/ \___|_| |_|\__,_|_|
  7. A remote terminal for wireless microcontrollers!
  8. Checkout Pro version at: https://webserial.pro
  9. -----
  10. Author: Ayush Sharma (ayush@softt.io)
  11. License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html)
  12. */
  13. #ifndef WebSerial_h
  14. #define WebSerial_h
  15. #include "Arduino.h"
  16. #include "stdlib_noniso.h"
  17. #include <functional>
  18. #if defined(ESP8266)
  19. #define HARDWARE "ESP8266"
  20. #include "ESP8266WiFi.h"
  21. #include "ESPAsyncTCP.h"
  22. #include "ESPAsyncWebServer.h"
  23. #elif defined(ESP32)
  24. #define HARDWARE "ESP32"
  25. #include "WiFi.h"
  26. #include "AsyncTCP.h"
  27. #include "ESPAsyncWebServer.h"
  28. #elif defined(TARGET_RP2040) || defined(TARGET_RP2350) || defined(PICO_RP2040) || defined(PICO_RP2350)
  29. #include "WiFi.h"
  30. #include "RPAsyncTCP.h"
  31. #include "ESPAsyncWebServer.h"
  32. #if defined(TARGET_RP2040) || defined(PICO_RP2040)
  33. #define HARDWARE "RP2040"
  34. #elif defined(TARGET_RP2350) || defined(PICO_RP2350)
  35. #define HARDWARE "RP2350"
  36. #endif
  37. #endif
  38. #ifndef WSL_MAX_WS_CLIENTS
  39. #define WSL_MAX_WS_CLIENTS DEFAULT_MAX_WS_CLIENTS
  40. #endif
  41. // High performance mode:
  42. // - Low memory footprint (no stack allocation, no global buffer by default)
  43. // - Low latency (messages sent immediately to the WebSocket queue)
  44. // - High throughput (up to 20 messages per second, no locking mechanism)
  45. // Activation with: -D WSL_HIGH_PERFORMANCE
  46. // Also recommended to tweak AsyncTCP and ESPAsyncWebServer settings, for example:
  47. // -D CONFIG_ASYNC_TCP_QUEUE_SIZE=128 // AsyncTCP queue size
  48. // -D CONFIG_ASYNC_TCP_RUNNING_CORE=1 // core for the async_task
  49. // -D WS_MAX_QUEUED_MESSAGES=128 // WS message queue size
  50. #ifndef WSL_HIGH_PERF
  51. // Global buffer ( buffers all packets )
  52. #ifndef WSL_BUFFER_SIZE
  53. #define WSL_BUFFER_SIZE 2048
  54. #endif
  55. #ifndef WSL_PRINT_BUFFER_SIZE
  56. #define WSL_PRINT_BUFFER_SIZE 1024
  57. #endif
  58. #ifndef WSL_MAX_ROW_PACKET_PAYLOAD_SIZE
  59. #define WSL_MAX_ROW_PACKET_PAYLOAD_SIZE 512
  60. #endif
  61. #ifndef WSL_PRINT_FLUSH_TIME_US
  62. #define WSL_PRINT_FLUSH_TIME_US 100
  63. #endif
  64. #ifndef WSL_GLOBAL_FLUSH_TIME_MS
  65. #define WSL_GLOBAL_FLUSH_TIME_MS 100
  66. #endif
  67. #ifndef WSL_CLEANUP_TIME_MS
  68. #define WSL_CLEANUP_TIME_MS 5000
  69. #endif
  70. #if WSL_BUFFER_SIZE < 512
  71. #error "WSL_BUFFER_SIZE must be >= 512 bytes"
  72. #endif
  73. #if WSL_BUFFER_SIZE < WSL_PRINT_BUFFER_SIZE
  74. #error "WSL_BUFFER_SIZE must be >= WSL_PRINT_BUFFER_SIZE"
  75. #endif
  76. #if WSL_PRINT_FLUSH_TIME_US < 1
  77. #error "WSL_PRINT_FLUSH_TIME_US must be greater than 1us"
  78. #endif
  79. #if WSL_GLOBAL_FLUSH_TIME_MS < 50
  80. #error "WSL_GLOBAL_FLUSH_TIME_MS must be greater than 50ms"
  81. #endif
  82. #endif // WSL_HIGH_PERFORMANCE
  83. typedef std::function<void(uint8_t *data, size_t len)> WSLMessageHandler;
  84. typedef std::function<void(const String& msg)> WSLStringMessageHandler;
  85. class WebSerialClass : public Print {
  86. public:
  87. void begin(AsyncWebServer *server, const char* url = "/webserial");
  88. inline void setAuthentication(const char* username, const char* password) { setAuthentication(String(username), String(password)); }
  89. void setAuthentication(const String& username, const String& password);
  90. void onMessage(WSLMessageHandler recv);
  91. void onMessage(WSLStringMessageHandler recv);
  92. bool getConnectionCount();
  93. size_t write(uint8_t) override;
  94. size_t write(const uint8_t* buffer, size_t size) override;
  95. // Only valid if WSL_HIGH_PERF is not activated (which is the default)
  96. // Housekeeping for WebSerial internals.
  97. // Calling this loop has no effect if WSL_HIGH_PERF is activated
  98. void loop();
  99. // Only valid if WSL_HIGH_PERF is activated
  100. // A buffer (shared across cores) can be initialised with an initial capacity to be able to use any Print functions event those that are not buffered and would
  101. // create a performance impact for WS calls. The goal of this buffer is to be used with lines ending with '\n', like log messages.
  102. // The buffer size will eventually grow until a '\n' is found, then the message will be sent to the WS clients and a new buffer will be created.
  103. // Set initialCapacity to 0 to disable buffering.
  104. // Must be called before begin(): calling it after will erase the buffer and its content will be lost.
  105. // The buffer is not enabled by default.
  106. void setBuffer(size_t initialCapacity);
  107. #ifdef WSL_HIGH_PERF
  108. // Expose the internal WebSocket makeBuffer to even improve memory consumption on client-side
  109. // 1. make a AsyncWebSocketMessageBuffer
  110. // 2. put the data inside
  111. // 3. send the buffer
  112. // This method avoids a buffer copy when creating the WebSocket message
  113. AsyncWebSocketMessageBuffer* makeBuffer(size_t size = 0) {
  114. if (!_ws)
  115. return nullptr;
  116. return _ws->makeBuffer(size);
  117. }
  118. void send(AsyncWebSocketMessageBuffer* buffer) {
  119. if (!_ws || !buffer)
  120. return;
  121. _ws->cleanupClients(WSL_MAX_WS_CLIENTS);
  122. if (_ws->count())
  123. _ws->textAll(buffer);
  124. }
  125. #endif
  126. private:
  127. // Server
  128. AsyncWebServer *_server;
  129. AsyncWebSocket *_ws;
  130. WSLMessageHandler _recv = nullptr;
  131. WSLStringMessageHandler _recvString = nullptr;
  132. bool _authenticate = false;
  133. String _username;
  134. String _password;
  135. #ifdef WSL_HIGH_PERF
  136. size_t _initialBufferCapacity = 0;
  137. String _buffer;
  138. void _send(const uint8_t* buffer, size_t size);
  139. #else
  140. unsigned long _last_cleanup_time = 0;
  141. // Global Buffer
  142. size_t _buffer_offset = 0;
  143. uint8_t _buffer[WSL_BUFFER_SIZE];
  144. // Print buffer
  145. size_t _print_buffer_offset = 0;
  146. uint8_t _print_buffer[WSL_PRINT_BUFFER_SIZE];
  147. unsigned long _last_print_buffer_write_time = 0;
  148. unsigned long _last_print_buffer_flush_time = 0;
  149. // Print
  150. bool _has_enough_space(size_t size);
  151. size_t _start_row();
  152. size_t _write_row(uint8_t *data, size_t len);
  153. size_t _end_row();
  154. void _flush_print_buffer();
  155. void _flush_global_buffer();
  156. #endif
  157. static size_t _write_row_packet(uint8_t* dest, const uint8_t *payload, size_t payload_size);
  158. };
  159. extern WebSerialClass WebSerial;
  160. #endif