bdring / FluidNC

The next generation of motion control firmware
Other
1.56k stars 379 forks source link

Feature: Ethernet connectivity to upload files and use Web-UI. #1023

Open romulie opened 1 year ago

romulie commented 1 year ago

Machine Context

The main goal is:

to have the possibility to upload job files using Ethernet (not WiFi because of interference issues and long distances) and start them on grbl_esp32. The machine is planned to use 3 axis.

Yes, I have read about the plans to add the Ethernet connectivity to grbl_esp32 in some distant future. I tried it myself but with no success yet.

Feature Description

The user should be able to connect an Ethernet shield to ESP32 controller and use web-UI just as with current WiFi connectivity.

Other Approaches

I’m not a very experienced programmer and I don’t fully understand the TCP\IP stack etc and internal guts of grbl_esp32 code. I have made some tries to have the feature working:

I connected SPI Ethernet shield based on W5500 to esp32. I used 10cm short wires Used the library suitable for the W5500 Configured Ethernet using Ethernet.init(W5500_CS_PIN) and Ethernet.begin(mac, IP) method BEFORE and AFTER the grbl_init() function in setup(); Made some manipulations with the library since there are some problems in it (example sketches for the Ethernet server with W5500 just didn’t work) I noticed problems to start Ethernet when the SD-card reader is on the same SPI despite the separate chip-select pins used for card-reader and Ethernet shield. I Read about problems with W5500 and SPIFFS on ESP32 and some other stuff… I have a ENC28J60 Ethernet board and it worked on my other project with ESP32 (not grbl) just fine but it requires newer ESP32-board version in ArduinoIDE (and grbl_esp32 requires the OLDER one…). So I’m running out of ideas and need some tips.

I would be grateful for:

The general outline of the problems to have the Ethernet connectivity added to the existing code: Is this a hardware or a software problem? Is this an architectural problem to insert the Ethernet functionality into existing code without breaking it apart? Because I don’t fully understand the logic and sequencing of the events in grbl concerning network connections, clients, configurations etc, could someone describe me the general flow of events in grbl_esp32. Steps to be taken to solve the task. Would it be much simpler to leave only Ethernet connectivity and discard all WiFi features for my local version of grbl_esp32?

Thank you in advance guys.

How I Can Help

I have plans to implement this functionality if I manage. So it will be available for other users.

BTW: I managed to add I2C OLED Display and simple joystick control to grbl_esp32 using some features of FreeRTOS and manipulations with guts of the existing code. Sure enough my implementation is not a good one and needs lots of refinements. But it somehow works and may be helpful for the people who want to have the features of this kind: https://github.com/romulie/ESP32_grbl_OLED_and_Joystick

MitchBradley commented 1 year ago

