|
| 1 | +/** |
| 2 | + * Marlin 3D Printer Firmware |
| 3 | + * Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] |
| 4 | + * |
| 5 | + * Based on Sprinter and grbl. |
| 6 | + * Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm |
| 7 | + * |
| 8 | + * This program is free software: you can redistribute it and/or modify |
| 9 | + * it under the terms of the GNU General Public License as published by |
| 10 | + * the Free Software Foundation, either version 3 of the License, or |
| 11 | + * (at your option) any later version. |
| 12 | + * |
| 13 | + * This program is distributed in the hope that it will be useful, |
| 14 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 16 | + * GNU General Public License for more details. |
| 17 | + * |
| 18 | + * You should have received a copy of the GNU General Public License |
| 19 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 20 | + * |
| 21 | + */ |
| 22 | +#ifdef ARDUINO_ARCH_ESP32 |
| 23 | + |
| 24 | +#include "../../inc/MarlinConfig.h" |
| 25 | + |
| 26 | +#if ENABLED(WIFISUPPORT) |
| 27 | + |
| 28 | +#include "WebSocketSerial.h" |
| 29 | +#include "wifi.h" |
| 30 | + |
| 31 | +#include <AsyncTCP.h> |
| 32 | +#include <ESPAsyncWebServer.h> |
| 33 | + |
| 34 | +struct ring_buffer_r { |
| 35 | + unsigned char buffer[RX_BUFFER_SIZE]; |
| 36 | + volatile ring_buffer_pos_t head, tail; |
| 37 | +}; |
| 38 | + |
| 39 | +struct ring_buffer_t { |
| 40 | + unsigned char buffer[256]; |
| 41 | + volatile uint8_t head, tail; |
| 42 | +}; |
| 43 | + |
| 44 | +ring_buffer_r rx_buffer = { { 0 }, 0, 0 }; |
| 45 | +ring_buffer_t tx_buffer = { { 0 }, 0, 0 }; |
| 46 | + |
| 47 | +static bool _written; |
| 48 | + |
| 49 | +#if ENABLED(EMERGENCY_PARSER) |
| 50 | + static EmergencyParser::State emergency_state; // = EP_RESET |
| 51 | +#endif |
| 52 | + |
| 53 | +AsyncWebSocket ws("/ws"); // access at ws://[esp ip]/ws |
| 54 | + |
| 55 | +FORCE_INLINE int next_rx_index(const int i) { return (ring_buffer_pos_t)(i + 1) & (ring_buffer_pos_t)(RX_BUFFER_SIZE - 1); } |
| 56 | +FORCE_INLINE int next_tx_index(const int i) { return (ring_buffer_pos_t)(i + 1) & (ring_buffer_pos_t)(TX_BUFFER_SIZE - 1); } |
| 57 | + |
| 58 | +static void addToBuffer(uint8_t * const data, const size_t len) { |
| 59 | + for (size_t i = 0; i < len; i++) { |
| 60 | + ring_buffer_pos_t h = rx_buffer.head; |
| 61 | + const ring_buffer_pos_t t = rx_buffer.tail, n = next_rx_index(h); |
| 62 | + |
| 63 | + if (n != t) { rx_buffer.buffer[h] = data[i]; h = n; } |
| 64 | + |
| 65 | + // TODO: buffer is full, handle? |
| 66 | + |
| 67 | + rx_buffer.head = h; |
| 68 | + } |
| 69 | +} |
| 70 | + |
| 71 | +// Handle WebSocket event |
| 72 | +static void onEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len) { |
| 73 | + switch (type) { |
| 74 | + case WS_EVT_CONNECT: client->ping(); break; // client connected |
| 75 | + case WS_EVT_DISCONNECT: // client disconnected |
| 76 | + case WS_EVT_ERROR: // error was received from the other end |
| 77 | + case WS_EVT_PONG: break; // pong message was received (in response to a ping request maybe) |
| 78 | + case WS_EVT_DATA: { // data packet |
| 79 | + AwsFrameInfo * info = (AwsFrameInfo*)arg; |
| 80 | + if (info->opcode == WS_TEXT || info->message_opcode == WS_TEXT) |
| 81 | + addToBuffer(data, len); |
| 82 | + } |
| 83 | + } |
| 84 | +} |
| 85 | + |
| 86 | +// Public Methods |
| 87 | +void WebSocketSerial::begin(const long baud_setting) { |
| 88 | + ws.onEvent(onEvent); |
| 89 | + server.addHandler(&ws); // attach AsyncWebSocket |
| 90 | +} |
| 91 | + |
| 92 | +void WebSocketSerial::end() { } |
| 93 | + |
| 94 | +int WebSocketSerial::peek(void) { |
| 95 | + const int v = rx_buffer.head == rx_buffer.tail ? -1 : rx_buffer.buffer[rx_buffer.tail]; |
| 96 | + return v; |
| 97 | +} |
| 98 | + |
| 99 | +int WebSocketSerial::read(void) { |
| 100 | + const ring_buffer_pos_t h = rx_buffer.head, t = rx_buffer.tail; |
| 101 | + if (h == t) return -1; // Nothing to read? Return now |
| 102 | + |
| 103 | + const int v = rx_buffer.buffer[t]; |
| 104 | + |
| 105 | + rx_buffer.tail = (ring_buffer_pos_t)(t + 1) & (RX_BUFFER_SIZE - 1); // Advance tail |
| 106 | + |
| 107 | + return v; |
| 108 | +} |
| 109 | + |
| 110 | +bool WebSocketSerial::available(void) { |
| 111 | + const ring_buffer_pos_t h = rx_buffer.head, t = rx_buffer.tail; |
| 112 | + return (ring_buffer_pos_t)(RX_BUFFER_SIZE + h - t) & (RX_BUFFER_SIZE - 1); |
| 113 | +} |
| 114 | + |
| 115 | +void WebSocketSerial::flush(void) { |
| 116 | + ws.textAll("flush"); |
| 117 | + rx_buffer.tail = rx_buffer.head; |
| 118 | +} |
| 119 | + |
| 120 | +#if TX_BUFFER_SIZE |
| 121 | + |
| 122 | + void WebSocketSerial::write(const uint8_t c) { |
| 123 | + _written = true; |
| 124 | + |
| 125 | + const uint8_t i = (tx_buffer.head + 1) & (TX_BUFFER_SIZE - 1); |
| 126 | + |
| 127 | + // Store new char. head is always safe to move |
| 128 | + tx_buffer.buffer[tx_buffer.head] = c; |
| 129 | + tx_buffer.head = i; |
| 130 | + |
| 131 | + if (c == '\n') { |
| 132 | + ws.textAll(tx_buffer.buffer, tx_buffer.head); |
| 133 | + tx_buffer.head = 0; |
| 134 | + } |
| 135 | + } |
| 136 | + |
| 137 | + void WebSocketSerial::flushTx(void) { |
| 138 | + ws.textAll("flushTx"); |
| 139 | + if (!_written) return; |
| 140 | + } |
| 141 | + |
| 142 | +#else |
| 143 | + |
| 144 | + //void WebSocketSerial::write(const uint8_t c) { _written = true; } |
| 145 | + //void WebSocketSerial::flushTx(void) { if (!_written) return; } |
| 146 | + |
| 147 | +#endif |
| 148 | + |
| 149 | +/** |
| 150 | + * Imports from print.h |
| 151 | + */ |
| 152 | + |
| 153 | +void WebSocketSerial::print(char c, int base) { print((long)c, base); } |
| 154 | +void WebSocketSerial::print(unsigned char b, int base) { print((unsigned long)b, base); } |
| 155 | +void WebSocketSerial::print(int n, int base) { print((long)n, base); } |
| 156 | +void WebSocketSerial::print(unsigned int n, int base) { print((unsigned long)n, base); } |
| 157 | +void WebSocketSerial::print(long n, int base) { |
| 158 | + if (base == 0) |
| 159 | + write(n); |
| 160 | + else if (base == 10) { |
| 161 | + if (n < 0) { print('-'); n = -n; } |
| 162 | + printNumber(n, 10); |
| 163 | + } |
| 164 | + else |
| 165 | + printNumber(n, base); |
| 166 | +} |
| 167 | + |
| 168 | +void WebSocketSerial::print(unsigned long n, int base) { |
| 169 | + if (base == 0) write(n); else printNumber(n, base); |
| 170 | +} |
| 171 | + |
| 172 | +void WebSocketSerial::print(double n, int digits) { printFloat(n, digits); } |
| 173 | + |
| 174 | +void WebSocketSerial::println(void) { print('\r'); print('\n'); } |
| 175 | +void WebSocketSerial::println(const String& s) { print(s); println(); } |
| 176 | +void WebSocketSerial::println(const char c[]) { print(c); println(); } |
| 177 | +void WebSocketSerial::println(char c, int base) { print(c, base); println(); } |
| 178 | +void WebSocketSerial::println(unsigned char b, int base) { print(b, base); println(); } |
| 179 | +void WebSocketSerial::println(int n, int base) { print(n, base); println(); } |
| 180 | +void WebSocketSerial::println(unsigned int n, int base) { print(n, base); println(); } |
| 181 | +void WebSocketSerial::println(long n, int base) { print(n, base); println(); } |
| 182 | +void WebSocketSerial::println(unsigned long n, int base) { print(n, base); println(); } |
| 183 | +void WebSocketSerial::println(double n, int digits) { print(n, digits); println(); } |
| 184 | + |
| 185 | +// Private Methods |
| 186 | + |
| 187 | +void WebSocketSerial::printNumber(unsigned long n, uint8_t base) { |
| 188 | + if (n) { |
| 189 | + unsigned char buf[8 * sizeof(long)]; // Enough space for base 2 |
| 190 | + int8_t i = 0; |
| 191 | + while (n) { |
| 192 | + buf[i++] = n % base; |
| 193 | + n /= base; |
| 194 | + } |
| 195 | + while (i--) |
| 196 | + print((char)(buf[i] + (buf[i] < 10 ? '0' : 'A' - 10))); |
| 197 | + } |
| 198 | + else |
| 199 | + print('0'); |
| 200 | +} |
| 201 | + |
| 202 | +void WebSocketSerial::printFloat(double number, uint8_t digits) { |
| 203 | + // Handle negative numbers |
| 204 | + if (number < 0.0) { print('-'); number = -number; } |
| 205 | + |
| 206 | + // Round correctly so that print(1.999, 2) prints as "2.00" |
| 207 | + // Use a lookup table for performance |
| 208 | + constexpr double rounds[] = { 0.5, 0.05, 0.005, 0.0005, 0.00005, 0.000005, 0.0000005, 0.00000005 }; |
| 209 | + number += rounds[digits]; |
| 210 | + |
| 211 | + //number += pow(10, -(digits + 1)); // slower single-line equivalent |
| 212 | + |
| 213 | + // Extract the integer part of the number and print it |
| 214 | + unsigned long int_part = (unsigned long)number; |
| 215 | + print(int_part); |
| 216 | + |
| 217 | + // Print the decimal point, but only if there are digits beyond |
| 218 | + double remainder = number - (double)int_part; |
| 219 | + if (digits) { |
| 220 | + print('.'); |
| 221 | + // Extract digits from the remainder one at a time |
| 222 | + while (digits--) { |
| 223 | + remainder *= 10.0; |
| 224 | + const int toPrint = int(remainder); |
| 225 | + print(toPrint); |
| 226 | + remainder -= toPrint; |
| 227 | + } |
| 228 | + } |
| 229 | +} |
| 230 | + |
| 231 | +#endif // WIFISUPPORT |
| 232 | +#endif // ARDUINO_ARCH_ESP32 |
0 commit comments