Browse Source

Add ESP32 WiFi interface (#11209)

Hadrien Jouet 6 years ago
parent
commit
0278ad0a6d

+ 1 - 0
.travis.yml

@@ -23,6 +23,7 @@ env:
   #- TEST_PLATFORM="STM32F1"
   - TEST_PLATFORM="teensy35"
   - TEST_PLATFORM="linux_native"
+  - TEST_PLATFORM="esp32"
 
 addons:
   apt:

+ 2 - 0
Marlin/Configuration_adv.h

@@ -2228,6 +2228,8 @@
 #if ENABLED(WIFISUPPORT)
   #define WIFI_SSID "Wifi SSID"
   #define WIFI_PWD  "Wifi Password"
+  //#define WEBSUPPORT        // Start a webserver with auto-discovery
+  //#define OTASUPPORT        // Support over-the-air firmware updates
 #endif
 
 /**

+ 17 - 3
Marlin/src/HAL/HAL_ESP32/HAL.cpp

@@ -34,7 +34,14 @@
 #include "../../inc/MarlinConfigPre.h"
 
 #if ENABLED(WIFISUPPORT)
-  #include "ota.h"
+  #include <ESPAsyncWebServer.h>
+  #include "wifi.h"
+  #if ENABLED(OTASUPPORT)
+    #include "ota.h"
+  #endif
+  #if ENABLED(WEBSUPPORT)
+    #include "web.h"
+  #endif
 #endif
 
 // --------------------------------------------------------------------------
@@ -83,14 +90,21 @@ esp_adc_cal_characteristics_t characteristics;
 
 void HAL_init(void) {
   #if ENABLED(WIFISUPPORT)
-    OTA_init();
+    wifi_init();
+    #if ENABLED(OTASUPPORT)
+      OTA_init();
+    #endif
+    #if ENABLED(WEBSUPPORT)
+      web_init();
+    #endif
+    server.begin();
   #endif
 
   i2s_init();
 }
 
 void HAL_idletask(void) {
-  #if ENABLED(WIFISUPPORT)
+  #if ENABLED(OTASUPPORT)
     OTA_handle();
   #endif
 }

+ 4 - 1
Marlin/src/HAL/HAL_ESP32/HAL.h

@@ -47,14 +47,17 @@
 
 #include "HAL_timers_ESP32.h"
 
+#include "WebSocketSerial.h"
+
 // --------------------------------------------------------------------------
 // Defines
 // --------------------------------------------------------------------------
 
 extern portMUX_TYPE spinlock;
 
-#define NUM_SERIAL 1
+#define NUM_SERIAL 2
 #define MYSERIAL0 Serial
+#define MYSERIAL1 webSocketSerial
 
 #define CRITICAL_SECTION_START portENTER_CRITICAL(&spinlock)
 #define CRITICAL_SECTION_END   portEXIT_CRITICAL(&spinlock)

+ 232 - 0
Marlin/src/HAL/HAL_ESP32/WebSocketSerial.cpp

@@ -0,0 +1,232 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ *
+ * Based on Sprinter and grbl.
+ * Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#ifdef ARDUINO_ARCH_ESP32
+
+#include "../../inc/MarlinConfig.h"
+
+#if ENABLED(WIFISUPPORT)
+
+#include "WebSocketSerial.h"
+#include "wifi.h"
+
+#include <AsyncTCP.h>
+#include <ESPAsyncWebServer.h>
+
+struct ring_buffer_r {
+  unsigned char buffer[RX_BUFFER_SIZE];
+  volatile ring_buffer_pos_t head, tail;
+};
+
+struct ring_buffer_t {
+  unsigned char buffer[256];
+  volatile uint8_t head, tail;
+};
+
+ring_buffer_r rx_buffer = { { 0 }, 0, 0 };
+ring_buffer_t tx_buffer = { { 0 }, 0, 0 };
+
+static bool _written;
+
+#if ENABLED(EMERGENCY_PARSER)
+  static EmergencyParser::State emergency_state; // = EP_RESET
+#endif
+
+AsyncWebSocket ws("/ws"); // access at ws://[esp ip]/ws
+
+FORCE_INLINE int next_rx_index(const int i) { return (ring_buffer_pos_t)(i + 1) & (ring_buffer_pos_t)(RX_BUFFER_SIZE - 1); }
+FORCE_INLINE int next_tx_index(const int i) { return (ring_buffer_pos_t)(i + 1) & (ring_buffer_pos_t)(TX_BUFFER_SIZE - 1); }
+
+static void addToBuffer(uint8_t * const data, const size_t len) {
+  for (size_t i = 0; i < len; i++) {
+    ring_buffer_pos_t h = rx_buffer.head;
+    const ring_buffer_pos_t t = rx_buffer.tail, n = next_rx_index(h);
+
+    if (n != t) { rx_buffer.buffer[h] = data[i]; h = n; }
+
+    // TODO: buffer is full, handle?
+
+    rx_buffer.head = h;
+  }
+}
+
+// Handle WebSocket event
+static void onEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len) {
+  switch (type) {
+    case WS_EVT_CONNECT: client->ping(); break; // client connected
+    case WS_EVT_DISCONNECT:                     // client disconnected
+    case WS_EVT_ERROR:                          // error was received from the other end
+    case WS_EVT_PONG: break;                    // pong message was received (in response to a ping request maybe)
+    case WS_EVT_DATA: {                         // data packet
+      AwsFrameInfo * info = (AwsFrameInfo*)arg;
+      if (info->opcode == WS_TEXT || info->message_opcode == WS_TEXT)
+        addToBuffer(data, len);
+    }
+  }
+}
+
+// Public Methods
+void WebSocketSerial::begin(const long baud_setting) {
+  ws.onEvent(onEvent);
+  server.addHandler(&ws); // attach AsyncWebSocket
+}
+
+void WebSocketSerial::end() { }
+
+int WebSocketSerial::peek(void) {
+  const int v = rx_buffer.head == rx_buffer.tail ? -1 : rx_buffer.buffer[rx_buffer.tail];
+  return v;
+}
+
+int WebSocketSerial::read(void) {
+  const ring_buffer_pos_t h = rx_buffer.head, t = rx_buffer.tail;
+  if (h == t) return -1;  // Nothing to read? Return now
+
+  const int v = rx_buffer.buffer[t];
+
+  rx_buffer.tail = (ring_buffer_pos_t)(t + 1) & (RX_BUFFER_SIZE - 1); // Advance tail
+
+  return v;
+}
+
+bool WebSocketSerial::available(void) {
+  const ring_buffer_pos_t h = rx_buffer.head, t = rx_buffer.tail;
+  return (ring_buffer_pos_t)(RX_BUFFER_SIZE + h - t) & (RX_BUFFER_SIZE - 1);
+}
+
+void WebSocketSerial::flush(void) {
+  ws.textAll("flush");
+  rx_buffer.tail = rx_buffer.head;
+}
+
+#if TX_BUFFER_SIZE
+
+  void WebSocketSerial::write(const uint8_t c) {
+    _written = true;
+
+    const uint8_t i = (tx_buffer.head + 1) & (TX_BUFFER_SIZE - 1);
+
+    // Store new char. head is always safe to move
+    tx_buffer.buffer[tx_buffer.head] = c;
+    tx_buffer.head = i;
+
+    if (c == '\n') {
+      ws.textAll(tx_buffer.buffer, tx_buffer.head);
+      tx_buffer.head = 0;
+    }
+  }
+
+  void WebSocketSerial::flushTx(void) {
+    ws.textAll("flushTx");
+    if (!_written) return;
+  }
+
+#else
+
+ //void WebSocketSerial::write(const uint8_t c) { _written = true; }
+ //void WebSocketSerial::flushTx(void) { if (!_written) return; }
+
+#endif
+
+/**
+ * Imports from print.h
+ */
+
+void WebSocketSerial::print(char c, int base) { print((long)c, base); }
+void WebSocketSerial::print(unsigned char b, int base) { print((unsigned long)b, base); }
+void WebSocketSerial::print(int n, int base) { print((long)n, base); }
+void WebSocketSerial::print(unsigned int n, int base) { print((unsigned long)n, base); }
+void WebSocketSerial::print(long n, int base) {
+  if (base == 0)
+    write(n);
+  else if (base == 10) {
+    if (n < 0) { print('-'); n = -n; }
+    printNumber(n, 10);
+  }
+  else
+    printNumber(n, base);
+}
+
+void WebSocketSerial::print(unsigned long n, int base) {
+  if (base == 0) write(n); else printNumber(n, base);
+}
+
+void WebSocketSerial::print(double n, int digits)         { printFloat(n, digits); }
+
+void WebSocketSerial::println(void)                       { print('\r'); print('\n'); }
+void WebSocketSerial::println(const String& s)            { print(s); println(); }
+void WebSocketSerial::println(const char c[])             { print(c); println(); }
+void WebSocketSerial::println(char c, int base)           { print(c, base); println(); }
+void WebSocketSerial::println(unsigned char b, int base)  { print(b, base); println(); }
+void WebSocketSerial::println(int n, int base)            { print(n, base); println(); }
+void WebSocketSerial::println(unsigned int n, int base)   { print(n, base); println(); }
+void WebSocketSerial::println(long n, int base)           { print(n, base); println(); }
+void WebSocketSerial::println(unsigned long n, int base)  { print(n, base); println(); }
+void WebSocketSerial::println(double n, int digits)       { print(n, digits); println(); }
+
+// Private Methods
+
+void WebSocketSerial::printNumber(unsigned long n, uint8_t base) {
+  if (n) {
+    unsigned char buf[8 * sizeof(long)]; // Enough space for base 2
+    int8_t i = 0;
+    while (n) {
+      buf[i++] = n % base;
+      n /= base;
+    }
+    while (i--)
+      print((char)(buf[i] + (buf[i] < 10 ? '0' : 'A' - 10)));
+  }
+  else
+    print('0');
+}
+
+void WebSocketSerial::printFloat(double number, uint8_t digits) {
+  // Handle negative numbers
+  if (number < 0.0) { print('-'); number = -number; }
+
+  // Round correctly so that print(1.999, 2) prints as "2.00"
+  // Use a lookup table for performance
+  constexpr double rounds[] = { 0.5, 0.05, 0.005, 0.0005, 0.00005, 0.000005, 0.0000005, 0.00000005 };
+  number += rounds[digits];
+
+  //number += pow(10, -(digits + 1)); // slower single-line equivalent
+
+  // Extract the integer part of the number and print it
+  unsigned long int_part = (unsigned long)number;
+  print(int_part);
+
+  // Print the decimal point, but only if there are digits beyond
+  double remainder = number - (double)int_part;
+  if (digits) {
+    print('.');
+    // Extract digits from the remainder one at a time
+    while (digits--) {
+      remainder *= 10.0;
+      const int toPrint = int(remainder);
+      print(toPrint);
+      remainder -= toPrint;
+    }
+  }
+}
+
+#endif // WIFISUPPORT
+#endif // ARDUINO_ARCH_ESP32

+ 99 - 0
Marlin/src/HAL/HAL_ESP32/WebSocketSerial.h

@@ -0,0 +1,99 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ *
+ * Based on Sprinter and grbl.
+ * Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#pragma once
+
+#include "../../inc/MarlinConfig.h"
+
+#include <WString.h>
+
+#define DEC 10
+#define HEX 16
+#define OCT 8
+#define BIN 2
+
+#ifndef RX_BUFFER_SIZE
+  #define RX_BUFFER_SIZE 128
+#endif
+#ifndef TX_BUFFER_SIZE
+  #define TX_BUFFER_SIZE 32
+#endif
+#if TX_BUFFER_SIZE <= 0
+  #error "TX_BUFFER_SIZE is required for the WebSocket."
+#endif
+
+#if RX_BUFFER_SIZE > 256
+  typedef uint16_t ring_buffer_pos_t;
+#else
+  typedef uint8_t ring_buffer_pos_t;
+#endif
+
+class WebSocketSerial {
+public:
+  WebSocketSerial() {};
+  static void begin(const long);
+  static void end();
+  static int peek(void);
+  static int read(void);
+  static void flush(void);
+  static void flushTx(void);
+  static bool available(void);
+  static void write(const uint8_t c);
+
+  #if ENABLED(SERIAL_STATS_DROPPED_RX)
+    FORCE_INLINE static uint32_t dropped() { return 0; }
+  #endif
+
+  #if ENABLED(SERIAL_STATS_MAX_RX_QUEUED)
+    FORCE_INLINE static int rxMaxEnqueued() { return 0; }
+  #endif
+
+  FORCE_INLINE static void write(const char* str) { while (*str) write(*str++); }
+  FORCE_INLINE static void write(const uint8_t* buffer, size_t size) { while (size--) write(*buffer++); }
+  FORCE_INLINE static void print(const String& s) { for (int i = 0; i < (int)s.length(); i++) write(s[i]); }
+  FORCE_INLINE static void print(const char* str) { write(str); }
+
+  static void print(char, int = 0);
+  static void print(unsigned char, int = 0);
+  static void print(int, int = DEC);
+  static void print(unsigned int, int = DEC);
+  static void print(long, int = DEC);
+  static void print(unsigned long, int = DEC);
+  static void print(double, int = 2);
+
+  static void println(const String& s);
+  static void println(const char[]);
+  static void println(char, int = 0);
+  static void println(unsigned char, int = 0);
+  static void println(int, int = DEC);
+  static void println(unsigned int, int = DEC);
+  static void println(long, int = DEC);
+  static void println(unsigned long, int = DEC);
+  static void println(double, int = 2);
+  static void println(void);
+  operator bool() { return true; }
+
+private:
+  static void printNumber(unsigned long, const uint8_t);
+  static void printFloat(double, uint8_t);
+};
+
+extern WebSocketSerial webSocketSerial;

+ 2 - 11
Marlin/src/HAL/HAL_ESP32/ota.cpp

@@ -21,7 +21,7 @@
 
 #include "../../inc/MarlinConfigPre.h"
 
-#if ENABLED(WIFISUPPORT)
+#if ENABLED(OTASUPPORT)
 
 #include <WiFi.h>
 #include <ESPmDNS.h>
@@ -30,15 +30,6 @@
 #include "driver/timer.h"
 
 void OTA_init() {
-  WiFi.mode(WIFI_STA);
-  WiFi.begin(WIFI_SSID, WIFI_PWD);
-
-  while (WiFi.waitForConnectResult() != WL_CONNECTED) {
-    Serial.println("Connection Failed! Rebooting...");
-    delay(5000);
-    ESP.restart();
-  }
-
   ArduinoOTA
     .onStart([]() {
       timer_pause(TIMER_GROUP_0, TIMER_0);
@@ -76,6 +67,6 @@ void OTA_handle() {
   ArduinoOTA.handle();
 }
 
-#endif // WIFISUPPORT
+#endif // OTASUPPORT
 
 #endif // ARDUINO_ARCH_ESP32

+ 49 - 0
Marlin/src/HAL/HAL_ESP32/web.cpp

@@ -0,0 +1,49 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ * Copyright (c) 2016 Bob Cousins bobcousins42@googlemail.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef ARDUINO_ARCH_ESP32
+
+#include "../../inc/MarlinConfigPre.h"
+
+#if ENABLED(WEBSUPPORT)
+
+#include "../../core/serial.h"
+
+#include "FS.h"
+#include "SPIFFS.h"
+#include "wifi.h"
+
+AsyncEventSource events("/events"); // event source (Server-Sent events)
+
+void onNotFound(AsyncWebServerRequest *request){
+  request->send(404);
+}
+
+void web_init() {
+  server.addHandler(&events);       // attach AsyncEventSource
+  if (SPIFFS.begin()) {
+    server.serveStatic("/", SPIFFS, "/www").setDefaultFile("index.html");
+    server.onNotFound(onNotFound);
+  }
+  else
+    SERIAL_ECHO_MSG("SPIFFS Mount Failed");
+}
+
+#endif // WEBSUPPORT
+#endif // ARDUINO_ARCH_ESP32

+ 21 - 0
Marlin/src/HAL/HAL_ESP32/web.h

@@ -0,0 +1,21 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ * Copyright (c) 2016 Bob Cousins bobcousins42@googlemail.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+
+void web_init();

+ 55 - 0
Marlin/src/HAL/HAL_ESP32/wifi.cpp

@@ -0,0 +1,55 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ * Copyright (c) 2016 Bob Cousins bobcousins42@googlemail.com
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef ARDUINO_ARCH_ESP32
+
+#include "../../inc/MarlinConfigPre.h"
+
+#if ENABLED(WIFISUPPORT)
+
+#include <WiFi.h>
+#include <ESPmDNS.h>
+#include <ESPAsyncWebServer.h>
+#include "wifi.h"
+
+AsyncWebServer server(80);
+
+#ifndef WIFI_HOSTNAME
+  #define WIFI_HOSTNAME DEFAULT_WIFI_HOSTNAME
+#endif
+
+void wifi_init() {
+  WiFi.mode(WIFI_STA);
+  WiFi.begin(WIFI_SSID, WIFI_PWD);
+
+  while (WiFi.waitForConnectResult() != WL_CONNECTED) {
+    delay(5000);
+    ESP.restart();
+  }
+
+  delay(10);
+
+  // Loop forever (watchdog kill) on failure
+  if (!MDNS.begin(WIFI_HOSTNAME)) for(;;) delay(5000);
+
+  MDNS.addService("http", "tcp", 80);
+}
+
+#endif // WIFISUPPORT
+#endif // ARDUINO_ARCH_ESP32

Some files were not shown because too many files changed in this diff