There are several challenges here.

  1. The first decision point is whether to use a controller-with-TCP-stack like W5500, or a MAC-only one like ENC28J60 (or you could bypass the W5500's TCP stack and use it in MAC mode). In TCP mode, you would have to hack several components of the Arduino Framework to make them work on top of the device's TCP stack instead of on top of the Arduino Framework WiFi class that in turn sits on top of the ESP-IDF/lwip TCP stack (but it might be possible to do all of the changes in that WiFi class). There is a structural problem with the Arduino Framework in that it was optimized for directly using WiFi instead of using the abstract networking approach that is common everywhere else. Conversely, in MAC mode, what you would do is to hook the Ethernet chip driver into the ESP-IDF/lwip stack way down at the netif level, and then everything above that level would "just work" for the most part. That is the approach that I used for adding W5500 support to the ESP8266 version of my "cforth" system. The code for that can be found at https://github.com/MitchBradley/cforth/tree/master/src/app/esp8266 in files whose names begin with w5500.
  2. Note that neither of those approaches involves much modification to the FluidNC code itself, apart from not bothering to do the WiFi connection dance. It is all about hooking into the underlying stack, either at the Arduino Framework level or the lwip level.
  3. Regardless, you are likely to run into tricky issues with sharing the SPI bus. In principle, you can share an SPI bus simply by switching the chip select lines. In practice, there are considerations that make it not so simple. One is the fact that network packets can arrive at any time, so you must be careful when you talk to the Ethernet chip, so as not to interfere with the other main user of the SPI bus which is the SD card. The other possible user of the SPI bus is some TMC driver chips. They are particularly troublesome because we use an Arduino Framework TMCStepper driver that has a cumbersome software API for controlling the SPI chip select. Getting the TMCStepper chip select coordinated with the SD card SPI chip select took a lot of head scratching and we got it wrong many times before finally getting it right. Adding an Ethernet chip into the mix is likely to reopen than painful wound.
  4. If we consider this Ethernet feature to become a standard part of the FluidNC code base, I expect that the support problem would be severe. We support many commercially-available controller boards, none of which has an onboard Ethernet adapter, so there is no "standard hardware" to use as the primary "known to work" reference. Therefore, every instance of suitable hardware will be some custom thing cobbled together by (likely) a novice, who probably does not really understand how any of it works - neither the hardware at the SPI or Ethernet level, nor the networking stack at any of the many different levels, nor the Arduino Framework, nor the FluidNC code. So the likely result is that everybody who tries to use it will run into multiple problems that they are incapable of debugging by themselves. And then they will ask for help. The only person who is likely to be able to help is Yours Truly, being the only person associated with FluidNC who has a deep background in networking hardware and software. If they subsequently donate to the FluidNC project, they might dig deep and contribute between 5 and 20 dollars.

Bottom line - this is eminently do-able, but it requires knowledge at many levels of a complex software stack containing components from many different sources that often play together poorly. And once you get it working once, the support implications are daunting.

romulie commented 1 year ago

Thank you, Sir, for your work and detailed answer! There are things to think about...

pionex commented 1 year ago

The esp32 has a built in ethernet MAC but the RMII interface to a PHY uses 8 IO. I just purchased an olimex esp32 gateway board that has ethernet, and I am planning to see what is involved in having that work in addition to the wifi.

From the looks of it, there are 2 recommended PHY chips, and one has better pricing and availability. The code doesn't look dramatically different.

https://espressif-docs.readthedocs-hosted.com/projects/arduino-esp32/en/latest/api/ethernet.html

MitchBradley commented 1 year ago

FluidNC does not support the ESP32 MAC because it uses so many pins. If you take away 8 pins, you do not have enough left for any reasonable CNC application. Furthermore, the electrical layout for Ethernet PHYs is critical, so the design would have to be done very carefully, with advanced knowledge of signal integrity techniques. I say that as the designer of Sun Microsystems' first 10 Mbit Ethernet board, going back to the early '80s.

pionex commented 1 year ago

I've done a basic pinout, and it looks to me like there is enough IO for a typical 3 axis machine with individual axis home switches, probe, spindle, laser, sd card, and uart in addition to the ethernet. It's no 6-pack, but it seems at least a usable as a DLC32 with external drivers. The UART could be removed and replaced with a jtag header or a FT232H to free up a few more IO. See below for a theoretical pinout if you are interested.

That said, I was just trying to find a solution that would easily integrate with the existing stack and avoid the problems of trying to support myriad SPI ethernet devices. If there is some consensus around a module, I'd be interested in looking into integrating it. I have spent a fair bit of time on getting lwip working on various hardware.

0 UART RTS/DTR? 1 UART 0 TX 2 Spindle Relay 3 UART 0 RX 4 0-10V Spindle 5 PHY_PWR (Optional?) 12 MISO / JTAG TDI 13 MOSI / JTAG TCK 14 SCLK / JTAG TMS 15 CS_SD / JTAG TDO 16 Laser PWM 17 EMAC_CLK_OUT 18 MDIO(RMII) 19 EMAC_TXD0 21 EMAC_TX_EN 22 EMAC_TXD1 23 MDC 25 EMAC_RXD0 26 EMAC_RXD1 27 EAMC_RX_CRS_DV 34 Probe 35 Home X 36 Home Y 39 Home Z

MitchBradley commented 1 year ago

What about step and direction for the 3 axes?

pionex commented 1 year ago

ah. I did have the i2s for the shift register in there, but accidentally deleted it - so in that case I would just get rid of the UART. I'm still checking if that PHY power is necessary.

MitchBradley commented 1 year ago

Fine tuning the pinout for a limited number of axes is a bad tradeoff, as it will result in a point solution that can only apply to limited use cases. The whole point of developing the I2S approach was to break out of that limitation. Prior to that, Bart had to make lots of different boards with pinouts tuned for different use cases - number of axes, number of inputs and misc outputs, etc.

Re the module question, there seem to be two viable alternatives - W5500 and ENC28J60. I vote for W5500 since it can handle 100 BaseT. I don't like to bog down my network with 10BaseT devices. You can get at the raw packets so it is not necessary to use the built in TCP stack, although I suppose it might be possible to splice it into Arduino WiFi class in place of lwip, thus saving memory.

pionex commented 1 year ago

I have tested adding ethernet to FluidNC using the internal ESP32 MAC. The Arduino drivers for the internal MAC + LAN8720 PHY are already developed and in the framework, so it is mostly a matter of calling a single function (ETH.begin(...)) with the correct parameters for the hardware.

There appears to be an unfinished effort in the Arduino code to add support for the DM9051 and there is a raw MAC driver for the W5500 in the ESP32 IDF code in the ESP32 Arduino framework. It doesn't seem like it should be too difficult to connect the W5500 to the lwip stack on the ESP32. Using the internal TCP stack in the W5500 would take a bit more effort. I have a W5500 on order, so I will experiment with it when it arrives.

MitchBradley commented 1 year ago

ESP-IDF has this in Kconfig

    menuconfig ETH_USE_SPI_ETHERNET
        bool "Support SPI to Ethernet Module"
        default y
        select ETH_ENABLED
        help
            ESP-IDF can also support some SPI-Ethernet modules.

    if ETH_USE_SPI_ETHERNET
        config ETH_SPI_ETHERNET_DM9051
            bool "Use DM9051"
            help
                DM9051 is a fast Ethernet controller with an SPI interface.
                It's also integrated with a 10/100M PHY and MAC.
                Select this to enable DM9051 driver.

        config ETH_SPI_ETHERNET_W5500
            bool "Use W5500 (MAC RAW)"
            help
                W5500 is a HW TCP/IP embedded Ethernet controller.
                TCP/IP stack, 10/100 Ethernet MAC and PHY are embedded in a single chip.
                However the driver in ESP-IDF only enables the RAW MAC mode,
                making it compatible with the software TCP/IP stack.
                Say yes to enable W5500 driver.

        config ETH_SPI_ETHERNET_KSZ8851SNL
            bool "Use KSZ8851SNL"
            help
                The KSZ8851SNL is a single-chip Fast Ethernet controller consisting of
                a 10/100 physical layer transceiver (PHY), a MAC, and a Serial Peripheral Interface (SPI).
                Select this to enable KSZ8851SNL driver.
    endif # ETH_USE_SPI_ETHERNET

I don't know if it is possible to enable the various ones at the same time, nor do I know what enabling them does to the system FLASH and RAM usage. This could become a configuration problem, considering the fact that the Arduino framework has precompiled libraries of ESP-IDF with a fixed sdkconfig file.

pionex commented 1 year ago

From what I can tell, those options are all enabled as it is in both the current and newer versions of the platform.
See ~.platformio\packages\framework-arduinoespressif32\tools\sdk\esp32\dio_qspi\include\sdkconfig.h and ~.platformio\packages\framework-arduinoespressif32\tools\sdk\esp32\sdkconfig"

In esp_eth_phy.h and esp_eth_mac.h, there are the function declarations for creating a W5500 MAC and PHY. Ideally, the ETH.h/ETH.cpp class at ~.platformio\packages\framework-arduinoespressif32\libraries\Ethernet\src\ETH.cpp

would be updated to support the W5500. I believe that is all that is missing. When I get hardware, I will test with a modified version of this class and ensure that it works ok.

pionex commented 1 year ago

My W5500 arrived today. I found some initialization code elsewhere, and by adding the code below, the ethernet works well -- seemingly much more responsively than over wifi. I still need to refactor the code so that it works with the configurable interface in FluidNC and to initialize things in the correct place during startup. I also need to test to see how well it works if the SPI interface is shared with the SD card, but I need to find a different dev board somwhere.

bool setupW5500() { WiFi.begin();

tcpip_adapter_set_default_eth_handlers();

// Initialize TCP/IP network interface ESP_ERROR_CHECK(esp_netif_init()); esp_netif_config_t cfg = ESP_NETIF_DEFAULT_ETH(); esp_netif_t *eth_netif = esp_netif_new(&cfg);

esp_eth_mac_t eth_mac = NULL; esp_eth_phy_t eth_phy = NULL; gpio_install_isr_service(0);

spi_bus_config_t buscfg = { .mosi_io_num = 32, // MOSI .miso_io_num = 33, // MISO .sclk_io_num = 17, // SCLK .quadwp_io_num = -1, .quadhd_io_num = -1, }; ESP_ERROR_CHECK(spi_bus_initialize(SPI3_HOST, &buscfg, 1));

spi_device_handle_t spi_handle = NULL; spi_device_interface_config_t devcfg = { .command_bits = 16, // Address phase in W5500 SPI frame .address_bits = 8, // Control phase in W5500 SPI frame .mode = 0, .clock_speed_hz = 12 1000 1000, .spics_io_num = 16, // SCS .queue_size = 20 }; ESP_ERROR_CHECK(spi_bus_add_device(SPI3_HOST, &devcfg, &spi_handle));

/ W5500 ethernet driver uses spi driver / eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(spi_handle); w5500_config.int_gpio_num = 34; // INT

eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG(); eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); phy_config.reset_gpio_num = -1;

eth_mac = esp_eth_mac_new_w5500(&w5500_config, &mac_config); if(eth_mac == NULL){ log_e("esp_eth_mac_new_esp32 failed"); return false; }

eth_phy = esp_eth_phy_new_w5500(&phy_config); if(eth_phy == NULL){ log_e("esp_eth_phy_new failed"); return false; }

esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(eth_mac, eth_phy); esp_eth_handle_t eth_handle = NULL; ESP_ERROR_CHECK(esp_eth_driver_install(&eth_config, &eth_handle));

uint8_t macArr[] = { 0x02, 0x00, 0x00, 0x12, 0x34, 0x56 }; ESP_ERROR_CHECK(esp_eth_ioctl(eth_handle, ETH_CMD_S_MAC_ADDR, macArr));

/ Attach Ethernet driver to TCP/IP stack / ESP_ERROR_CHECK(esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handle)));

/ Start Ethernet driver state machine / ESP_ERROR_CHECK(esp_eth_start(eth_handle));

return true; }

pionex commented 1 year ago

Here is an initial draft of support for the W5500. I have yet to test with the SD Card. (https://github.com/pionex/FluidNC-Ethernet/tree/W5500)

I am now trying to understand how the spi bus is used in the Trinamic and sdcard drivers without using spi_master

justinclift commented 10 months ago

@pionex How's your progress on this looking? :smile: