diff --git a/FluidNC/esp32/delay_usecs.cpp b/FluidNC/esp32/delay_usecs.cpp new file mode 100644 index 000000000..984d086cf --- /dev/null +++ b/FluidNC/esp32/delay_usecs.cpp @@ -0,0 +1,59 @@ +#include "Driver/delay_usecs.h" + +#include // IRAM_ATTR +#include + +#include + +#if CONFIG_IDF_TARGET_ESP32 +# include "esp32/clk.h" +#elif CONFIG_IDF_TARGET_ESP32S2 +# include "esp32s2/clk.h" +#elif CONFIG_IDF_TARGET_ESP32S3 +# include "esp32s3/clk.h" +#elif CONFIG_IDF_TARGET_ESP32C3 +# include "esp32c3/clk.h" +#elif CONFIG_IDF_TARGET_ESP32H2 +# include "esp32h2/clk.h" +#endif + +uint32_t ticks_per_us; +int esp_clk_cpu_freq(void); + +void timing_init() { + ticks_per_us = esp_clk_cpu_freq() / 1000000; +} + +void IRAM_ATTR delay_us(int32_t us) { + spinUntil(usToEndTicks(us)); +} + +int32_t IRAM_ATTR usToCpuTicks(int32_t us) { + return us * ticks_per_us; +} + +int32_t IRAM_ATTR usToEndTicks(int32_t us) { + return getCpuTicks() + usToCpuTicks(us); +} + +// At the usual ESP32 clock rate of 240MHz, the range of this is +// just under 18 seconds, but it really should be used only for +// short delays up to a few tens of microseconds. + +void IRAM_ATTR spinUntil(int32_t endTicks) { + while ((getCpuTicks() - endTicks) < 0) { +#ifdef ESP32 + asm volatile("nop"); +#endif + } +} + +// Short delays measured using the CPU cycle counter. There is a ROM +// routine "esp_delay_us(us)" that almost does what what we need, +// except that it is in ROM and thus dodgy for use from ISRs. We +// duplicate the esp_delay_us() here, but placed in IRAM, inlined, +// and factored so it can be used in different ways. + +int32_t IRAM_ATTR getCpuTicks() { + return XTHAL_GET_CCOUNT(); +} diff --git a/FluidNC/esp32/gpio.cpp b/FluidNC/esp32/gpio.cpp index 21975ccc6..c71053e7c 100644 --- a/FluidNC/esp32/gpio.cpp +++ b/FluidNC/esp32/gpio.cpp @@ -38,7 +38,6 @@ void gpio_mode(pinnum_t pin, bool input, bool output, bool pullup, bool pulldown if (opendrain) { conf.mode = (gpio_mode_t)((int)conf.mode | GPIO_MODE_DEF_OD); } - log_debug("gpio conf " << conf.pull_up_en << " " << pin); gpio_config(&conf); } void IRAM_ATTR gpio_set_interrupt_type(pinnum_t pin, int mode) { diff --git a/FluidNC/esp32/sdspi.cpp b/FluidNC/esp32/sdspi.cpp index 079e4293b..dc507702f 100644 --- a/FluidNC/esp32/sdspi.cpp +++ b/FluidNC/esp32/sdspi.cpp @@ -12,7 +12,7 @@ #define CHECK_EXECUTE_RESULT(err, str) \ do { \ if ((err) != ESP_OK) { \ - log_error(str << " code 0x" << String(err, 16)); \ + log_error(str << " code 0x" << to_hex(err)); \ goto cleanup; \ } \ } while (0) @@ -78,10 +78,7 @@ bool sd_init_slot(uint32_t freq_hz, int cs_pin, int cd_pin, int wp_pin) { sdspi_device_config_t slot_config; - // If freq_hz is 0, use the system default - if (freq_hz) { - host_config.max_freq_khz = freq_hz / 1000; - } + host_config.max_freq_khz = freq_hz / 1000; err = host_config.init(); CHECK_EXECUTE_RESULT(err, "host init failed"); @@ -104,7 +101,6 @@ bool sd_init_slot(uint32_t freq_hz, int cs_pin, int cd_pin, int wp_pin) { // If you do it only once below, the attempt to change it seems to // be ignored, and you get 20 MHz regardless of what you ask for. if (freq_hz) { - printf("hz = %d\n", freq_hz); err = sdspi_host_set_card_clk(host_config.slot, freq_hz / 1000); CHECK_EXECUTE_RESULT(err, "set slot clock speed failed"); } diff --git a/FluidNC/esp32/spi.cpp b/FluidNC/esp32/spi.cpp index 718735d44..afdd54d72 100644 --- a/FluidNC/esp32/spi.cpp +++ b/FluidNC/esp32/spi.cpp @@ -3,10 +3,15 @@ #include "Driver/spi.h" -#include #include "driver/spi_common.h" #include "src/Config.h" +#include + +#ifdef CONFIG_IDF_TARGET_ESP32S3 +# define HSPI_HOST SPI2_HOST +#endif + bool spi_init_bus(pinnum_t sck_pin, pinnum_t miso_pin, pinnum_t mosi_pin, bool dma) { // Start the SPI bus with the pins defined here. Once it has been started, // those pins "stick" and subsequent attempts to restart it with defaults @@ -22,7 +27,7 @@ bool spi_init_bus(pinnum_t sck_pin, pinnum_t miso_pin, pinnum_t mosi_pin, bool d }; // Depends on the chip variant - return !spi_bus_initialize(HSPI_HOST, &bus_cfg, dma ? SPI_DMA_CH1 : SPI_DMA_DISABLED); + return !spi_bus_initialize(HSPI_HOST, &bus_cfg, dma ? SPI_DMA_CH_AUTO : SPI_DMA_DISABLED); } void spi_deinit_bus() { diff --git a/FluidNC/esp32/tmc_spi.cpp b/FluidNC/esp32/tmc_spi.cpp index f03d10c41..d0b700d4f 100644 --- a/FluidNC/esp32/tmc_spi.cpp +++ b/FluidNC/esp32/tmc_spi.cpp @@ -36,91 +36,14 @@ // with SCK, MOSI, and MISO pins assigned, via SPIBus.cpp #include "src/Config.h" +#include "esp32/tmc_spi_support.h" #include // https://github.com/teemuatlut/TMCStepper -#include "hal/spi_ll.h" - -spi_dev_t* hw = SPI_LL_GET_HW(HSPI_HOST); - -static spi_ll_clock_val_t clk_reg_val = 0; - -// Establish the SPI bus configuration needed for TMC device access -// This should be done one before every TMC read or write operation, -// to reconfigure the bus from whatever mode the SD card driver used. -static void tmc_spi_bus_setup() { -#if 0 - PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->miso_io_num], PIN_FUNC_GPIO); - gpio_set_direction(bus_config->miso_io_num, GPIO_MODE_INPUT); - gpio_matrix_out(bus_config->miso_io_num, io_signal[host].spiq_out, false, false); - gpio_matrix_in(bus_config->miso_io_num, io_signal[host].spiq_in, false); -#endif - - if (clk_reg_val == 0) { - spi_ll_master_cal_clock(SPI_LL_PERIPH_CLK_FREQ, 2000000, 128, &clk_reg_val); - } - spi_ll_master_set_clock_by_reg(hw, &clk_reg_val); - - spi_ll_master_init(hw); - spi_ll_master_set_mode(hw, 3); - spi_ll_set_half_duplex(hw, false); - - spi_ll_master_set_line_mode(hw, { 1, 1, 1 }); // Single-line transfers; not DIO or QIO -} - -// Perform a full-duplex transfer from out/out_bitlen to in/in_bitlen -// If in_bitlen is 0, the input data will be ignored -static void tmc_spi_transfer_data(uint8_t* out, int out_bitlen, uint8_t* in, int in_bitlen) { - spi_ll_set_mosi_bitlen(hw, out_bitlen); - - spi_ll_set_miso_bitlen(hw, in_bitlen); - - spi_ll_set_addr_bitlen(hw, 0); - spi_ll_set_command_bitlen(hw, 0); - - spi_ll_write_buffer(hw, out, out_bitlen); - spi_ll_enable_mosi(hw, 1); - if (in_bitlen) { - spi_ll_enable_miso(hw, 1); - } - - spi_ll_clear_int_stat(hw); - spi_ll_master_user_start(hw); - while (!spi_ll_usr_is_done(hw)) {} - - spi_ll_read_buffer(hw, in, in_bitlen); // No-op if in_bitlen is 0 -} - -// Do a single 5-byte (reg# + data) access to a TMC register, -// accounting for the number of TMC devices (index) daisy-chained -// before the target device. For reads, this is the first register -// access that latches the register data into the output register. -static void tmc_spi_rw_reg(uint8_t cmd, uint32_t data, int index) { - int before = index > 0 ? index - 1 : 0; - - const size_t packetLen = 5; - size_t dummy_out_bytes = before * packetLen; - size_t total_bytes = (before + 1) * packetLen; - size_t total_bits = total_bytes * 8; - - uint8_t out[total_bytes] = { 0 }; - - // The data always starts at the beginning of the buffer then - // the trailing 0 bytes will push it through the chain to the - // target chip. - out[0] = cmd; - out[1] = data >> 24; - out[2] = data >> 16; - out[3] = data >> 8; - out[4] = data >> 0; - - tmc_spi_transfer_data(out, total_bits, NULL, 0); -} - // Replace the library's weak definition of TMC2130Stepper::write() // This is executed in the object context so it has access to class // data such as the CS pin that switchCSpin() uses void TMC2130Stepper::write(uint8_t reg, uint32_t data) { - log_verbose("TMC reg 0x" << String(reg, 16) << " write 0x" << String(data, 16)); + log_verbose("TMC reg 0x" << to_hex(reg) << " write 0x" << to_hex(data)); tmc_spi_bus_setup(); switchCSpin(0); @@ -165,7 +88,7 @@ uint32_t TMC2130Stepper::read(uint8_t reg) { data += (uint32_t)in[dummy_in_bytes + 4]; switchCSpin(1); - log_verbose("TMC reg 0x" << String(reg, 16) << " read 0x" << String(data, 16) << " status 0x" << String(status, 16)); + log_verbose("TMC reg 0x" << to_hex(reg) << " read 0x" << to_hex(data) << " status 0x" << to_hex(status)); return data; } diff --git a/FluidNC/esp32/tmc_spi_support.c b/FluidNC/esp32/tmc_spi_support.c new file mode 100644 index 000000000..7fcf14e40 --- /dev/null +++ b/FluidNC/esp32/tmc_spi_support.c @@ -0,0 +1,93 @@ +// Copyright (c) 2022 Mitch Bradley +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +// C support routines for tmc_spi.cpp . These routines must be complied +// by a C compiler instead of a C++ compiler because of a problem in the +// ESP32_S3 version of esp_addr.h . It defines a FLAG_ATTR() macro in a +// way that causes compiler error on spi_ll.h + +#include "hal/spi_ll.h" + +#include +#ifdef CONFIG_IDF_TARGET_ESP32S3 +# define HSPI_HOST SPI2_HOST +# define SPI2 GPSPI2 +#endif + +spi_dev_t* hw = &SPI2; + +static spi_ll_clock_val_t clk_reg_val = 0; + +// Establish the SPI bus configuration needed for TMC device access +// This should be done one before every TMC read or write operation, +// to reconfigure the bus from whatever mode the SD card driver used. +void tmc_spi_bus_setup() { +#if 0 + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->miso_io_num], PIN_FUNC_GPIO); + gpio_set_direction(bus_config->miso_io_num, GPIO_MODE_INPUT); + gpio_matrix_out(bus_config->miso_io_num, io_signal[host].spiq_out, false, false); + gpio_matrix_in(bus_config->miso_io_num, io_signal[host].spiq_in, false); +#endif + + if (clk_reg_val == 0) { + spi_ll_master_cal_clock(SPI_LL_PERIPH_CLK_FREQ, 2000000, 128, &clk_reg_val); + } + spi_ll_master_set_clock_by_reg(hw, &clk_reg_val); + + spi_ll_master_init(hw); + spi_ll_master_set_mode(hw, 3); + spi_ll_set_half_duplex(hw, false); + + spi_line_mode_t mode = { 1, 1, 1 }; + spi_ll_master_set_line_mode(hw, mode); // Single-line transfers; not DIO or QIO +} + +// Perform a full-duplex transfer from out/out_bitlen to in/in_bitlen +// If in_bitlen is 0, the input data will be ignored +void tmc_spi_transfer_data(uint8_t* out, int out_bitlen, uint8_t* in, int in_bitlen) { + spi_ll_set_mosi_bitlen(hw, out_bitlen); + + spi_ll_set_miso_bitlen(hw, in_bitlen); + + spi_ll_set_addr_bitlen(hw, 0); + spi_ll_set_command_bitlen(hw, 0); + + spi_ll_write_buffer(hw, out, out_bitlen); + spi_ll_enable_mosi(hw, 1); + if (in_bitlen) { + spi_ll_enable_miso(hw, 1); + } + + spi_ll_clear_int_stat(hw); + spi_ll_master_user_start(hw); + while (!spi_ll_usr_is_done(hw)) {} + + spi_ll_read_buffer(hw, in, in_bitlen); // No-op if in_bitlen is 0 +} + +// Do a single 5-byte (reg# + data) access to a TMC register, +// accounting for the number of TMC devices (index) daisy-chained +// before the target device. For reads, this is the first register +// access that latches the register data into the output register. +void tmc_spi_rw_reg(uint8_t cmd, uint32_t data, int index) { + int before = index > 0 ? index - 1 : 0; + + const size_t packetLen = 5; + size_t dummy_out_bytes = before * packetLen; + size_t total_bytes = (before + 1) * packetLen; + size_t total_bits = total_bytes * 8; + + uint8_t out[total_bytes]; + + // The data always starts at the beginning of the buffer then + // the trailing 0 bytes will push it through the chain to the + // target chip. + out[0] = cmd; + out[1] = data >> 24; + out[2] = data >> 16; + out[3] = data >> 8; + out[4] = data >> 0; + memset(&out[5], 0, total_bytes - 5); + + tmc_spi_transfer_data(out, total_bits, NULL, 0); +} diff --git a/FluidNC/esp32/tmc_spi_support.h b/FluidNC/esp32/tmc_spi_support.h new file mode 100644 index 000000000..28b983739 --- /dev/null +++ b/FluidNC/esp32/tmc_spi_support.h @@ -0,0 +1,16 @@ +// Copyright (c) 2022 Mitch Bradley +// Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void tmc_spi_bus_setup(); +void tmc_spi_transfer_data(uint8_t* out, int out_bitlen, uint8_t* in, int in_bitlen); +void tmc_spi_rw_reg(uint8_t cmd, uint32_t data, int index); + +#ifdef __cplusplus +} +#endif diff --git a/FluidNC/esp32/wdt.cpp b/FluidNC/esp32/wdt.cpp index 2feede8c3..e0baaab81 100644 --- a/FluidNC/esp32/wdt.cpp +++ b/FluidNC/esp32/wdt.cpp @@ -6,16 +6,42 @@ #include #include "src/Config.h" -void enable_core0_WDT() { +static TaskHandle_t wdt_task_handle = nullptr; + +static void get_wdt_task_handle() { TaskHandle_t idle_0 = xTaskGetIdleTaskHandleForCPU(0); - if (idle_0 == NULL || esp_task_wdt_add(idle_0) != ESP_OK) { - log_error("Failed to add Core 0 IDLE task to WDT"); + esp_err_t err; + err = esp_task_wdt_status(idle_0); + switch (err) { + case ESP_OK: + wdt_task_handle = idle_0; + break; + case ESP_ERR_NOT_FOUND: + wdt_task_handle = nullptr; + return; + case ESP_ERR_INVALID_STATE: + wdt_task_handle = nullptr; + return; + } +} + +void enable_core0_WDT() { + if (!wdt_task_handle) { + return; + } + esp_err_t err; + if ((err = esp_task_wdt_add(wdt_task_handle)) != ESP_OK) { + log_error("Failed to add Core 0 IDLE task to WDT " << err); } } void disable_core0_WDT() { - TaskHandle_t idle_0 = xTaskGetIdleTaskHandleForCPU(0); - if (idle_0 == NULL || esp_task_wdt_delete(idle_0) != ESP_OK) { - log_error("Failed to remove Core 0 IDLE task from WDT"); + get_wdt_task_handle(); + if (!wdt_task_handle) { + return; + } + esp_err_t err; + if ((err = esp_task_wdt_delete(wdt_task_handle)) != ESP_OK) { + log_error("Failed to remove Core 0 IDLE task from WDT " << err); } } diff --git a/FluidNC/include/Driver/delay_usecs.h b/FluidNC/include/Driver/delay_usecs.h new file mode 100644 index 000000000..072713ae6 --- /dev/null +++ b/FluidNC/include/Driver/delay_usecs.h @@ -0,0 +1,9 @@ +#include + +void timing_init(); +void spinUntil(int32_t endTicks); +void delay_us(int32_t us); + +int32_t usToCpuTicks(int32_t us); +int32_t usToEndTicks(int32_t us); +int32_t getCpuTicks(); diff --git a/FluidNC/src/Configuration/AfterParse.h b/FluidNC/src/Configuration/AfterParse.h index f1f2f7df1..d9f00e8b1 100644 --- a/FluidNC/src/Configuration/AfterParse.h +++ b/FluidNC/src/Configuration/AfterParse.h @@ -31,7 +31,7 @@ namespace Configuration { void item(const char* name, float& value, float minValue, float maxValue) override {} void item(const char* name, std::vector& value) override {} void item(const char* name, UartData& wordLength, UartParity& parity, UartStop& stopBits) override {} - void item(const char* name, String& value, int minLength, int maxLength) override {} + void item(const char* name, std::string& value, int minLength, int maxLength) override {} void item(const char* name, Pin& value) override {} void item(const char* name, IPAddress& value) override {} void item(const char* name, int& value, EnumItem* e) override {} diff --git a/FluidNC/src/Configuration/Completer.cpp b/FluidNC/src/Configuration/Completer.cpp index 046ce87e4..2c0bae939 100644 --- a/FluidNC/src/Configuration/Completer.cpp +++ b/FluidNC/src/Configuration/Completer.cpp @@ -6,14 +6,14 @@ #include "../Report.h" -#include +#include #include namespace Configuration { Completer::Completer(const char* key, int reqMatch, char* matchedStr) : _key(key), _reqMatch(reqMatch), _matchedStr(matchedStr), _currentPath("/"), _numMatches(0) {} - void Completer::addCandidate(String fullName) { + void Completer::addCandidate(std::string fullName) { if (_matchedStr && _numMatches == _reqMatch) { strcpy(_matchedStr, fullName.c_str()); } @@ -24,13 +24,13 @@ namespace Configuration { auto previous = _currentPath; _currentPath += name; _currentPath += "/"; - if (_key.startsWith(_currentPath)) { + if (_key.rfind(_currentPath, 0) == 0) { // If _currentPath is an initial substring of _key, this section // is part of a path leading to the key, so we have to check // this section's children // Example: _key = /axes/x/motor0/cy _currentPath=/axes/x/motor0 value->group(*this); - } else if (_currentPath.startsWith(_key)) { + } else if (_currentPath.rfind(_key, 0) == 0) { // If _key is an initial substring of _currentPath, this section // is a candidate. Example: _key = /axes/x/h _currentPath=/axes/x/homing addCandidate(_currentPath); @@ -39,8 +39,8 @@ namespace Configuration { } void Completer::item(const char* name) { - String fullItemName = _currentPath + name; - if (fullItemName.startsWith(_key)) { + std::string fullItemName = _currentPath + name; + if (fullItemName.rfind(_key, 0) == 0) { addCandidate(fullItemName); } } @@ -50,6 +50,14 @@ namespace Configuration { #include "../Settings.h" +static bool isInitialSubstringCI(const char* key, const char* test) { + while (*key && *test) { + if (tolower(*key++) != tolower(*test++)) { + return false; + } + } + return *key == '\0'; +} // This provides the interface to the completion routines in lineedit.cpp // The argument signature is idiosyncratic, based on the needs of the // Forth implementation for which the completion code was first developed. @@ -73,13 +81,8 @@ int num_initial_matches(char* key, int keylen, int matchnum, char* matchname) { nfound = completer._numMatches; } else { // Match NVS settings - auto lcKey = String(key); - lcKey.toLowerCase(); for (Setting* s = Setting::List; s; s = s->next()) { - auto lcTest = String(s->getName()); - lcTest.toLowerCase(); - - if (*key == '\0' || lcTest.startsWith(lcKey)) { + if (isInitialSubstringCI(key, s->getName())) { if (matchname && nfound == matchnum) { strcpy(matchname, s->getName()); } diff --git a/FluidNC/src/Configuration/Completer.h b/FluidNC/src/Configuration/Completer.h index 58e9bf287..a7db12f5a 100644 --- a/FluidNC/src/Configuration/Completer.h +++ b/FluidNC/src/Configuration/Completer.h @@ -9,12 +9,12 @@ namespace Configuration { class Completer : public Configuration::HandlerBase { private: - String _key; - int _reqMatch; - char* _matchedStr; - String _currentPath; + std::string _key; + int _reqMatch; + char* _matchedStr; + std::string _currentPath; - void addCandidate(String fullName); + void addCandidate(std::string fullName); protected: void enterSection(const char* name, Configuration::Configurable* value) override; @@ -32,7 +32,7 @@ namespace Configuration { void item(const char* name, float& value, float minValue, float maxValue) override { item(name); } void item(const char* name, std::vector& value) override { item(name); } void item(const char* name, UartData& wordLength, UartParity& parity, UartStop& stopBits) override { item(name); } - void item(const char* name, String& value, int minLength, int maxLength) override { item(name); } + void item(const char* name, std::string& value, int minLength, int maxLength) override { item(name); } void item(const char* name, Pin& value) { item(name); } void item(const char* name, IPAddress& value) override { item(name); } void item(const char* name, int& value, EnumItem* e) override { item(name); } diff --git a/FluidNC/src/Configuration/Configurable.h b/FluidNC/src/Configuration/Configurable.h index 8ed1fff47..12f2e0b44 100644 --- a/FluidNC/src/Configuration/Configurable.h +++ b/FluidNC/src/Configuration/Configurable.h @@ -19,7 +19,7 @@ namespace Configuration { public: Configurable() = default; - virtual void validate() const {}; + virtual void validate() {}; virtual void group(HandlerBase& handler) = 0; virtual void afterParse() {} // virtual const char* name() const = 0; diff --git a/FluidNC/src/Configuration/ConfigurationHandlers.md b/FluidNC/src/Configuration/ConfigurationHandlers.md index 3545f859f..59d701593 100644 --- a/FluidNC/src/Configuration/ConfigurationHandlers.md +++ b/FluidNC/src/Configuration/ConfigurationHandlers.md @@ -40,7 +40,7 @@ public: Pin _data; Pin _ws; - void validate() const override { + void validate() override { if (!_bck.undefined() || !_data.undefined() || !_ws.undefined()) { Assert(!_bck.undefined(), "I2SO BCK pin should be configured once."); Assert(!_data.undefined(), "I2SO Data pin should be configured once."); diff --git a/FluidNC/src/Configuration/Generator.h b/FluidNC/src/Configuration/Generator.h index 537671b2b..f59c39c62 100644 --- a/FluidNC/src/Configuration/Generator.h +++ b/FluidNC/src/Configuration/Generator.h @@ -98,13 +98,13 @@ namespace Configuration { send_item(name, s); } - void item(const char* name, String& value, int minLength, int maxLength) override { send_item(name, value.c_str()); } + void item(const char* name, std::string& value, int minLength, int maxLength) override { send_item(name, value); } void item(const char* name, bool& value) override { send_item(name, value ? "true" : "false"); } - void item(const char* name, Pin& value) override { send_item(name, value.name().c_str()); } + void item(const char* name, Pin& value) override { send_item(name, value.name()); } - void item(const char* name, IPAddress& value) override { send_item(name, value.toString().c_str()); } + void item(const char* name, IPAddress& value) override { send_item(name, IP_string(value)); } void item(const char* name, int& value, EnumItem* e) override { const char* str = "unknown"; for (; e->name; ++e) { diff --git a/FluidNC/src/Configuration/HandlerBase.h b/FluidNC/src/Configuration/HandlerBase.h index 727868159..4bb2bd27d 100644 --- a/FluidNC/src/Configuration/HandlerBase.h +++ b/FluidNC/src/Configuration/HandlerBase.h @@ -10,6 +10,7 @@ #include "../UartTypes.h" #include +#include namespace Configuration { class Configurable; @@ -52,7 +53,7 @@ namespace Configuration { virtual void item(const char* name, int& value, EnumItem* e) = 0; - virtual void item(const char* name, String& value, int minLength = 0, int maxLength = 255) = 0; + virtual void item(const char* name, std::string& value, int minLength = 0, int maxLength = 255) = 0; virtual HandlerType handlerType() = 0; @@ -60,9 +61,12 @@ namespace Configuration { void section(const char* name, T*& value, U... args) { if (handlerType() == HandlerType::Parser) { // For Parser, matchesUninitialized(name) resolves to _parser.is(name) - if (value == nullptr && matchesUninitialized(name)) { - value = new T(args...); - enterSection(name, value); + if (matchesUninitialized(name)) { + Assert(value == nullptr, "Duplicate section %s", name); + if (value == nullptr) { + value = new T(args...); + enterSection(name, value); + } } } else { if (value != nullptr) { diff --git a/FluidNC/src/Configuration/JsonGenerator.cpp b/FluidNC/src/Configuration/JsonGenerator.cpp index 0f9ec45e4..54597fc0d 100644 --- a/FluidNC/src/Configuration/JsonGenerator.cpp +++ b/FluidNC/src/Configuration/JsonGenerator.cpp @@ -8,6 +8,8 @@ #include #include #include +#include +#include namespace Configuration { JsonGenerator::JsonGenerator(WebUI::JSONencoder& encoder) : _encoder(encoder) { @@ -73,7 +75,7 @@ namespace Configuration { leave(); } - void JsonGenerator::item(const char* name, uint32_t& value, uint32_t minValue, uint32_t maxValue) { + void JsonGenerator::item(const char* name, uint32_t& value, uint32_t minValue, uint32_t maxValue) { enter(name); char buf[32]; itoa(value, buf, 10); @@ -90,7 +92,9 @@ namespace Configuration { } else if (value < -999999.999f) { value = -999999.999f; } - _encoder.begin_webui(_currentPath, _currentPath, "R", String(value, 3).c_str()); + std::ostringstream fstr; + fstr << std::fixed << std::setprecision(3) << value; + _encoder.begin_webui(_currentPath, _currentPath, "R", fstr.str()); _encoder.end_object(); leave(); } @@ -100,7 +104,7 @@ namespace Configuration { // Not sure if I should comment this out or not. The implementation is similar to the one in Generator.h. } - void JsonGenerator::item(const char* name, String& value, int minLength, int maxLength) { + void JsonGenerator::item(const char* name, std::string& value, int minLength, int maxLength) { enter(name); _encoder.begin_webui(_currentPath, _currentPath, "S", value.c_str(), minLength, maxLength); _encoder.end_object(); @@ -121,7 +125,7 @@ namespace Configuration { void JsonGenerator::item(const char* name, IPAddress& value) { enter(name); - _encoder.begin_webui(_currentPath, _currentPath, "A", value.toString().c_str()); + _encoder.begin_webui(_currentPath, _currentPath, "A", IP_string(value)); _encoder.end_object(); leave(); } diff --git a/FluidNC/src/Configuration/JsonGenerator.h b/FluidNC/src/Configuration/JsonGenerator.h index 8e87686bd..4d6d1dcd8 100644 --- a/FluidNC/src/Configuration/JsonGenerator.h +++ b/FluidNC/src/Configuration/JsonGenerator.h @@ -6,7 +6,6 @@ #include #include "../Pin.h" -#include "../StringStream.h" #include "HandlerBase.h" #include "../WebUI/JSONEncoder.h" @@ -41,7 +40,7 @@ namespace Configuration { void item(const char* name, float& value, float minValue, float maxValue) override; void item(const char* name, std::vector& value) override; void item(const char* name, UartData& wordLength, UartParity& parity, UartStop& stopBits) override; - void item(const char* name, String& value, int minLength, int maxLength) override; + void item(const char* name, std::string& value, int minLength, int maxLength) override; void item(const char* name, Pin& value) override; void item(const char* name, IPAddress& value) override; void item(const char* name, int& value, EnumItem* e) override; diff --git a/FluidNC/src/Configuration/ParseException.h b/FluidNC/src/Configuration/ParseException.h index 8f4974273..173e01355 100644 --- a/FluidNC/src/Configuration/ParseException.h +++ b/FluidNC/src/Configuration/ParseException.h @@ -6,7 +6,7 @@ namespace Configuration { class ParseException { int line_; - const char* description_; + std::string description_; public: ParseException() = default; @@ -14,7 +14,7 @@ namespace Configuration { ParseException(int line, const char* description) : line_(line), description_(description) {} - inline int LineNumber() const { return line_; } - inline const char* What() const { return description_; } + inline int LineNumber() const { return line_; } + inline const std::string& What() const { return description_; } }; } diff --git a/FluidNC/src/Configuration/Parser.cpp b/FluidNC/src/Configuration/Parser.cpp index b321648c0..9cdecaa5a 100644 --- a/FluidNC/src/Configuration/Parser.cpp +++ b/FluidNC/src/Configuration/Parser.cpp @@ -38,9 +38,7 @@ namespace Configuration { return result; } - StringRange Parser::stringValue() const { - return StringRange(token_.sValueStart_, token_.sValueEnd_); - } + StringRange Parser::stringValue() const { return StringRange(token_.sValueStart_, token_.sValueEnd_); } bool Parser::boolValue() const { auto str = StringRange(token_.sValueStart_, token_.sValueEnd_); @@ -118,7 +116,7 @@ namespace Configuration { IPAddress Parser::ipValue() const { IPAddress ip; auto str = StringRange(token_.sValueStart_, token_.sValueEnd_); - if (!ip.fromString(str.str())) { + if (!ip.fromString(str.str().c_str())) { parseError("Expected an IP address like 192.168.0.100"); } return ip; @@ -138,7 +136,7 @@ namespace Configuration { auto str = StringRange(token_.sValueStart_, token_.sValueEnd_); if (str.length() == 5 || str.length() == 3) { int32_t wordLenInt; - if (!str.subString(0, 1).isInteger(wordLenInt)) { + if (!str.substr(0, 1).isInteger(wordLenInt)) { parseError("Uart mode should be specified as [Bits Parity Stopbits] like [8N1]"); } else if (wordLenInt < 5 || wordLenInt > 8) { parseError("Number of data bits for uart is out of range. Expected format like [8N1]."); @@ -163,7 +161,7 @@ namespace Configuration { break; // Omits compiler warning. Never hit. } - auto stop = str.subString(2, str.length() - 2); + auto stop = str.substr(2, str.length() - 2); if (stop.equals("1")) { stopBits = UartStop::Bits1; } else if (stop.equals("1.5")) { diff --git a/FluidNC/src/Configuration/ParserHandler.h b/FluidNC/src/Configuration/ParserHandler.h index a5e5c3bc2..2fb46e7a7 100644 --- a/FluidNC/src/Configuration/ParserHandler.h +++ b/FluidNC/src/Configuration/ParserHandler.h @@ -142,9 +142,9 @@ namespace Configuration { } } - void item(const char* name, String& value, int minLength, int maxLength) override { + void item(const char* name, std::string& value, int minLength, int maxLength) override { if (_parser.is(name)) { - value = _parser.stringValue().str(); + value = _parser.stringValue().str().c_str(); } } diff --git a/FluidNC/src/Configuration/RuntimeSetting.cpp b/FluidNC/src/Configuration/RuntimeSetting.cpp index f827fa0f7..bfd80c17d 100644 --- a/FluidNC/src/Configuration/RuntimeSetting.cpp +++ b/FluidNC/src/Configuration/RuntimeSetting.cpp @@ -109,13 +109,13 @@ namespace Configuration { } } - void RuntimeSetting::item(const char* name, String& value, int minLength, int maxLength) { + void RuntimeSetting::item(const char* name, std::string& value, int minLength, int maxLength) { if (is(name)) { isHandled_ = true; if (newValue_ == nullptr) { log_to(out_, "", setting_prefix() << value); } else { - value = String(newValue_); + value = newValue_; } } } @@ -173,27 +173,27 @@ namespace Configuration { } } else { // It is distasteful to have this code that essentially duplicates - // Parser.cpp speedEntryValue(), albeit using String instead of - // StringRange. It would be better to have a single String version, + // Parser.cpp speedEntryValue(), albeit using std::string instead of + // StringRange. It might be better to have a single std::string version, // then pass it StringRange.str() - auto newStr = String(newValue_); + std::string newStr(newValue_); std::vector smValue; - while (newStr.trim(), newStr.length()) { - speedEntry entry; - String entryStr; - auto i = newStr.indexOf(' '); - if (i >= 0) { - entryStr = newStr.substring(0, i); - newStr = newStr.substring(i + 1); + while (newStr.length()) { + speedEntry entry; + std::string entryStr; + auto i = newStr.find(' '); + if (i != std::string::npos) { + entryStr = newStr.substr(0, i); + newStr = newStr.substr(i + 1); } else { entryStr = newStr; newStr = ""; } - String speed; - i = entryStr.indexOf('='); - Assert(i > 0, "Bad speed map entry"); - entry.speed = entryStr.substring(0, i).toInt(); - entry.percent = entryStr.substring(i + 1).toFloat(); + std::string speed; + i = entryStr.find('='); + Assert(i != std::string::npos, "Bad speed map entry"); + entry.speed = ::atoi(entryStr.substr(0, i).c_str()); + entry.percent = ::atof(entryStr.substr(i + 1).c_str()); smValue.push_back(entry); } value = smValue; @@ -205,7 +205,7 @@ namespace Configuration { if (is(name)) { isHandled_ = true; if (newValue_ == nullptr) { - log_to(out_, "", setting_prefix() << value.toString()); + log_to(out_, "", setting_prefix() << IP_string(value)); } else { IPAddress ip; if (!ip.fromString(newValue_)) { diff --git a/FluidNC/src/Configuration/RuntimeSetting.h b/FluidNC/src/Configuration/RuntimeSetting.h index 014176455..cbce85588 100644 --- a/FluidNC/src/Configuration/RuntimeSetting.h +++ b/FluidNC/src/Configuration/RuntimeSetting.h @@ -39,7 +39,7 @@ namespace Configuration { void item(const char* name, float& value, float minValue, float maxValue) override; void item(const char* name, std::vector& value) override; void item(const char* name, UartData& wordLength, UartParity& parity, UartStop& stopBits) override {} - void item(const char* name, String& value, int minLength, int maxLength) override; + void item(const char* name, std::string& value, int minLength, int maxLength) override; void item(const char* name, Pin& value) override; void item(const char* name, IPAddress& value) override; void item(const char* name, int& value, EnumItem* e) override; diff --git a/FluidNC/src/Configuration/Tokenizer.cpp b/FluidNC/src/Configuration/Tokenizer.cpp index a4105602e..eb1d4a150 100644 --- a/FluidNC/src/Configuration/Tokenizer.cpp +++ b/FluidNC/src/Configuration/Tokenizer.cpp @@ -93,9 +93,9 @@ namespace Configuration { } if (Current() != ':') { - String err = "Key "; - err += StringRange(token_.keyStart_, token_.keyEnd_).str(); - err += "must be followed by ':'"; + std::string err = "Key "; + err += StringRange(token_.keyStart_, token_.keyEnd_).str().c_str(); + err += " must be followed by ':'"; ParseError(err.c_str()); } Inc(); diff --git a/FluidNC/src/Configuration/Validator.h b/FluidNC/src/Configuration/Validator.h index e0d70f0ea..c44e859ae 100644 --- a/FluidNC/src/Configuration/Validator.h +++ b/FluidNC/src/Configuration/Validator.h @@ -31,7 +31,7 @@ namespace Configuration { void item(const char* name, float& value, float minValue, float maxValue) override {} void item(const char* name, std::vector& value) override {} void item(const char* name, UartData& wordLength, UartParity& parity, UartStop& stopBits) override {} - void item(const char* name, String& value, int minLength, int maxLength) override {} + void item(const char* name, std::string& value, int minLength, int maxLength) override {} void item(const char* name, Pin& value) override {} void item(const char* name, IPAddress& value) override {} void item(const char* name, int& value, EnumItem* e) override {} diff --git a/FluidNC/src/Configuration/_Overview.md b/FluidNC/src/Configuration/_Overview.md index df2606fad..624a642a4 100644 --- a/FluidNC/src/Configuration/_Overview.md +++ b/FluidNC/src/Configuration/_Overview.md @@ -45,7 +45,7 @@ public: Pin _bck; // ... - void validate() const override { + void validate() override { if (!_bck.undefined() || !_data.undefined() || !_ws.undefined()) { Assert(!_bck.undefined(), "I2SO BCK pin should be configured once."); // ... diff --git a/FluidNC/src/Event.h b/FluidNC/src/Event.h index daeceafa7..a6453c79f 100644 --- a/FluidNC/src/Event.h +++ b/FluidNC/src/Event.h @@ -2,7 +2,6 @@ // Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. #pragma once -#include // String // Objects derived from the Event base class are placed in the event queue. // Protocol dequeues them and calls their run methods. diff --git a/FluidNC/src/FileStream.cpp b/FluidNC/src/FileStream.cpp index 3cb6e2a29..4d5cfcbed 100644 --- a/FluidNC/src/FileStream.cpp +++ b/FluidNC/src/FileStream.cpp @@ -5,11 +5,11 @@ #include "Machine/MachineConfig.h" // config-> #include "Driver/localfs.h" -String FileStream::path() { +std::string FileStream::path() { return _fpath.c_str(); } -String FileStream::name() { +std::string FileStream::name() { return path(); } diff --git a/FluidNC/src/FileStream.h b/FluidNC/src/FileStream.h index 8962f4169..01559bafd 100644 --- a/FluidNC/src/FileStream.h +++ b/FluidNC/src/FileStream.h @@ -26,18 +26,18 @@ class FileStream : public Channel { public: FileStream() = default; - FileStream(String filename, const char* mode, const char* defaultFs = "") : FileStream(filename.c_str(), mode, defaultFs) {} + FileStream(std::string filename, const char* mode, const char* defaultFs = "") : FileStream(filename.c_str(), mode, defaultFs) {} FileStream(const char* filename, const char* mode, const char* defaultFs = ""); FileStream(FluidPath fpath, const char* mode); FluidPath fpath() { return _fpath; } - String path(); - String name(); - int available() override; - int read() override; - int peek() override; - void flush() override; + std::string path(); + std::string name(); + int available() override; + int read() override; + int peek() override; + void flush() override; size_t readBytes(char* buffer, size_t length) { return read((uint8_t*)buffer, length); } diff --git a/FluidNC/src/FluidPath.h b/FluidNC/src/FluidPath.h index 706ab7a04..0701eceba 100644 --- a/FluidNC/src/FluidPath.h +++ b/FluidNC/src/FluidPath.h @@ -9,11 +9,9 @@ namespace stdfs = std::filesystem; class FluidPath : public stdfs::path { public: FluidPath(const char* name, const char* fs, std::error_code& ec) noexcept : FluidPath(name, fs, &ec) {} - FluidPath(String name, const char* fs, std::error_code& ec) noexcept : FluidPath(name.c_str(), fs, &ec) {} - // FluidPath(std::string name, std::error_code& ec) noexcept : FluidPath(name.c_str(), &ec) {} + FluidPath(const std::string& name, const char* fs, std::error_code& ec) noexcept : FluidPath(name.c_str(), fs, &ec) {} FluidPath(const char* name, const char* fs) : FluidPath(name, fs, nullptr) {} - FluidPath(String name, const char* fs) : FluidPath(name.c_str(), fs) {} - // FluidPath(std::string name) : FluidPath(name.c_str()) {} + FluidPath(const std::string& name, const char* fs) : FluidPath(name.c_str(), fs) {} ~FluidPath(); diff --git a/FluidNC/src/I2SOut.cpp b/FluidNC/src/I2SOut.cpp index 40ef6e65f..3cf336f0a 100644 --- a/FluidNC/src/I2SOut.cpp +++ b/FluidNC/src/I2SOut.cpp @@ -5,44 +5,76 @@ #include "I2SOut.h" -#include "Config.h" -#include "Pin.h" -#include "Settings.h" -#include "SettingsDefinitions.h" -#include "Machine/MachineConfig.h" -#include "Stepper.h" - -#include // IRAM_ATTR - -#include -#include -#include -#include -#include -#include -#include "Driver/fluidnc_gpio.h" +#include + +#ifndef CONFIG_IDF_TARGET_ESP32 +// The newer ESP32 variants have quite different I2S hardware engines +// then the old ESP32 hardware. For now we stub out I2S support for new ESP32s + +uint8_t i2s_out_read(pinnum_t pin) { + return 0; +} +void i2s_out_write(pinnum_t pin, uint8_t val) {} +void i2s_out_push_sample(uint32_t usec) {} +void i2s_out_push() {} +void i2s_out_delay() {} +int i2s_out_set_passthrough() { + return 0; +} +i2s_out_pulser_status_t i2s_out_get_pulser_status() { + return PASSTHROUGH; +} +int i2s_out_set_stepping() { + return 0; +} +int i2s_out_set_pulse_period(uint32_t period) { + return 0; +} +int i2s_out_reset() { + return 0; +} +int i2s_out_init() { + return -1; +} +#else +# include "Config.h" +# include "Pin.h" +# include "Settings.h" +# include "SettingsDefinitions.h" +# include "Machine/MachineConfig.h" +# include "Stepper.h" + +# include // IRAM_ATTR + +# include +# include +# include +# include +# include +# include +# include "Driver/fluidnc_gpio.h" // The library routines are not in IRAM so they can crash when called from FLASH // The GCC intrinsic versions which are prefixed with __ are compiled inline -#define USE_INLINE_ATOMIC - -#ifdef USE_INLINE_ATOMIC -# define MEMORY_MODEL_FETCH __ATOMIC_RELAXED -# define MEMORY_MODEL_STORE __ATOMIC_RELAXED -# define ATOMIC_LOAD(var) __atomic_load_n(var, MEMORY_MODEL_FETCH) -# define ATOMIC_STORE(var, val) __atomic_store_n(var, val, MEMORY_MODEL_STORE) -# define ATOMIC_FETCH_AND(var, val) __atomic_fetch_and(var, val, MEMORY_MODEL_FETCH) -# define ATOMIC_FETCH_OR(var, val) __atomic_fetch_or(var, val, MEMORY_MODEL_FETCH) +# define USE_INLINE_ATOMIC + +# ifdef USE_INLINE_ATOMIC +# define MEMORY_MODEL_FETCH __ATOMIC_RELAXED +# define MEMORY_MODEL_STORE __ATOMIC_RELAXED +# define ATOMIC_LOAD(var) __atomic_load_n(var, MEMORY_MODEL_FETCH) +# define ATOMIC_STORE(var, val) __atomic_store_n(var, val, MEMORY_MODEL_STORE) +# define ATOMIC_FETCH_AND(var, val) __atomic_fetch_and(var, val, MEMORY_MODEL_FETCH) +# define ATOMIC_FETCH_OR(var, val) __atomic_fetch_or(var, val, MEMORY_MODEL_FETCH) static uint32_t i2s_out_port_data = 0; -#else -# include -# define ATOMIC_LOAD(var) atomic_load(var) -# define ATOMIC_STORE(var, val) atomic_store(var, val) -# define ATOMIC_FETCH_AND(var, val) atomic_fetch_and(var, val) -# define ATOMIC_FETCH_OR(var, val) atomic_fetch_or(var, val) +# else +# include +# define ATOMIC_LOAD(var) atomic_load(var) +# define ATOMIC_STORE(var, val) atomic_store(var, val) +# define ATOMIC_FETCH_AND(var, val) atomic_fetch_and(var, val) +# define ATOMIC_FETCH_OR(var, val) atomic_fetch_or(var, val) static std::atomic i2s_out_port_data = ATOMIC_VAR_INIT(0); -#endif +# endif // // Configrations for DMA connected I2S @@ -77,24 +109,24 @@ static intr_handle_t i2s_out_isr_handle; // inner lock static portMUX_TYPE i2s_out_spinlock = portMUX_INITIALIZER_UNLOCKED; -#define I2S_OUT_ENTER_CRITICAL() \ - do { \ - if (xPortInIsrContext()) { \ - portENTER_CRITICAL_ISR(&i2s_out_spinlock); \ - } else { \ - portENTER_CRITICAL(&i2s_out_spinlock); \ - } \ - } while (0) -#define I2S_OUT_EXIT_CRITICAL() \ - do { \ - if (xPortInIsrContext()) { \ - portEXIT_CRITICAL_ISR(&i2s_out_spinlock); \ - } else { \ - portEXIT_CRITICAL(&i2s_out_spinlock); \ - } \ - } while (0) -#define I2S_OUT_ENTER_CRITICAL_ISR() portENTER_CRITICAL_ISR(&i2s_out_spinlock) -#define I2S_OUT_EXIT_CRITICAL_ISR() portEXIT_CRITICAL_ISR(&i2s_out_spinlock) +# define I2S_OUT_ENTER_CRITICAL() \ + do { \ + if (xPortInIsrContext()) { \ + portENTER_CRITICAL_ISR(&i2s_out_spinlock); \ + } else { \ + portENTER_CRITICAL(&i2s_out_spinlock); \ + } \ + } while (0) +# define I2S_OUT_EXIT_CRITICAL() \ + do { \ + if (xPortInIsrContext()) { \ + portEXIT_CRITICAL_ISR(&i2s_out_spinlock); \ + } else { \ + portEXIT_CRITICAL(&i2s_out_spinlock); \ + } \ + } while (0) +# define I2S_OUT_ENTER_CRITICAL_ISR() portENTER_CRITICAL_ISR(&i2s_out_spinlock) +# define I2S_OUT_EXIT_CRITICAL_ISR() portEXIT_CRITICAL_ISR(&i2s_out_spinlock) static int i2s_out_initialized = 0; @@ -109,30 +141,30 @@ static volatile i2s_out_pulser_status_t i2s_out_pulser_status = PASSTHROUGH; // outer lock static portMUX_TYPE i2s_out_pulser_spinlock = portMUX_INITIALIZER_UNLOCKED; -#define I2S_OUT_PULSER_ENTER_CRITICAL() \ - do { \ - if (xPortInIsrContext()) { \ - portENTER_CRITICAL_ISR(&i2s_out_pulser_spinlock); \ - } else { \ - portENTER_CRITICAL(&i2s_out_pulser_spinlock); \ - } \ - } while (0) -#define I2S_OUT_PULSER_EXIT_CRITICAL() \ - do { \ - if (xPortInIsrContext()) { \ - portEXIT_CRITICAL_ISR(&i2s_out_pulser_spinlock); \ - } else { \ - portEXIT_CRITICAL(&i2s_out_pulser_spinlock); \ - } \ - } while (0) -#define I2S_OUT_PULSER_ENTER_CRITICAL_ISR() portENTER_CRITICAL_ISR(&i2s_out_pulser_spinlock) -#define I2S_OUT_PULSER_EXIT_CRITICAL_ISR() portEXIT_CRITICAL_ISR(&i2s_out_pulser_spinlock) - -#if I2S_OUT_NUM_BITS == 16 -# define DATA_SHIFT 16 -#else -# define DATA_SHIFT 0 -#endif +# define I2S_OUT_PULSER_ENTER_CRITICAL() \ + do { \ + if (xPortInIsrContext()) { \ + portENTER_CRITICAL_ISR(&i2s_out_pulser_spinlock); \ + } else { \ + portENTER_CRITICAL(&i2s_out_pulser_spinlock); \ + } \ + } while (0) +# define I2S_OUT_PULSER_EXIT_CRITICAL() \ + do { \ + if (xPortInIsrContext()) { \ + portEXIT_CRITICAL_ISR(&i2s_out_pulser_spinlock); \ + } else { \ + portEXIT_CRITICAL(&i2s_out_pulser_spinlock); \ + } \ + } while (0) +# define I2S_OUT_PULSER_ENTER_CRITICAL_ISR() portENTER_CRITICAL_ISR(&i2s_out_pulser_spinlock) +# define I2S_OUT_PULSER_EXIT_CRITICAL_ISR() portEXIT_CRITICAL_ISR(&i2s_out_pulser_spinlock) + +# if I2S_OUT_NUM_BITS == 16 +# define DATA_SHIFT 16 +# else +# define DATA_SHIFT 0 +# endif // // Internal functions @@ -418,14 +450,14 @@ static void IRAM_ATTR i2s_out_intr_handler(void* arg) { port_data = ATOMIC_LOAD(&i2s_out_port_data); } I2S_OUT_PULSER_EXIT_CRITICAL_ISR(); -#ifdef CONFIG_IDF_TARGET_ESP32 +# ifdef CONFIG_IDF_TARGET_ESP32 // lldesc_t.buf is const for S2. Perhaps we can get by // without replacing the data in the buffer since we are // already in an error situation. for (int i = 0; i < DMA_SAMPLE_COUNT; i++) { front_desc->buf[i] = port_data; } -#endif +# endif front_desc->length = I2S_OUT_DMABUF_LEN; } @@ -493,9 +525,9 @@ static void i2sOutTask(void* parameter) { I2S_OUT_PULSER_EXIT_CRITICAL(); // Unlock pulser status static UBaseType_t uxHighWaterMark = 0; -#ifdef DEBUG_TASK_STACK +# ifdef DEBUG_TASK_STACK reportTaskStackSize(uxHighWaterMark); -#endif +# endif } } @@ -753,12 +785,12 @@ int i2s_out_init(i2s_out_init_t& init_param) { I2S0.lc_conf.out_eof_mode = 1; // I2S_OUT_EOF_INT generated when DMA has popped all data from the FIFO; I2S0.conf2.lcd_en = 0; I2S0.conf2.camera_en = 0; -#ifdef SOC_I2S_SUPPORTS_PDM_TX +# ifdef SOC_I2S_SUPPORTS_PDM_TX // i2s_ll_tx_enable_pdm(dev, false); // i2s_ll_tx_enable_pdm(dev2, false); I2S0.pdm_conf.pcm2pdm_conv_en = 0; I2S0.pdm_conf.pdm2pcm_conv_en = 0; -#endif +# endif I2S0.fifo_conf.dscr_en = 0; @@ -772,19 +804,19 @@ int i2s_out_init(i2s_out_init_t& init_param) { I2S0.conf_single_data = init_param.init_val; } -#if I2S_OUT_NUM_BITS == 16 +# if I2S_OUT_NUM_BITS == 16 I2S0.fifo_conf.tx_fifo_mod = 0; // 0: 16-bit dual channel data, 3: 32-bit single channel data I2S0.fifo_conf.rx_fifo_mod = 0; // 0: 16-bit dual channel data, 3: 32-bit single channel data I2S0.sample_rate_conf.tx_bits_mod = 16; // default is 16-bits I2S0.sample_rate_conf.rx_bits_mod = 16; // default is 16-bits -#else +# else I2S0.fifo_conf.tx_fifo_mod = 3; // 0: 16-bit dual channel data, 3: 32-bit single channel data I2S0.fifo_conf.rx_fifo_mod = 3; // 0: 16-bit dual channel data, 3: 32-bit single channel data // Data width is 32-bit. Forgetting this setting will result in a 16-bit transfer. I2S0.sample_rate_conf.tx_bits_mod = 32; I2S0.sample_rate_conf.rx_bits_mod = 32; -#endif - I2S0.conf.tx_mono = 0; // Set this bit to enable transmitter’s mono mode in PCM standard mode. +# endif + I2S0.conf.tx_mono = 0; // Set this bit to enable transmitter’s mono mode in PCM standard mode. I2S0.conf_chan.rx_chan_mod = 1; // 1: right+right I2S0.conf.rx_mono = 0; @@ -798,14 +830,14 @@ int i2s_out_init(i2s_out_init_t& init_param) { I2S0.conf.tx_slave_mod = 0; // Master I2S0.fifo_conf.tx_fifo_mod_force_en = 1; //The bit should always be set to 1. -#ifdef SOC_I2S_SUPPORTS_PDM_RX +# ifdef SOC_I2S_SUPPORTS_PDM_RX //i2s_ll_rx_enable_pdm(dev, false); I2S0.pdm_conf.rx_pdm_en = 0; // Set this bit to enable receiver’s PDM mode. -#endif -#ifdef SOC_I2S_SUPPORTS_PDM_TX +# endif +# ifdef SOC_I2S_SUPPORTS_PDM_TX //i2s_ll_tx_enable_pdm(dev, false); I2S0.pdm_conf.tx_pdm_en = 0; // Set this bit to enable transmitter’s PDM mode. -#endif +# endif // I2S_COMM_FORMAT_I2S_LSB I2S0.conf.tx_short_sync = 0; // Set this bit to enable transmitter in PCM standard mode. @@ -818,20 +850,20 @@ int i2s_out_init(i2s_out_init_t& init_param) { // // set clock (fi2s) 160MHz / 5 -#ifdef CONFIG_IDF_TARGET_ESP32 +# ifdef CONFIG_IDF_TARGET_ESP32 // i2s_ll_rx_clk_set_src(dev, I2S_CLK_D2CLK); I2S0.clkm_conf.clka_en = 0; // Use 160 MHz PLL_D2_CLK as reference -#endif +# endif // N + b/a = 0 -#if I2S_OUT_NUM_BITS == 16 +# if I2S_OUT_NUM_BITS == 16 // N = 10 I2S0.clkm_conf.clkm_div_num = 10; // minimum value of 2, reset value of 4, max 256 (I²S clock divider’s integral value) -#else +# else // N = 5 // 5 could be changed to 2 to make I2SO pulse at 312.5 kHZ instead of 125 kHz, but doing so would // require some changes to deal with pulse lengths that are not an integral number of microseconds. I2S0.clkm_conf.clkm_div_num = 5; // minimum value of 2, reset value of 4, max 256 (I²S clock divider’s integral value) -#endif +# endif // b/a = 0 I2S0.clkm_conf.clkm_div_b = 0; // 0 at reset I2S0.clkm_conf.clkm_div_a = 0; // 0 at reset, what about divide by 0? (not an issue) @@ -876,9 +908,9 @@ int i2s_out_init(i2s_out_init_t& init_param) { return 0; } -#ifndef I2S_OUT_INIT_VAL -# define I2S_OUT_INIT_VAL 0 -#endif +# ifndef I2S_OUT_INIT_VAL +# define I2S_OUT_INIT_VAL 0 +# endif /* Initialize I2S out by default parameters. @@ -915,3 +947,4 @@ int i2s_out_init() { return i2s_out_init(default_param); } } +#endif diff --git a/FluidNC/src/Kinematics/Cartesian.h b/FluidNC/src/Kinematics/Cartesian.h index d2814cce3..50476241a 100644 --- a/FluidNC/src/Kinematics/Cartesian.h +++ b/FluidNC/src/Kinematics/Cartesian.h @@ -36,7 +36,7 @@ namespace Kinematics { // Configuration handlers: void afterParse() override {} void group(Configuration::HandlerBase& handler) override {} - void validate() const override {} + void validate() override {} // Name of the configurable. Must match the name registered in the cpp file. const char* name() const override { return "Cartesian"; } diff --git a/FluidNC/src/Kinematics/CoreXY.h b/FluidNC/src/Kinematics/CoreXY.h index de2891614..9c5d6c63f 100644 --- a/FluidNC/src/Kinematics/CoreXY.h +++ b/FluidNC/src/Kinematics/CoreXY.h @@ -36,7 +36,7 @@ namespace Kinematics { bool limitReached(AxisMask& axisMask, MotorMask& motors, MotorMask limited); // Configuration handlers: - void validate() const override {} + void validate() override {} virtual void group(Configuration::HandlerBase& handler) override; void afterParse() override {} diff --git a/FluidNC/src/Kinematics/Kinematics.h b/FluidNC/src/Kinematics/Kinematics.h index cd88e05ea..e8ea30352 100644 --- a/FluidNC/src/Kinematics/Kinematics.h +++ b/FluidNC/src/Kinematics/Kinematics.h @@ -80,7 +80,7 @@ namespace Kinematics { // Configuration interface. void afterParse() override {} void group(Configuration::HandlerBase& handler) override {} - void validate() const override {} + void validate() override {} // Name of the configurable. Must match the name registered in the cpp file. virtual const char* name() const = 0; diff --git a/FluidNC/src/Kinematics/WallPlotter.h b/FluidNC/src/Kinematics/WallPlotter.h index e713d6661..ed558dfe0 100644 --- a/FluidNC/src/Kinematics/WallPlotter.h +++ b/FluidNC/src/Kinematics/WallPlotter.h @@ -31,7 +31,7 @@ namespace Kinematics { void transform_cartesian_to_motors(float* cartesian, float* motors) override; // Configuration handlers: - void validate() const override {} + void validate() override {} void group(Configuration::HandlerBase& handler) override; void afterParse() override {} diff --git a/FluidNC/src/Limits.cpp b/FluidNC/src/Limits.cpp index 3a471a481..b6b1b05b8 100644 --- a/FluidNC/src/Limits.cpp +++ b/FluidNC/src/Limits.cpp @@ -59,16 +59,54 @@ void constrainToSoftLimits(float* cartesian) { auto axes = config->_axes; auto n_axis = config->_axes->_numberAxis; - bool limit_error = false; + float* current_position = get_mpos(); + MotorMask lim_pin_state = limits_get_state(); + for (int axis = 0; axis < n_axis; axis++) { auto axisSetting = axes->_axis[axis]; - if (axisSetting->_softLimits) { + // If the axis is moving from the current location and soft limits are on. + if (axisSetting->_softLimits && cartesian[axis] != current_position[axis]) { + // When outside the axis range, only small nudges to clear switches are allowed + if (current_position[axis] < limitsMinPosition(axis) || current_position[axis] > limitsMaxPosition(axis)) { + // only allow a nudge if a switch is active + if (bitnum_is_false(lim_pin_state, Machine::Axes::motor_bit(axis, 0)) && + bitnum_is_false(lim_pin_state, Machine::Axes::motor_bit(axis, 1))) { + cartesian[axis] = current_position[axis]; // cancel the move on this axis + log_debug("Soft limit violation on " << Machine::Axes::_names[axis]); + continue; + } + float jog_dist = cartesian[axis] - current_position[axis]; + + MotorMask axisMotors = Machine::Axes::axes_to_motors(1 << axis); + bool posLimited = bits_are_true(Machine::Axes::posLimitMask, axisMotors); + bool negLimited = bits_are_true(Machine::Axes::negLimitMask, axisMotors); + + // if jog is positive and only the positive switch is active, then kill the move + // if jog is negative and only the negative switch is active, then kill the move + if (posLimited != negLimited) { // XOR, because ambiguous (both) is OK + if ((negLimited && (jog_dist < 0)) || (posLimited && (jog_dist > 0))) { + cartesian[axis] = current_position[axis]; // cancel the move on this axis + log_debug("Jog into active switch blocked on " << Machine::Axes::_names[axis]); + continue; + } + } + + auto nudge_max = axisSetting->_motors[0]->_pulloff; + if (abs(jog_dist) > nudge_max) { + cartesian[axis] = (jog_dist >= 0) ? current_position[axis] + nudge_max : current_position[axis] + nudge_max; + log_debug("Jog amount limited when outside soft limits") + } + continue; + } + if (cartesian[axis] < limitsMinPosition(axis)) { cartesian[axis] = limitsMinPosition(axis); - } - if (cartesian[axis] > limitsMaxPosition(axis)) { + } else if (cartesian[axis] > limitsMaxPosition(axis)) { cartesian[axis] = limitsMaxPosition(axis); + } else { + continue; } + log_debug("Jog constrained to axis range"); } } } @@ -84,8 +122,7 @@ void limits_soft_check(float* cartesian) { for (int axis = 0; axis < n_axis; axis++) { if (axes->_axis[axis]->_softLimits && (cartesian[axis] < limitsMinPosition(axis) || cartesian[axis] > limitsMaxPosition(axis))) { - String axis_letter = String(Machine::Axes::_names[axis]); - log_info("Soft limit on " << axis_letter << " target:" << cartesian[axis]); + log_info("Soft limit on " << Machine::Axes::_names[axis] << " target:" << cartesian[axis]); limit_error = true; } } @@ -121,7 +158,7 @@ void limitCheckTask(void* pvParameters) { vTaskDelay(config->_softwareDebounceMs / portTICK_PERIOD_MS); // delay a while auto switch_state = limits_get_state(); if (switch_state) { - log_debug("Limit Switch State " << String(switch_state, HEX)); + log_debug("Limit Switch State " << to_hex(switch_state)); mc_reset(); // Initiate system kill. rtAlarm = ExecAlarm::HardLimit; // Indicate hard limit critical event } diff --git a/FluidNC/src/Machine/Axes.cpp b/FluidNC/src/Machine/Axes.cpp index d502153fe..4e94efd7b 100644 --- a/FluidNC/src/Machine/Axes.cpp +++ b/FluidNC/src/Machine/Axes.cpp @@ -227,9 +227,9 @@ namespace Machine { } } - String Axes::maskToNames(AxisMask mask) { - String retval = ""; - auto n_axis = _numberAxis; + std::string Axes::maskToNames(AxisMask mask) { + std::string retval(""); + auto n_axis = _numberAxis; for (int axis = 0; axis < n_axis; axis++) { if (bitnum_is_true(mask, axis)) { retval += _names[axis]; @@ -237,9 +237,9 @@ namespace Machine { } return retval; } - String Axes::motorMaskToNames(MotorMask mask) { - String retval = ""; - auto n_axis = _numberAxis; + std::string Axes::motorMaskToNames(MotorMask mask) { + std::string retval(""); + auto n_axis = _numberAxis; for (int axis = 0; axis < n_axis; axis++) { if (bitnum_is_true(mask, axis)) { retval += " "; diff --git a/FluidNC/src/Machine/Axes.h b/FluidNC/src/Machine/Axes.h index d7991b24f..dfbd7005f 100644 --- a/FluidNC/src/Machine/Axes.h +++ b/FluidNC/src/Machine/Axes.h @@ -71,10 +71,10 @@ namespace Machine { void unstep(); void config_motors(); - String maskToNames(AxisMask mask); - bool namesToMask(const char* names, AxisMask& mask); + std::string maskToNames(AxisMask mask); + bool namesToMask(const char* names, AxisMask& mask); - String motorMaskToNames(MotorMask mask); + std::string motorMaskToNames(MotorMask mask); // Configuration helpers: void group(Configuration::HandlerBase& handler) override; diff --git a/FluidNC/src/Machine/EventPin.h b/FluidNC/src/Machine/EventPin.h index 696502d1b..709237bb9 100644 --- a/FluidNC/src/Machine/EventPin.h +++ b/FluidNC/src/Machine/EventPin.h @@ -16,7 +16,7 @@ namespace Machine { static bool inactive(EventPin* pin); public: - String _legend; // The name that appears in init() messages and the name of the configuration item + std::string _legend; // The name that appears in init() messages and the name of the configuration item EventPin(Event* event, const char* legend, Pin* pin); diff --git a/FluidNC/src/Machine/Homing.cpp b/FluidNC/src/Machine/Homing.cpp index fa985ccbd..d4e00ec4e 100644 --- a/FluidNC/src/Machine/Homing.cpp +++ b/FluidNC/src/Machine/Homing.cpp @@ -67,9 +67,7 @@ namespace Machine { protocol_send_event(&cycleStartEvent); } - static MotorMask limited() { - return Machine::Axes::posLimitMask | Machine::Axes::negLimitMask; - } + static MotorMask limited() { return Machine::Axes::posLimitMask | Machine::Axes::negLimitMask; } void Homing::cycleStop() { log_debug("CycleStop " << phaseName(_phase)); @@ -406,9 +404,10 @@ namespace Machine { axes->set_homing_mode(_cycleAxes, false); // tell motors homing is done } - static String axisNames(AxisMask axisMask) { - String retval = ""; - auto n_axis = config->_axes->_numberAxis; +#if 0 + static std::string axisNames(AxisMask axisMask) { + std::string retval = ""; + auto n_axis = config->_axes->_numberAxis; for (size_t axis = 0; axis < n_axis; axis++) { if (bitnum_is_true(axisMask, axis)) { retval += Machine::Axes::_names[axis]; @@ -416,6 +415,7 @@ namespace Machine { } return retval; } +#endif // Construct a list of homing cycles to run. If there are any // such cycles, enter Homing state and begin running the first diff --git a/FluidNC/src/Machine/Homing.h b/FluidNC/src/Machine/Homing.h index b218e4799..d1a780c22 100644 --- a/FluidNC/src/Machine/Homing.h +++ b/FluidNC/src/Machine/Homing.h @@ -55,7 +55,7 @@ namespace Machine { float _feed_scaler = 1.1f; // multiplier to pulloff for moving to switch after pulloff // Configuration system helpers: - void validate() const override { Assert(_cycle >= set_mpos_only, "Homing cycle must be defined"); } + void validate() override { Assert(_cycle >= set_mpos_only, "Homing cycle must be defined"); } void group(Configuration::HandlerBase& handler) override { handler.item("cycle", _cycle, set_mpos_only, MAX_N_AXIS); diff --git a/FluidNC/src/Machine/I2CBus.cpp b/FluidNC/src/Machine/I2CBus.cpp index 46265d26b..285c4bbbf 100644 --- a/FluidNC/src/Machine/I2CBus.cpp +++ b/FluidNC/src/Machine/I2CBus.cpp @@ -7,7 +7,7 @@ namespace Machine { I2CBus::I2CBus(int busNumber) : _busNumber(busNumber) {} - void I2CBus::validate() const { + void I2CBus::validate() { if (_sda.defined() || _scl.defined()) { Assert(_sda.defined(), "I2C SDA pin configured multiple times"); Assert(_scl.defined(), "I2C SCL pin configured multiple times"); diff --git a/FluidNC/src/Machine/I2CBus.h b/FluidNC/src/Machine/I2CBus.h index 32fbc05da..27aa7c2aa 100644 --- a/FluidNC/src/Machine/I2CBus.h +++ b/FluidNC/src/Machine/I2CBus.h @@ -23,7 +23,7 @@ namespace Machine { uint32_t _frequency = 100000; void init(); - void validate() const override; + void validate() override; void group(Configuration::HandlerBase& handler) override; int write(uint8_t address, const uint8_t* data, size_t count); diff --git a/FluidNC/src/Machine/I2SOBus.cpp b/FluidNC/src/Machine/I2SOBus.cpp index a64172499..10394679d 100644 --- a/FluidNC/src/Machine/I2SOBus.cpp +++ b/FluidNC/src/Machine/I2SOBus.cpp @@ -6,7 +6,7 @@ #include "../I2SOut.h" namespace Machine { - void I2SOBus::validate() const { + void I2SOBus::validate() { if (_bck.defined() || _data.defined() || _ws.defined()) { Assert(_bck.defined(), "I2SO BCK pin should be configured once"); Assert(_data.defined(), "I2SO Data pin should be configured once"); diff --git a/FluidNC/src/Machine/I2SOBus.h b/FluidNC/src/Machine/I2SOBus.h index 153085196..da3c31367 100644 --- a/FluidNC/src/Machine/I2SOBus.h +++ b/FluidNC/src/Machine/I2SOBus.h @@ -15,7 +15,7 @@ namespace Machine { Pin _data; Pin _ws; - void validate() const override; + void validate() override; void group(Configuration::HandlerBase& handler) override; void init(); diff --git a/FluidNC/src/Machine/LimitPin.cpp b/FluidNC/src/Machine/LimitPin.cpp index 58be4cb36..c4a10008b 100644 --- a/FluidNC/src/Machine/LimitPin.cpp +++ b/FluidNC/src/Machine/LimitPin.cpp @@ -9,7 +9,7 @@ namespace Machine { LimitPin::LimitPin(Pin& pin, int axis, int motor, int direction, bool& pHardLimits, bool& pLimited) : EventPin(&limitEvent, "Limit", &pin), _axis(axis), _motorNum(motor), _value(false), _pHardLimits(pHardLimits), _pLimited(pLimited) { - String sDir; + const char* sDir; // Select one or two bitmask variables to receive the switch data switch (direction) { case 1: @@ -38,7 +38,9 @@ namespace Machine { // The bitmap looks like CBAZYX..cbazyx where motor0 motors are in the lower bits _bitmask = 1 << Axes::motor_bit(axis, motor); _legend = config->_axes->motorMaskToNames(_bitmask); - _legend += " " + sDir + " Limit"; + _legend += " "; + _legend += sDir; + _legend += " Limit"; } void LimitPin::init() { diff --git a/FluidNC/src/Machine/MachineConfig.cpp b/FluidNC/src/Machine/MachineConfig.cpp index b49b36f4c..11b16ee77 100644 --- a/FluidNC/src/Machine/MachineConfig.cpp +++ b/FluidNC/src/Machine/MachineConfig.cpp @@ -65,7 +65,7 @@ namespace Machine { handler.section("user_outputs", _userOutputs); handler.section("oled", _oled); - + Spindles::SpindleFactory::factory(handler, _spindles); // TODO: Consider putting these under a gcode: hierarchy level? Or motion control? @@ -221,7 +221,7 @@ namespace Machine { Configuration::AfterParse afterParse; config->afterParse(); config->group(afterParse); - } catch (std::exception& ex) { log_info("Validation error: " << ex.what()); } + } catch (std::exception& ex) { log_error("Validation error: " << ex.what()); } log_debug("Checking configuration"); @@ -229,14 +229,14 @@ namespace Machine { Configuration::Validator validator; config->validate(); config->group(validator); - } catch (std::exception& ex) { log_info("Validation error: " << ex.what()); } + } catch (std::exception& ex) { log_error("Validation error: " << ex.what()); } // log_info("Heap size after configuation load is " << uint32_t(xPortGetFreeHeapSize())); successful = (sys.state != State::ConfigAlarm); if (!successful) { - log_info("Configuration is invalid"); + log_error("Configuration is invalid"); } } catch (const Configuration::ParseException& ex) { diff --git a/FluidNC/src/Machine/MachineConfig.h b/FluidNC/src/Machine/MachineConfig.h index 16c0673e7..8f5ff270c 100644 --- a/FluidNC/src/Machine/MachineConfig.h +++ b/FluidNC/src/Machine/MachineConfig.h @@ -93,9 +93,9 @@ namespace Machine { // Tracks and reports gcode line numbers. Disabled by default. bool _useLineNumbers = false; - String _board = "None"; - String _name = "None"; - String _meta = ""; + std::string _board = "None"; + std::string _name = "None"; + std::string _meta = ""; #if 1 static MachineConfig*& instance() { static MachineConfig* instance = nullptr; diff --git a/FluidNC/src/Machine/Macros.cpp b/FluidNC/src/Machine/Macros.cpp index 381a955d3..a493af8ea 100644 --- a/FluidNC/src/Machine/Macros.cpp +++ b/FluidNC/src/Machine/Macros.cpp @@ -51,7 +51,7 @@ bool Macros::run_macro(size_t index) { if (index >= n_macros) { return false; } - String macro = _macro[index]; + auto macro = _macro[index]; if (macro == "") { return true; } diff --git a/FluidNC/src/Machine/Macros.h b/FluidNC/src/Machine/Macros.h index 905c618dc..ac785b10f 100644 --- a/FluidNC/src/Machine/Macros.h +++ b/FluidNC/src/Machine/Macros.h @@ -8,6 +8,7 @@ #include "src/WebUI/InputBuffer.h" // WebUI::inputBuffer #include "src/UartChannel.h" #include "src/Event.h" +#include class MacroEvent : public Event { int _num; @@ -29,25 +30,25 @@ namespace Machine { static const int n_macros = 4; private: - String _startup_line[n_startup_lines]; - String _macro[n_macros]; + std::string _startup_line[n_startup_lines]; + std::string _macro[n_macros]; public: Macros() = default; bool run_macro(size_t index); - String startup_line(size_t index) { + std::string startup_line(size_t index) { if (index >= n_startup_lines) { return ""; } - String s = _startup_line[index]; + auto s = _startup_line[index]; if (s == "") { return s; } // & is a proxy for newlines in startup lines, because you cannot // enter a newline directly in a config file string value. - s.replace('&', '\n'); + std::replace(s.begin(), s.end(), '&', '\n'); return s + "\n"; } diff --git a/FluidNC/src/Machine/SPIBus.cpp b/FluidNC/src/Machine/SPIBus.cpp index a6d961e62..cdbd4a85d 100644 --- a/FluidNC/src/Machine/SPIBus.cpp +++ b/FluidNC/src/Machine/SPIBus.cpp @@ -8,7 +8,7 @@ #include "src/SettingsDefinitions.h" namespace Machine { - void SPIBus::validate() const { + void SPIBus::validate() { if (_miso.defined() || _mosi.defined() || _sck.defined()) { Assert(_miso.defined(), "SPI MISO pin should be configured once"); Assert(_mosi.defined(), "SPI MOSI pin should be configured once"); diff --git a/FluidNC/src/Machine/SPIBus.h b/FluidNC/src/Machine/SPIBus.h index 84f8a7891..f8a8ae3c9 100644 --- a/FluidNC/src/Machine/SPIBus.h +++ b/FluidNC/src/Machine/SPIBus.h @@ -15,7 +15,7 @@ namespace Machine { Pin _mosi; Pin _sck; - void validate() const override; + void validate() override; void group(Configuration::HandlerBase& handler) override; void afterParse() override; diff --git a/FluidNC/src/Machine/WifiAPConfig.h b/FluidNC/src/Machine/WifiAPConfig.h index 108e22e0f..6b54152fc 100644 --- a/FluidNC/src/Machine/WifiAPConfig.h +++ b/FluidNC/src/Machine/WifiAPConfig.h @@ -14,7 +14,7 @@ namespace Machine { int _channel = 1; - void validate() const override { + void validate() override { WifiConfig::validate(); Assert(_channel >= 1 && _channel <= 16, "WIFI channel %d is out of bounds", _channel); // TODO: I guess? } diff --git a/FluidNC/src/Machine/WifiConfig.h b/FluidNC/src/Machine/WifiConfig.h index ee4a4593b..fe130d871 100644 --- a/FluidNC/src/Machine/WifiConfig.h +++ b/FluidNC/src/Machine/WifiConfig.h @@ -16,17 +16,13 @@ namespace Machine { WifiConfig() : _ipAddress(10, 0, 0, 1), _gateway(10, 0, 0, 1), _netmask(255, 255, 0, 0) {} - String _ssid = "FluidNC"; - - // Passwords don't belong in a YAML! - // String _password = "12345678"; + std::string _ssid = "FluidNC"; bool _dhcp = true; void group(Configuration::HandlerBase& handler) override { handler.item("ssid", _ssid); - // handler.item("password", _password); - + // No passwords in the config file! handler.item("ip_address", _ipAddress); handler.item("gateway", _gateway); handler.item("netmask", _netmask); diff --git a/FluidNC/src/Machine/WifiSTAConfig.h b/FluidNC/src/Machine/WifiSTAConfig.h index 453f53658..5a5990efa 100644 --- a/FluidNC/src/Machine/WifiSTAConfig.h +++ b/FluidNC/src/Machine/WifiSTAConfig.h @@ -12,7 +12,7 @@ namespace Machine { public: WifiSTAConfig() = default; - void validate() const override { WifiConfig::validate(); } + void validate() override { WifiConfig::validate(); } void group(Configuration::HandlerBase& handler) override { WifiConfig::group(handler); } diff --git a/FluidNC/src/Main.cpp b/FluidNC/src/Main.cpp index 5647064b6..1462e3c95 100644 --- a/FluidNC/src/Main.cpp +++ b/FluidNC/src/Main.cpp @@ -30,6 +30,7 @@ extern void make_user_commands(); void setup() { disableCore0WDT(); try { + timing_init(); uartInit(); // Setup serial port Uart0.println(); // create some white space after ESP32 boot info diff --git a/FluidNC/src/Main.h b/FluidNC/src/Main.h index 02a0fc9bd..c75e0f919 100644 --- a/FluidNC/src/Main.h +++ b/FluidNC/src/Main.h @@ -3,7 +3,5 @@ #pragma once -#include - void main_init(); void run_once(); diff --git a/FluidNC/src/MotionControl.cpp b/FluidNC/src/MotionControl.cpp index 4afcff0c8..9c986f8dd 100644 --- a/FluidNC/src/MotionControl.cpp +++ b/FluidNC/src/MotionControl.cpp @@ -102,7 +102,9 @@ void mc_cancel_jog() { // unless invert_feed_rate is true. Then the feed_rate means that the motion should be completed in // (1 minute)/feed_rate time. bool mc_linear(float* target, plan_line_data_t* pl_data, float* position) { - limits_soft_check(target); + if (!pl_data->is_jog) { // soft limits for jogs have already been dealt with + limits_soft_check(target); + } return config->_kinematics->cartesian_to_motors(target, pl_data, position); } diff --git a/FluidNC/src/Motors/Dynamixel2.cpp b/FluidNC/src/Motors/Dynamixel2.cpp index b1f21e0fd..0c01cc258 100644 --- a/FluidNC/src/Motors/Dynamixel2.cpp +++ b/FluidNC/src/Motors/Dynamixel2.cpp @@ -24,19 +24,20 @@ #include namespace MotorDrivers { - Uart* MotorDrivers::Dynamixel2::_uart = nullptr; - uint8_t MotorDrivers::Dynamixel2::_first_id = 0; - uint8_t MotorDrivers::Dynamixel2::_last_id = 0; + Uart* Dynamixel2::_uart = nullptr; + TimerHandle_t Dynamixel2::_timer = nullptr; + std::vector Dynamixel2::_instances; + bool Dynamixel2::_has_errors = false; - uint8_t MotorDrivers::Dynamixel2::bulk_message[100]; - uint8_t MotorDrivers::Dynamixel2::bulk_message_index; + int Dynamixel2::_timer_ms = 75; - uint8_t MotorDrivers::Dynamixel2::_dxl_rx_message[50] = {}; // received from dynamixel + uint8_t Dynamixel2::_tx_message[100]; // send to dynamixel + uint8_t Dynamixel2::_rx_message[50]; // received from dynamixel + uint8_t Dynamixel2::_msg_index = 0; // Current length of message being constructed - bool MotorDrivers::Dynamixel2::_uart_started = false; + bool Dynamixel2::_uart_started = false; void Dynamixel2::init() { - _has_errors = false; // Initially assume okay _axis_index = axis_index(); if (!_uart_started) { @@ -52,17 +53,13 @@ namespace MotorDrivers { return; } _uart_started = true; + schedule_update(this, _timer_ms); } - // for bulk updating - if (_first_id == 0) { - _first_id = _id; - } - _last_id = _id; - config_message(); // print the config - startUpdateTask(_timer_ms); + // for bulk updating + _instances.push_back(this); } void Dynamixel2::config_motor() { @@ -85,20 +82,22 @@ namespace MotorDrivers { } bool Dynamixel2::test() { - uint16_t len = 3; - - _dxl_tx_message[DXL_MSG_INSTR] = DXL_INSTR_PING; + start_message(_id, DXL_INSTR_PING); + finish_message(); - dxl_finish_message(_id, _dxl_tx_message, len); - len = dxl_get_response(PING_RSP_LEN); // wait for and get response + uint16_t len = dxl_get_response(PING_RSP_LEN); // wait for and get response if (len == PING_RSP_LEN) { - uint16_t model_num = _dxl_rx_message[10] << 8 | _dxl_rx_message[9]; + uint16_t model_num = _rx_message[10] << 8 | _rx_message[9]; + uint8_t fw_rev = _rx_message[11]; + std::string msg("Axis ping reply "); + msg += axisName().c_str(); if (model_num == 1060) { - log_info("Axis ping reply " << axisName() << " Model XL430-W250 F/W Rev " << String(_dxl_rx_message[11], HEX)); + msg += " Model XL430-W250"; } else { - log_info("Axis ping reply " << axisName() << " M/N " << model_num << " F/W Rev " << String(_dxl_rx_message[11], HEX)); + msg += " M/N " + std::to_string(model_num); } + log_info(msg << " F/W Rev " << to_hex(fw_rev)); } else { log_warn(" Ping failed"); return false; @@ -111,48 +110,55 @@ namespace MotorDrivers { // sets the PWM to zero. This allows most servos to be manually moved void IRAM_ATTR Dynamixel2::set_disable(bool disable) { - uint8_t param_count = 1; - if (_disabled == disable) { return; } _disabled = disable; - dxl_write(DXL_ADDR_TORQUE_EN, param_count, !_disabled); + start_write(DXL_ADDR_TORQUE_EN); + add_uint8(!disable); + finish_write(); } void Dynamixel2::set_operating_mode(uint8_t mode) { - uint8_t param_count = 1; - dxl_write(DXL_OPERATING_MODE, param_count, mode); + start_write(DXL_OPERATING_MODE); + add_uint8(mode); + finish_write(); } - void Dynamixel2::update() { + // This is static; it updates the positions of all the Dynamixels on the UART bus + void Dynamixel2::update_all() { if (_has_errors) { return; } - if (_disabled) { - // TO DO need to properly time this. Currently it would read too fast. - // Needs delay between reads or bulk read. - //dxl_read_position(); - } else { - if (_id == _last_id) { // from a LIFO List - // initialize message - bulk_message_index = DXL_MSG_INSTR; - - bulk_message[bulk_message_index] = DXL_SYNC_WRITE; - bulk_message[++bulk_message_index] = DXL_GOAL_POSITION & 0xFF; // low order address - bulk_message[++bulk_message_index] = (DXL_GOAL_POSITION & 0xFF00) >> 8; // high order address - bulk_message[++bulk_message_index] = 4; // low order data length - bulk_message[++bulk_message_index] = 0; // high order data length - } - add_to_bulk_message(); - if (_id == _first_id) { - send_bulk_message(); - } + start_message(DXL_BROADCAST_ID, DXL_SYNC_WRITE); + add_uint16(DXL_GOAL_POSITION); + add_uint16(4); // data length + + float* mpos = get_mpos(); + float motors[MAX_N_AXIS]; + config->_kinematics->transform_cartesian_to_motors(motors, mpos); + + for (const auto& instance : _instances) { + float dxl_count_min, dxl_count_max; + uint32_t dxl_position; + + dxl_count_min = float(instance->_countMin); + dxl_count_max = float(instance->_countMax); + + // map the mm range to the servo range + auto axis_index = instance->_axis_index; + dxl_position = static_cast(mapConstrain( + motors[axis_index], limitsMinPosition(axis_index), limitsMaxPosition(axis_index), dxl_count_min, dxl_count_max)); + + add_uint8(instance->_id); // ID of the servo + add_uint32(dxl_position); } + finish_message(); } + void Dynamixel2::update() { update_all(); } void Dynamixel2::set_location() {} @@ -171,15 +177,48 @@ namespace MotorDrivers { return false; // Cannot do conventional homing } + void Dynamixel2::add_uint8(uint8_t n) { _tx_message[_msg_index++] = n & 0xff; } + void Dynamixel2::add_uint16(uint16_t n) { + add_uint8(n); + add_uint8(n >> 8); + } + void Dynamixel2::add_uint32(uint32_t n) { + add_uint16(n); + add_uint16(n >> 16); + } + + void Dynamixel2::start_message(uint8_t id, uint8_t instr) { + _msg_index = 0; + add_uint8(0xFF); // HDR1 + add_uint8(0xFF); // HDR2 + add_uint8(0xFD); // HDR3 + add_uint8(0x00); // reserved + add_uint8(id); // ID + _msg_index += 2; // Length goes here, filled in later + add_uint8(instr); // ID + } + void Dynamixel2::finish_message() { + // length is the number of bytes after the INSTR, including the CRC + uint16_t msg_len = _msg_index - DXL_MSG_INSTR + 2; + + _tx_message[DXL_MSG_LEN_L] = msg_len & 0xff; + _tx_message[DXL_MSG_LEN_H] = (msg_len >> 8) * 0xff; + + uint16_t crc = 0; + crc = dxl_update_crc(crc, _tx_message, _msg_index); + + add_uint16(crc); + + _uart->flushRx(); + _uart->write(_tx_message, _msg_index); + + //hex_msg(_tx_message, "0x", _msg_index); + } + void Dynamixel2::dxl_goal_position(int32_t position) { - uint8_t param_count = 4; - - dxl_write(DXL_GOAL_POSITION, - param_count, - (position & 0xFF), - (position & 0xFF00) >> 8, - (position & 0xFF0000) >> 16, - (position & 0xFF000000) >> 24); + start_write(DXL_GOAL_POSITION); + add_uint32(position); + finish_write(); } uint32_t Dynamixel2::dxl_read_position() { @@ -190,8 +229,7 @@ namespace MotorDrivers { data_len = dxl_get_response(15); if (data_len == 15) { - uint32_t dxl_position = _dxl_rx_message[9] | (_dxl_rx_message[10] << 8) | (_dxl_rx_message[11] << 16) | - (_dxl_rx_message[12] << 24); + uint32_t dxl_position = _rx_message[9] | (_rx_message[10] << 8) | (_rx_message[11] << 16) | (_rx_message[12] << 24); auto axis = config->_axes->_axis[_axis_index]; @@ -211,152 +249,72 @@ namespace MotorDrivers { } void Dynamixel2::dxl_read(uint16_t address, uint16_t data_len) { - uint8_t msg_len = 3 + 4; - - _dxl_tx_message[DXL_MSG_INSTR] = DXL_READ; - _dxl_tx_message[DXL_MSG_START] = (address & 0xFF); // low-order address value - _dxl_tx_message[DXL_MSG_START + 1] = ((address & 0xFF00) >> 8); // High-order address value - _dxl_tx_message[DXL_MSG_START + 2] = (data_len & 0xFF); // low-order data length value - _dxl_tx_message[DXL_MSG_START + 3] = ((data_len & 0xFF00) >> 8); // high-order address value - - dxl_finish_message(_id, _dxl_tx_message, msg_len); + start_message(_id, DXL_READ); + add_uint16(address); + add_uint16(data_len); + finish_message(); } + void Dynamixel2::start_write(uint16_t address) { + start_message(_id, DXL_WRITE); + add_uint16(address); + } + void Dynamixel2::finish_write() { + finish_message(); + show_status(); + } void Dynamixel2::LED_on(bool on) { - uint8_t param_count = 1; - - if (on) - dxl_write(DXL_ADDR_LED_ON, param_count, 1); - else - dxl_write(DXL_ADDR_LED_ON, param_count, 0); + start_write(DXL_ADDR_LED_ON); + add_uint8(on); + finish_write(); } // wait for and get the servo response - uint16_t Dynamixel2::dxl_get_response(uint16_t length) { - length = _uart->timedReadBytes((char*)_dxl_rx_message, length, DXL_RESPONSE_WAIT_TICKS); - return length; + size_t Dynamixel2::dxl_get_response(uint16_t length) { + return _uart->timedReadBytes((char*)_rx_message, length, DXL_RESPONSE_WAIT_TICKS); } - void Dynamixel2::dxl_write(uint16_t address, uint8_t paramCount, ...) { - _dxl_tx_message[DXL_MSG_INSTR] = DXL_WRITE; - _dxl_tx_message[DXL_MSG_START] = (address & 0xFF); // low-order address value - _dxl_tx_message[DXL_MSG_START + 1] = ((address & 0xFF00) >> 8); // High-order address value - - uint8_t msg_offset = 1; // this is the offset from DXL_MSG_START in the message - - va_list valist; - - /* Initializing arguments */ - va_start(valist, paramCount); - - for (int x = 0; x < paramCount; x++) { - msg_offset++; - _dxl_tx_message[DXL_MSG_START + msg_offset] = (uint8_t)va_arg(valist, int); - } - va_end(valist); // Cleans up the list - - dxl_finish_message(_id, _dxl_tx_message, msg_offset + 4); - - uint16_t len = 11; // response length - len = dxl_get_response(len); - - if (len == 11) { - uint8_t err = _dxl_rx_message[8]; - switch (err) { - case 1: - log_error(name() << " ID " << _id << " Write fail error"); - break; - case 2: - log_error(name() << " ID " << _id << " Write instruction error"); - break; - case 3: - log_error(name() << " ID " << _id << " CRC Error"); - break; - case 4: - log_error(name() << " ID " << _id << " Write data range error"); - break; - case 5: - log_error(name() << " ID " << _id << " Write data length error"); - break; - case 6: - log_error(name() << " ID " << _id << " Write data limit error"); - break; - case 7: - log_error(name() << " ID " << _id << " Write access error addr:" << address); - break; - default: - break; - } - } else { - // timeout + void Dynamixel2::show_status() { + size_t len = dxl_get_response(11); + if (len != 11) { log_error(name() << " ID " << _id << " Timeout"); + return; + } + uint8_t err = _rx_message[DXL_MSG_START]; + if (!err) { + return; } - } - - void Dynamixel2::add_to_bulk_message() { - float dxl_count_min, dxl_count_max; - uint32_t dxl_position; - - float* mpos = get_mpos(); - float motors[MAX_N_AXIS]; - - dxl_count_min = float(_countMin); - dxl_count_max = float(_countMax); - - config->_kinematics->transform_cartesian_to_motors(motors, mpos); - - // map the mm range to the servo range - dxl_position = static_cast(mapConstrain( - motors[_axis_index], limitsMinPosition(_axis_index), limitsMaxPosition(_axis_index), dxl_count_min, dxl_count_max)); - - bulk_message[++bulk_message_index] = _id; // ID of the servo - bulk_message[++bulk_message_index] = dxl_position & 0xFF; // data - bulk_message[++bulk_message_index] = (dxl_position & 0xFF00) >> 8; // data - bulk_message[++bulk_message_index] = (dxl_position & 0xFF0000) >> 16; // data - bulk_message[++bulk_message_index] = (dxl_position & 0xFF000000) >> 24; // data - } - - void Dynamixel2::send_bulk_message() { - //static uint64_t ping = esp_timer_get_time() / 1000; - //log_debug("Ping:" << esp_timer_get_time() / 1000 - ping); - //ping = esp_timer_get_time() / 1000; - dxl_finish_message(DXL_BROADCAST_ID, bulk_message, bulk_message_index - DXL_MSG_INSTR + 3); - } - - /* - Static - - This is a helper function to complete and send the message - The body of the message should be in msg, at the correct location - before calling this function. - This function will add the header, length bytes and CRC - It will then send the message -*/ - void Dynamixel2::dxl_finish_message(uint8_t id, uint8_t* msg, uint16_t msg_len) { - uint16_t crc = 0; - // header - msg[DXL_MSG_HDR1] = char(0xFF); - msg[DXL_MSG_HDR2] = char(0xFF); - msg[DXL_MSG_HDR3] = char(0xFD); - // - // reserved - msg[DXL_MSG_RSRV] = 0x00; - msg[DXL_MSG_ID] = id; - // length - msg[DXL_MSG_LEN_L] = msg_len & 0xFF; - msg[DXL_MSG_LEN_H] = (msg_len & 0xFF00) >> 8; - - // the message should already be here - - crc = dxl_update_crc(crc, msg, 5 + msg_len); - - msg[msg_len + 5] = crc & 0xFF; // CRC_L - msg[msg_len + 6] = (crc & 0xFF00) >> 8; - - _uart->flushRx(); - _uart->write(msg, msg_len + 7); - //hex_msg(msg, "0x", msg_len + 7); + std::string msg(name()); + msg += " ID " + _rx_message[DXL_MSG_ID]; + + switch (err) { + case 1: + msg += " Write fail error"; + break; + case 2: + msg += " Write instruction error"; + break; + case 3: + msg += " CRC Error"; + break; + case 4: + msg += " Write data range error"; + break; + case 5: + msg += " Write data length error"; + break; + case 6: + msg += " Write data limit error"; + break; + case 7: + msg += " Write access error addr:" + std::to_string(_rx_message[DXL_MSG_INSTR]); + break; + default: + msg += " Unknown error code:" + std::to_string(err); + break; + } + log_error(msg); } // from http://emanual.robotis.com/docs/en/dxl/crc/ diff --git a/FluidNC/src/Motors/Dynamixel2.h b/FluidNC/src/Motors/Dynamixel2.h index 9da9cb228..eabe096f4 100644 --- a/FluidNC/src/Motors/Dynamixel2.h +++ b/FluidNC/src/Motors/Dynamixel2.h @@ -21,28 +21,39 @@ namespace MotorDrivers { void set_location(); - uint8_t _id; - uint8_t _dxl_tx_message[50]; // outgoing to dynamixel - static uint8_t _dxl_rx_message[50]; // received from dynamixel + uint8_t _id; + + static int _timer_ms; + + static uint8_t _tx_message[100]; // outgoing to dynamixel + static uint8_t _msg_index; + static uint8_t _rx_message[50]; // received from dynamixel + + static void start_message(uint8_t id, uint8_t instr); + static void finish_message(); + static void add_uint8(uint8_t n); + static void add_uint16(uint16_t n); + static void add_uint32(uint32_t n); + + void start_write(uint16_t address); + void finish_write(); + void show_status(); bool test(); uint32_t dxl_read_position(); void dxl_read(uint16_t address, uint16_t data_len); - void dxl_write(uint16_t address, uint8_t paramCount, ...); - void dxl_goal_position(int32_t position); // set one motor - void set_operating_mode(uint8_t mode); - void LED_on(bool on); - static void dxl_finish_message(uint8_t id, uint8_t* msg, uint16_t msg_len); - static uint16_t dxl_get_response(uint16_t length); + void dxl_goal_position(int32_t position); // set one motor + void set_operating_mode(uint8_t mode); + void LED_on(bool on); + + size_t dxl_get_response(uint16_t length); + static uint16_t dxl_update_crc(uint16_t crc_accum, uint8_t* data_blk_ptr, uint8_t data_blk_size); - // static things for the bulk position command (set all axes at one time) - static void init_bulk_message(); - void add_to_bulk_message(); - static void send_bulk_message(); - static uint8_t bulk_message[100]; - static uint8_t bulk_message_index; + static TimerHandle_t _timer; + + static std::vector _instances; int _axis_index; @@ -50,9 +61,6 @@ namespace MotorDrivers { int _uart_num = -1; - static uint8_t _first_id; - static uint8_t _last_id; - static bool _uart_started; static const int DXL_RESPONSE_WAIT_TICKS = 20; // how long to wait for a response @@ -91,22 +99,25 @@ namespace MotorDrivers { uint32_t _countMin = 1024; uint32_t _countMax = 3072; - bool _disabled; - bool _has_errors; + bool _disabled; + static bool _has_errors; public: - Dynamixel2() : _id(255), _disabled(true), _has_errors(true) {} + Dynamixel2() : _id(255), _disabled(true) {} // Overrides for inherited methods - void init() override; - void read_settings() override; - bool set_homing_mode(bool isHoming) override; - void set_disable(bool disable) override; - void update() override; - void config_motor() override; + void init() override; + void read_settings() override; + bool set_homing_mode(bool isHoming) override; + void set_disable(bool disable) override; + void update() override; + static void update_all(); + void config_motor() override; + + const char* name() override { return "dynamixel2"; } // Configuration handlers: - void validate() const override { + void validate() override { Assert(_uart_num != -1, "Dynamixel: Missing uart_num configuration"); Assert(_id != 255, "Dynamixel: ID must be configured."); } @@ -114,10 +125,11 @@ namespace MotorDrivers { void group(Configuration::HandlerBase& handler) override { handler.item("uart_num", _uart_num); handler.item("id", _id); - handler.item("count_min", _countMin); handler.item("count_max", _countMax); handler.item("timer_ms", _timer_ms); + + Servo::group(handler); } // Name of the configurable. Must match the name registered in the cpp file. diff --git a/FluidNC/src/Motors/MotorDriver.cpp b/FluidNC/src/Motors/MotorDriver.cpp index 0a6bdf32f..47b5aaf37 100644 --- a/FluidNC/src/Motors/MotorDriver.cpp +++ b/FluidNC/src/Motors/MotorDriver.cpp @@ -26,8 +26,8 @@ #include "../Limits.h" // limitsMinPosition namespace MotorDrivers { - String MotorDriver::axisName() const { - return String(config->_axes->axisName(axis_index())) + (dual_axis_index() ? "2" : "") + " Axis"; + std::string MotorDriver::axisName() const { + return std::string(1, config->_axes->axisName(axis_index())) + (dual_axis_index() ? "2" : "") + " Axis"; } void MotorDriver::debug_message() {} diff --git a/FluidNC/src/Motors/MotorDriver.h b/FluidNC/src/Motors/MotorDriver.h index 159ef6704..3ec8d9d5f 100644 --- a/FluidNC/src/Motors/MotorDriver.h +++ b/FluidNC/src/Motors/MotorDriver.h @@ -87,11 +87,6 @@ namespace MotorDrivers { // TODO Architecture: Should this be private? virtual bool test(); - // update() is used for some types of "smart" motors that - // can be told to move to a specific position. It is - // called from a periodic task. - virtual void update() {} - // Name is required for the configuration factory to work. virtual const char* name() const = 0; @@ -102,7 +97,7 @@ namespace MotorDrivers { virtual ~MotorDriver() {} protected: - String axisName() const; + std::string axisName() const; // config_message(), called from init(), displays a message describing // the motor configuration - pins and other motor-specific items diff --git a/FluidNC/src/Motors/RcServo.cpp b/FluidNC/src/Motors/RcServo.cpp index dabbd9d33..11bafebe6 100644 --- a/FluidNC/src/Motors/RcServo.cpp +++ b/FluidNC/src/Motors/RcServo.cpp @@ -47,7 +47,7 @@ namespace MotorDrivers { _disabled = true; - startUpdateTask(_timer_ms); + schedule_update(this, _timer_ms); } void RcServo::config_message() { diff --git a/FluidNC/src/Motors/RcServo.h b/FluidNC/src/Motors/RcServo.h index 8fb83f117..de867409f 100644 --- a/FluidNC/src/Motors/RcServo.h +++ b/FluidNC/src/Motors/RcServo.h @@ -10,6 +10,8 @@ namespace MotorDrivers { class RcServo : public Servo { protected: + int _timer_ms = 20; + void config_message() override; void set_location(); @@ -48,12 +50,17 @@ namespace MotorDrivers { void _write_pwm(uint32_t duty); + const char* name() override { return "rc_servo"; } + // Configuration handlers: void group(Configuration::HandlerBase& handler) override { handler.item("output_pin", _output_pin); handler.item("pwm_hz", _pwm_freq, SERVO_PWM_FREQ_MIN, SERVO_PWM_FREQ_MAX); handler.item("min_pulse_us", _min_pulse_us, SERVO_PULSE_US_MIN, SERVO_PULSE_US_MAX); handler.item("max_pulse_us", _max_pulse_us, SERVO_PULSE_US_MIN, SERVO_PULSE_US_MAX); + handler.item("timer_ms", _timer_ms); + + Servo::group(handler); } // Name of the configurable. Must match the name registered in the cpp file. diff --git a/FluidNC/src/Motors/Servo.cpp b/FluidNC/src/Motors/Servo.cpp index f51982ff3..01e4e82b5 100644 --- a/FluidNC/src/Motors/Servo.cpp +++ b/FluidNC/src/Motors/Servo.cpp @@ -22,50 +22,27 @@ #include // portTICK_PERIOD_MS, vTaskDelay namespace MotorDrivers { - Servo* Servo::List = NULL; - Servo::Servo() : MotorDriver() { - link = List; - List = this; - } + Servo::Servo() : MotorDriver() {} - void Servo::startUpdateTask(int ms) { - if (_timer_ms == 0 || ms < _timer_ms) { - _timer_ms = ms; - } - //log_info("Servo Update Task Started"); - if (this == List) { - xTaskCreatePinnedToCore(updateTask, // task - "servoUpdateTask", // name for task - 4096, // size of task stack - (void*)&_timer_ms, // parameters - 1, // priority - NULL, // handle - SUPPORT_TASK_CORE // core - ); - } + void Servo::update_servo(TimerHandle_t timer) { + Servo* servo = static_cast(pvTimerGetTimerID(timer)); + servo->update(); } - void Servo::updateTask(void* pvParameters) { - TickType_t xLastWakeTime; - const TickType_t xUpdate = *static_cast(pvParameters) / portTICK_PERIOD_MS; // in ticks (typically ms) - auto n_axis = config->_axes->_numberAxis; - - xLastWakeTime = xTaskGetTickCount(); // Initialise the xLastWakeTime variable with the current time. - vTaskDelay(2000); // initial delay - while (true) { // don't ever return from this or the task dies - std::atomic_thread_fence(std::memory_order::memory_order_seq_cst); // read fence for settings - //log_info("Servo update"); - for (Servo* p = List; p; p = p->link) { - p->update(); - } - - vTaskDelayUntil(&xLastWakeTime, xUpdate); - - static UBaseType_t uxHighWaterMark = 0; -#ifdef DEBUG_TASK_STACK - reportTaskStackSize(uxHighWaterMark); -#endif + void Servo::schedule_update(Servo* object, int interval) { + auto timer = xTimerCreate("", + interval, + true, // auto reload + (void*)object, + update_servo); + if (!timer) { + log_error("Failed to create timer for " << object->name()); + return; + } + if (xTimerStart(timer, 0) == pdFAIL) { + log_error("Failed to start timer for " << object->name()); } + log_info(" Update timer for " << object->name() << " at " << interval << " ms"); } } diff --git a/FluidNC/src/Motors/Servo.h b/FluidNC/src/Motors/Servo.h index 68f418f4b..36610186c 100644 --- a/FluidNC/src/Motors/Servo.h +++ b/FluidNC/src/Motors/Servo.h @@ -2,6 +2,8 @@ // Use of this source code is governed by a GPLv3 license that can be found in the LICENSE file. #pragma once +#include +#include // TimerHandle_t /* This is a base class for servo-type motors - ones that autonomously @@ -14,30 +16,15 @@ namespace MotorDrivers { class Servo : public MotorDriver { public: - int _timer_ms = 75; - Servo(); -#if 0 - // Overrides for inherited methods - void init() override; - void read_settings() override; - bool set_homing_mode(bool isHoming) override; - void IRAM_ATTR set_disable(bool disable) override; -#endif + virtual void update() = 0; // This must be implemented by derived classes - void group(Configuration::HandlerBase& handler) override { handler.item("timer_ms", _timer_ms); } + void group(Configuration::HandlerBase& handler) override {} - protected: - // Start the servo update task. Each derived subclass instance calls this - // during init(), which happens after all objects have been constructed. - // startUpdateTask(ms) finds the smallest update interval among all - // the calls, and starts the task on the final call. - void startUpdateTask(int ms); + virtual const char* name() = 0; // This must be implemented by derived classes - private: - // Linked list of servo instances, used by the servo task - static Servo* List; - Servo* link; - static void updateTask(void*); + protected: + static void update_servo(TimerHandle_t timer); + static void schedule_update(Servo* object, int interval); }; } diff --git a/FluidNC/src/Motors/Solenoid.cpp b/FluidNC/src/Motors/Solenoid.cpp index 9c8072b04..061e111fd 100644 --- a/FluidNC/src/Motors/Solenoid.cpp +++ b/FluidNC/src/Motors/Solenoid.cpp @@ -64,7 +64,7 @@ namespace MotorDrivers { _current_pwm_duty = 0; - startUpdateTask(_update_rate_ms); + schedule_update(this, _update_rate_ms); } void Solenoid::update() { set_location(); } diff --git a/FluidNC/src/Motors/Solenoid.h b/FluidNC/src/Motors/Solenoid.h index ce220d0bb..9a695f9c2 100644 --- a/FluidNC/src/Motors/Solenoid.h +++ b/FluidNC/src/Motors/Solenoid.h @@ -5,6 +5,8 @@ namespace MotorDrivers { class Solenoid : public RcServo { protected: + int _timer_ms = 50; + void config_message() override; void update() override; @@ -46,6 +48,9 @@ namespace MotorDrivers { handler.item("hold_percent", _hold_percent, 0.0f, 100.0f); handler.item("pull_ms", _pull_ms, 0, 3000); handler.item("direction_invert", _dir_invert); + handler.item("timer_ms", _timer_ms); + + Servo::group(handler); } // Name of the configurable. Must match the name registered in the cpp file. diff --git a/FluidNC/src/Motors/StandardStepper.cpp b/FluidNC/src/Motors/StandardStepper.cpp index bef63eb32..fa519ffcc 100644 --- a/FluidNC/src/Motors/StandardStepper.cpp +++ b/FluidNC/src/Motors/StandardStepper.cpp @@ -12,6 +12,7 @@ #include "../Stepping.h" // config->_stepping->_engine #include // gpio +#include // CONFIG_IDF_TARGET_* using namespace Machine; @@ -42,7 +43,7 @@ namespace MotorDrivers { .idle_level = invert_step ? RMT_IDLE_LEVEL_HIGH : RMT_IDLE_LEVEL_LOW, .carrier_duty_percent = 50, #if SOC_RMT_SUPPORT_TX_LOOP_COUNT - .loop_count = DONT_KNOW_YET_SINCE_ESP32_DOES_NOT_HAVE_IT, + .loop_count = 1, #endif .carrier_en = false, .loop_en = false, @@ -92,9 +93,16 @@ namespace MotorDrivers { void IRAM_ATTR StandardStepper::step() { if (config->_stepping->_engine == Stepping::RMT && _rmt_chan_num != RMT_CHANNEL_MAX) { +#ifdef CONFIG_IDF_TARGET_ESP32 RMT.conf_ch[_rmt_chan_num].conf1.mem_rd_rst = 1; RMT.conf_ch[_rmt_chan_num].conf1.mem_rd_rst = 0; RMT.conf_ch[_rmt_chan_num].conf1.tx_start = 1; +#endif +#ifdef CONFIG_IDF_TARGET_ESP32S3 + RMT.chnconf0[_rmt_chan_num].mem_rd_rst_n = 1; + RMT.chnconf0[_rmt_chan_num].mem_rd_rst_n = 0; + RMT.chnconf0[_rmt_chan_num].tx_start_n = 1; +#endif } else { _step_pin.on(); } @@ -115,19 +123,19 @@ namespace MotorDrivers { MotorFactory::InstanceBuilder registration("standard_stepper"); } - void StandardStepper::validate() const { + void StandardStepper::validate() { Assert(_step_pin.defined(), "Step pin must be configured."); bool isI2SO = config->_stepping->_engine == Stepping::I2S_STREAM || config->_stepping->_engine == Stepping::I2S_STATIC; if (isI2SO) { - Assert(_step_pin.name().startsWith("I2SO"), "Step pin must be an I2SO pin"); + Assert(_step_pin.name().rfind("I2SO", 0) == 0, "Step pin must be an I2SO pin"); if (_dir_pin.defined()) { - Assert((_dir_pin.name().startsWith("I2SO")), "Direction pin must be an I2SO pin"); + Assert(_dir_pin.name().rfind("I2SO", 0) == 0, "Direction pin must be an I2SO pin"); } } else { - Assert(_step_pin.name().startsWith("gpio"), "Step pin must be a GPIO pin"); + Assert(_step_pin.name().rfind("gpio", 0) == 0, "Step pin must be a GPIO pin"); if (_dir_pin.defined()) { - Assert((_dir_pin.name().startsWith("gpio")), "Direction pin must be a GPIO pin"); + Assert(_dir_pin.name().rfind("gpio", 0) == 0, "Direction pin must be a GPIO pin"); } } } diff --git a/FluidNC/src/Motors/StandardStepper.h b/FluidNC/src/Motors/StandardStepper.h index 1f4f6c0eb..1a14954ae 100644 --- a/FluidNC/src/Motors/StandardStepper.h +++ b/FluidNC/src/Motors/StandardStepper.h @@ -32,7 +32,7 @@ namespace MotorDrivers { Pin _disable_pin; // Configuration handlers: - void validate() const override; + void validate() override; void group(Configuration::HandlerBase& handler) override { handler.item("step_pin", _step_pin); diff --git a/FluidNC/src/Motors/StepStick.cpp b/FluidNC/src/Motors/StepStick.cpp index 6c68fa3cf..f09bd518a 100644 --- a/FluidNC/src/Motors/StepStick.cpp +++ b/FluidNC/src/Motors/StepStick.cpp @@ -18,7 +18,7 @@ namespace MotorDrivers { } // Configuration handlers: - void StepStick::validate() const { StandardStepper::validate(); } + void StepStick::validate() { StandardStepper::validate(); } void StepStick::group(Configuration::HandlerBase& handler) { StandardStepper::group(handler); diff --git a/FluidNC/src/Motors/StepStick.h b/FluidNC/src/Motors/StepStick.h index 72b70129a..a3de1ba93 100644 --- a/FluidNC/src/Motors/StepStick.h +++ b/FluidNC/src/Motors/StepStick.h @@ -22,7 +22,7 @@ namespace MotorDrivers { void init() override; // Configuration handlers: - void validate() const override; + void validate() override; void group(Configuration::HandlerBase& handler) override; void afterParse() override; diff --git a/FluidNC/src/Motors/TMC2130Driver.cpp b/FluidNC/src/Motors/TMC2130Driver.cpp index b0d77866c..027b69d99 100644 --- a/FluidNC/src/Motors/TMC2130Driver.cpp +++ b/FluidNC/src/Motors/TMC2130Driver.cpp @@ -21,7 +21,7 @@ namespace MotorDrivers { tmc2130 = new TMC2130Stepper(cs_id, _r_sense, _spi_index); // TODO hardwired to non daisy chain index - TrinamicSpiDriver::finalInit(); + registration(); } void TMC2130Driver::config_motor() { @@ -42,6 +42,7 @@ namespace MotorDrivers { // but the TMCStepper library expresses run current as (uint16_t) mA // and hold current as (float) fraction of run current. uint16_t run_i = (uint16_t)(_run_current * 1000.0); + tmc2130->I_scale_analog(false); // do not scale via pot tmc2130->rms_current(run_i, TrinamicSpiDriver::holdPercent()); // The TMCStepper library uses the value 0 to mean 1x microstepping diff --git a/FluidNC/src/Motors/TMC2130Driver.h b/FluidNC/src/Motors/TMC2130Driver.h index 943204f02..11c680fcc 100644 --- a/FluidNC/src/Motors/TMC2130Driver.h +++ b/FluidNC/src/Motors/TMC2130Driver.h @@ -20,7 +20,7 @@ namespace MotorDrivers { void set_disable(bool disable); void config_motor() override; void debug_message() override; - void validate() const override { StandardStepper::validate(); } + void validate() override { StandardStepper::validate(); } // Name of the configurable. Must match the name registered in the cpp file. const char* name() const override { return "tmc_2130"; } diff --git a/FluidNC/src/Motors/TMC2208Driver.cpp b/FluidNC/src/Motors/TMC2208Driver.cpp index ef9b161a7..7434b8a84 100644 --- a/FluidNC/src/Motors/TMC2208Driver.cpp +++ b/FluidNC/src/Motors/TMC2208Driver.cpp @@ -22,7 +22,7 @@ namespace MotorDrivers { tmc2208 = new TMC2209Stepper(_uart, _r_sense, _addr); - TrinamicUartDriver::finalInit(); + registration(); } void TMC2208Driver::config_motor() { @@ -41,6 +41,7 @@ namespace MotorDrivers { // but the TMCStepper library expresses run current as (uint16_t) mA // and hold current as (float) fraction of run current. uint16_t run_i = (uint16_t)(_run_current * 1000.0); + tmc2208->I_scale_analog(false); // do not scale via pot tmc2208->rms_current(run_i, TrinamicBase::holdPercent()); // The TMCStepper library uses the value 0 to mean 1x microstepping diff --git a/FluidNC/src/Motors/TMC2208Driver.h b/FluidNC/src/Motors/TMC2208Driver.h index b3b16735b..226842568 100644 --- a/FluidNC/src/Motors/TMC2208Driver.h +++ b/FluidNC/src/Motors/TMC2208Driver.h @@ -20,7 +20,7 @@ namespace MotorDrivers { void set_disable(bool disable); void config_motor() override; void debug_message() override; - void validate() const override { StandardStepper::validate(); } + void validate() override { StandardStepper::validate(); } void group(Configuration::HandlerBase& handler) override { TrinamicUartDriver::group(handler); diff --git a/FluidNC/src/Motors/TMC2209Driver.cpp b/FluidNC/src/Motors/TMC2209Driver.cpp index b8753882d..6ca5ee117 100644 --- a/FluidNC/src/Motors/TMC2209Driver.cpp +++ b/FluidNC/src/Motors/TMC2209Driver.cpp @@ -23,7 +23,7 @@ namespace MotorDrivers { tmc2209 = new TMC2209Stepper(_uart, _r_sense, _addr); - TrinamicUartDriver::finalInit(); + registration(); } void TMC2209Driver::config_motor() { @@ -42,6 +42,7 @@ namespace MotorDrivers { // but the TMCStepper library expresses run current as (uint16_t) mA // and hold current as (float) fraction of run current. uint16_t run_i = (uint16_t)(_run_current * 1000.0); + tmc2209->I_scale_analog(false); // do not scale via pot tmc2209->rms_current(run_i, TrinamicBase::holdPercent()); // The TMCStepper library uses the value 0 to mean 1x microstepping @@ -72,6 +73,15 @@ namespace MotorDrivers { break; } } + + // dump the registers. This is helpful for people migrating to the Pro version + log_debug("CHOPCONF: 0x" << to_hex(tmc2209->CHOPCONF())); + log_debug("COOLCONF: 0x" << to_hex(tmc2209->COOLCONF())); + log_debug("TPWMTHRS: 0x" << to_hex(tmc2209->TPWMTHRS())); + log_debug("TCOOLTHRS: 0x" << to_hex(tmc2209->TCOOLTHRS())); + log_debug("GCONF: 0x" << to_hex(tmc2209->GCONF())); + log_debug("PWMCONF: 0x" << to_hex(tmc2209->PWMCONF())); + log_debug("IHOLD_IRUN: 0x" << to_hex(tmc2209->IHOLD_IRUN())); } void TMC2209Driver::debug_message() { diff --git a/FluidNC/src/Motors/TMC2209Driver.h b/FluidNC/src/Motors/TMC2209Driver.h index 8e7bacc20..c6c38e4a4 100644 --- a/FluidNC/src/Motors/TMC2209Driver.h +++ b/FluidNC/src/Motors/TMC2209Driver.h @@ -20,7 +20,7 @@ namespace MotorDrivers { void set_disable(bool disable); void config_motor() override; void debug_message() override; - void validate() const override { StandardStepper::validate(); } + void validate() override { StandardStepper::validate(); } void group(Configuration::HandlerBase& handler) override { TrinamicUartDriver::group(handler); diff --git a/FluidNC/src/Motors/TMC5160Driver.cpp b/FluidNC/src/Motors/TMC5160Driver.cpp index 6da247c06..b47ce261c 100644 --- a/FluidNC/src/Motors/TMC5160Driver.cpp +++ b/FluidNC/src/Motors/TMC5160Driver.cpp @@ -21,7 +21,7 @@ namespace MotorDrivers { if (_cs_pin.capabilities().has(Pin::Capabilities::I2S)) { tmc5160->setSPISpeed(_spi_freq); } - TrinamicSpiDriver::finalInit(); + registration(); } void TMC5160Driver::config_motor() { @@ -29,9 +29,7 @@ namespace MotorDrivers { TrinamicBase::config_motor(); } - bool TMC5160Driver::test() { - return checkVersion(0x30, tmc5160->version()); - } + bool TMC5160Driver::test() { return checkVersion(0x30, tmc5160->version()); } void TMC5160Driver::set_registers(bool isHoming) { if (_has_errors) { @@ -82,13 +80,13 @@ namespace MotorDrivers { } } // dump the registers. This is helpful for people migrating to the Pro version - log_debug("CHOPCONF: 0x" << String(tmc5160->CHOPCONF(), HEX)); - log_debug("COOLCONF: 0x" << String(tmc5160->COOLCONF(), HEX)); - log_debug("THIGH: 0x" << String(tmc5160->THIGH(), HEX)); - log_debug("TCOOLTHRS: 0x" << String(tmc5160->TCOOLTHRS(), HEX)); - log_debug("GCONF: 0x" << String(tmc5160->GCONF(), HEX)); - log_debug("PWMCONF: 0x" << String(tmc5160->PWMCONF(), HEX)); - log_debug("IHOLD_IRUN: 0x" << String(tmc5160->IHOLD_IRUN(), HEX)); + log_debug("CHOPCONF: 0x" << to_hex(tmc5160->CHOPCONF())); + log_debug("COOLCONF: 0x" << to_hex(tmc5160->COOLCONF())); + log_debug("THIGH: 0x" << to_hex(tmc5160->THIGH())); + log_debug("TCOOLTHRS: 0x" << to_hex(tmc5160->TCOOLTHRS())); + log_debug("GCONF: 0x" << to_hex(tmc5160->GCONF())); + log_debug("PWMCONF: 0x" << to_hex(tmc5160->PWMCONF())); + log_debug("IHOLD_IRUN: 0x" << to_hex(tmc5160->IHOLD_IRUN())); } // Report diagnostic and tuning info diff --git a/FluidNC/src/Motors/TMC5160Driver.h b/FluidNC/src/Motors/TMC5160Driver.h index 8bd3ba185..ddc27081e 100644 --- a/FluidNC/src/Motors/TMC5160Driver.h +++ b/FluidNC/src/Motors/TMC5160Driver.h @@ -20,7 +20,7 @@ namespace MotorDrivers { void set_disable(bool disable); void config_motor() override; void debug_message() override; - void validate() const override { StandardStepper::validate(); } + void validate() override { StandardStepper::validate(); } void group(Configuration::HandlerBase& handler) override { TrinamicSpiDriver::group(handler); diff --git a/FluidNC/src/Motors/TMC5160ProDriver.cpp b/FluidNC/src/Motors/TMC5160ProDriver.cpp index 18361006a..ed1956b25 100644 --- a/FluidNC/src/Motors/TMC5160ProDriver.cpp +++ b/FluidNC/src/Motors/TMC5160ProDriver.cpp @@ -18,7 +18,7 @@ namespace MotorDrivers { if (_cs_pin.capabilities().has(Pin::Capabilities::I2S)) { tmc5160->setSPISpeed(_spi_freq); } - TrinamicSpiDriver::finalInit(); + registration(); } void TMC5160ProDriver::config_motor() { @@ -26,9 +26,7 @@ namespace MotorDrivers { TrinamicBase::config_motor(); } - bool TMC5160ProDriver::test() { - return checkVersion(0x30, tmc5160->version()); - } + bool TMC5160ProDriver::test() { return checkVersion(0x30, tmc5160->version()); } void TMC5160ProDriver::set_registers(bool isHoming) { if (_has_errors) { diff --git a/FluidNC/src/Motors/TMC5160ProDriver.h b/FluidNC/src/Motors/TMC5160ProDriver.h index 473ec40ac..c91b5443f 100644 --- a/FluidNC/src/Motors/TMC5160ProDriver.h +++ b/FluidNC/src/Motors/TMC5160ProDriver.h @@ -32,7 +32,7 @@ namespace MotorDrivers { void set_disable(bool disable); void config_motor() override; void debug_message() override; - void validate() const override { StandardStepper::validate(); } + void validate() override { StandardStepper::validate(); } void group(Configuration::HandlerBase& handler) override { //handler.item("tpfd", _tpfd, 0, 15); diff --git a/FluidNC/src/Motors/TrinamicBase.cpp b/FluidNC/src/Motors/TrinamicBase.cpp index a4202a56d..ba6324600 100644 --- a/FluidNC/src/Motors/TrinamicBase.cpp +++ b/FluidNC/src/Motors/TrinamicBase.cpp @@ -12,34 +12,18 @@ namespace MotorDrivers { { TrinamicMode::StallGuard, "StallGuard" }, EnumItem(TrinamicMode::StealthChop) }; - TrinamicBase* TrinamicBase::List = NULL; // a static list of all drivers for stallguard reporting - - // Prints StallGuard data that is useful for tuning. - void TrinamicBase::readSgTask(void* pvParameters) { - auto trinamicDriver = static_cast(pvParameters); - - TickType_t xLastWakeTime; - const TickType_t xreadSg = 200; // in ticks (typically ms) - auto n_axis = config->_axes->_numberAxis; - - xLastWakeTime = xTaskGetTickCount(); // Initialise the xLastWakeTime variable with the current time. - while (true) { // don't ever return from this or the task dies - std::atomic_thread_fence(std::memory_order::memory_order_seq_cst); // read fence for settings - if (inMotionState()) { - for (TrinamicBase* p = List; p; p = p->link) { - if (p->_stallguardDebugMode) { - //log_info("SG:" << p->_stallguardDebugMode); - p->debug_message(); - } + std::vector TrinamicBase::_instances; // static list of all drivers for stallguard reporting + + // Another approach would be to register a separate timer for each instance. + // I think that timers are cheap so having only a single timer might not buy us much + void TrinamicBase::read_sg(TimerHandle_t timer) { + if (inMotionState()) { + for (TrinamicBase* t : _instances) { + if (t->_stallguardDebugMode) { + //log_info("SG:" << t->_stallguardDebugMode); + t->debug_message(); } - } // sys.state - - vTaskDelayUntil(&xLastWakeTime, xreadSg); - - static UBaseType_t uxHighWaterMark = 0; -#ifdef DEBUG_TASK_STACK - reportTaskStackSize(uxHighWaterMark); -#endif + } } } @@ -122,7 +106,7 @@ namespace MotorDrivers { bool TrinamicBase::checkVersion(uint8_t expected, uint8_t got) { if (expected != got) { - log_error(axisName() << " TMC driver not detected - expected 0x" << String(expected, 16) << " got 0x" << String(got, 16)); + log_error(axisName() << " TMC driver not detected - expected 0x" << to_hex(expected) << " got 0x" << to_hex(got)); return false; } log_info(axisName() << " driver test passed"); @@ -158,4 +142,22 @@ namespace MotorDrivers { set_registers(false); } + void TrinamicBase::registration() { + // Display the stepper library version message once, before the first + // TMC config message. + if (_instances.empty()) { + log_debug("TMCStepper Library Ver. 0x" << to_hex(TMCSTEPPER_VERSION)); + auto timer = xTimerCreate("Stallguard", 200, true, nullptr, read_sg); + // Timer failure is not fatal because you can still use the system + if (!timer) { + log_error("Failed to create timer for stallguard"); + } else if (xTimerStart(timer, 0) == pdFAIL) { + log_error("Failed to start timer for stallguard"); + } + } + + _instances.push_back(this); + + config_message(); + } } diff --git a/FluidNC/src/Motors/TrinamicBase.h b/FluidNC/src/Motors/TrinamicBase.h index ada4fcefc..267376f5c 100644 --- a/FluidNC/src/Motors/TrinamicBase.h +++ b/FluidNC/src/Motors/TrinamicBase.h @@ -19,6 +19,11 @@ namespace MotorDrivers { extern EnumItem trinamicModes[]; class TrinamicBase : public StandardStepper { + private: + static void read_sg(TimerHandle_t); + + static std::vector _instances; + protected: uint32_t calc_tstep(float speed, float percent); @@ -44,12 +49,6 @@ namespace MotorDrivers { uint8_t _toff_stealthchop = 5; uint8_t _toff_coolstep = 3; - // Linked list of Trinamic driver instances, used by the - // StallGuard reporting task. - static TrinamicBase* List; - TrinamicBase* link; - static void readSgTask(void*); - const double fclk = 12700000.0; // Internal clock Approx (Hz) used to calculate TSTEP from homing rate float holdPercent(); @@ -67,6 +66,8 @@ namespace MotorDrivers { const char* yn(bool v) { return v ? "Y" : "N"; } + void registration(); + public: TrinamicBase() = default; diff --git a/FluidNC/src/Motors/TrinamicSpiDriver.cpp b/FluidNC/src/Motors/TrinamicSpiDriver.cpp index df4ffcd29..15d995b2c 100644 --- a/FluidNC/src/Motors/TrinamicSpiDriver.cpp +++ b/FluidNC/src/Motors/TrinamicSpiDriver.cpp @@ -35,34 +35,6 @@ namespace MotorDrivers { return cs_id; } - void TrinamicSpiDriver::finalInit() { - _has_errors = false; - - link = List; - List = this; - - // Display the stepper library version message once, before the first - // TMC config message. Link is NULL for the first TMC instance. - if (!link) { - log_debug("TMCStepper Library Ver. 0x" << String(TMCSTEPPER_VERSION, HEX)); - } - - config_message(); - - // After initializing all of the TMC drivers, create a task to - // display StallGuard data. List == this for the final instance. - if (List == this) { - xTaskCreatePinnedToCore(readSgTask, // task - "readSgTask", // name for task - 4096, // size of task stack - this, // parameters - 1, // priority - NULL, - SUPPORT_TASK_CORE // must run the task on same core - ); - } - } - /* This is the startup message showing the basic definition */ diff --git a/FluidNC/src/Motors/TrinamicSpiDriver.h b/FluidNC/src/Motors/TrinamicSpiDriver.h index bb690ea23..d56de23f4 100644 --- a/FluidNC/src/Motors/TrinamicSpiDriver.h +++ b/FluidNC/src/Motors/TrinamicSpiDriver.h @@ -49,7 +49,7 @@ namespace MotorDrivers { _spi_setup_done = true; } - void validate() const override { StandardStepper::validate(); } + void validate() override { StandardStepper::validate(); } void group(Configuration::HandlerBase& handler) override { TrinamicBase::group(handler); @@ -73,7 +73,6 @@ namespace MotorDrivers { void config_message() override; uint8_t setupSPI(); - void finalInit(); bool reportTest(uint8_t result); uint8_t toffValue(); diff --git a/FluidNC/src/Motors/TrinamicUartDriver.cpp b/FluidNC/src/Motors/TrinamicUartDriver.cpp index af4667f6b..3533f9e39 100644 --- a/FluidNC/src/Motors/TrinamicUartDriver.cpp +++ b/FluidNC/src/Motors/TrinamicUartDriver.cpp @@ -35,34 +35,6 @@ namespace MotorDrivers { << " Disable:" << _disable_pin.name() << " R:" << _r_sense); } - void TrinamicUartDriver::finalInit() { - _has_errors = false; - - link = List; - List = this; - - // Display the stepper library version message once, before the first - // TMC config message. Link is NULL for the first TMC instance. - if (!link) { - log_debug("TMCStepper Library Ver. 0x" << String(TMCSTEPPER_VERSION, HEX)); - } - - config_message(); - - // After initializing all of the TMC drivers, create a task to - // display StallGuard data. List == this for the final instance. - if (List == this) { - xTaskCreatePinnedToCore(readSgTask, // task - "readSgTask", // name for task - 4096, // size of task stack - this, // parameters - 1, // priority - NULL, - SUPPORT_TASK_CORE // must run the task on same core - ); - } - } - uint8_t TrinamicUartDriver::toffValue() { if (_disabled) { return _toff_disable; diff --git a/FluidNC/src/Motors/TrinamicUartDriver.h b/FluidNC/src/Motors/TrinamicUartDriver.h index f8d257189..081755763 100644 --- a/FluidNC/src/Motors/TrinamicUartDriver.h +++ b/FluidNC/src/Motors/TrinamicUartDriver.h @@ -28,7 +28,7 @@ namespace MotorDrivers { uint8_t _addr; // Configuration handlers: - void validate() const override { StandardStepper::validate(); } + void validate() override { StandardStepper::validate(); } void afterParse() override { StandardStepper::validate(); @@ -49,8 +49,6 @@ namespace MotorDrivers { static bool _uart_started; void config_message() override; - void finalInit(); - uint8_t toffValue(); // TO DO move to Base? private: diff --git a/FluidNC/src/Motors/UnipolarMotor.h b/FluidNC/src/Motors/UnipolarMotor.h index 456233bc6..29384b674 100644 --- a/FluidNC/src/Motors/UnipolarMotor.h +++ b/FluidNC/src/Motors/UnipolarMotor.h @@ -18,7 +18,7 @@ namespace MotorDrivers { void step() override; // Configuration handlers: - void validate() const override { + void validate() override { Assert(!_pin_phase0.undefined(), "Phase 0 pin should be configured."); Assert(!_pin_phase1.undefined(), "Phase 1 pin should be configured."); Assert(!_pin_phase2.undefined(), "Phase 2 pin should be configured."); diff --git a/FluidNC/src/MyIOStream.h b/FluidNC/src/MyIOStream.h index b29b7797f..157bc6045 100644 --- a/FluidNC/src/MyIOStream.h +++ b/FluidNC/src/MyIOStream.h @@ -8,6 +8,9 @@ #include #include "Pin.h" +#include "StringRange.h" + +std::string IP_string(uint32_t ipaddr); inline Print& operator<<(Print& lhs, char c) { lhs.print(c); @@ -19,8 +22,10 @@ inline Print& operator<<(Print& lhs, const char* v) { return lhs; } -inline Print& operator<<(Print& lhs, String v) { - lhs.print(v.c_str()); +inline Print& operator<<(Print& lhs, const StringRange& s) { + for (const char* p = s.begin(); p < s.end(); ++p) { + lhs.print(*p); + } return lhs; } @@ -55,12 +60,12 @@ inline Print& operator<<(Print& lhs, double v) { } inline Print& operator<<(Print& lhs, const Pin& v) { - lhs.print(v.name()); + lhs.print(v.name().c_str()); return lhs; } -inline Print& operator<<(Print& lhs, IPAddress a) { - lhs.print(a.toString()); +inline Print& operator<<(Print& lhs, IPAddress v) { + lhs.print(IP_string(v).c_str()); return lhs; } diff --git a/FluidNC/src/NutsBolts.cpp b/FluidNC/src/NutsBolts.cpp index 2e16090e2..fb7271d4e 100644 --- a/FluidNC/src/NutsBolts.cpp +++ b/FluidNC/src/NutsBolts.cpp @@ -9,6 +9,8 @@ #include #include #include +#include +#include const int MAX_INT_DIGITS = 8; // Maximum number of digits in int32 (and float) @@ -92,10 +94,6 @@ bool read_float(const char* line, size_t* char_counter, float* float_ptr) { return true; } -void IRAM_ATTR delay_us(int32_t us) { - spinUntil(usToEndTicks(us)); -} - void delay_ms(uint16_t ms) { vTaskDelay(ms / portTICK_PERIOD_MS); } @@ -223,26 +221,51 @@ const char* to_hex(uint32_t n) { return hexstr; } -String formatBytes(uint64_t bytes) { +std::string formatBytes(uint64_t bytes) { if (bytes < 1024) { - return String((uint16_t)bytes) + " B"; + return std::to_string((uint16_t)bytes) + " B"; } float b = bytes; b /= 1024; if (b < 1024) { - return String(b, 2) + " KB"; + std::ostringstream msg; + msg << std::fixed << std::setprecision(2) << b << " KB"; + return msg.str(); } b /= 1024; if (b < 1024) { - return String(b, 2) + " MB"; + std::ostringstream msg; + msg << std::fixed << std::setprecision(2) << b << " MB"; + return msg.str(); } b /= 1024; if (b < 1024) { - return String(b, 2) + " GB"; + std::ostringstream msg; + msg << std::fixed << std::setprecision(2) << b << " TB"; + return msg.str(); } b /= 1024; if (b > 99999) { b = 99999; } - return String(b, 2) + " TB"; + std::ostringstream msg; + msg << std::fixed << std::setprecision(2) << b << " GB"; + return msg.str(); +} + +std::string IP_string(uint32_t ipaddr) { + std::string retval; + retval += std::to_string(uint8_t((ipaddr >> 00) & 0xff)) + "."; + retval += std::to_string(uint8_t((ipaddr >> 8) & 0xff)) + "."; + retval += std::to_string(uint8_t((ipaddr >> 16) & 0xff)) + "."; + retval += std::to_string(uint8_t((ipaddr >> 24) & 0xff)); + return retval; +} + +void replace_string_in_place(std::string& subject, const std::string& search, const std::string& replace) { + size_t pos = 0; + while ((pos = subject.find(search, pos)) != std::string::npos) { + subject.replace(pos, search.length(), replace); + pos += replace.length(); + } } diff --git a/FluidNC/src/NutsBolts.h b/FluidNC/src/NutsBolts.h index 1aea1970d..248649d65 100644 --- a/FluidNC/src/NutsBolts.h +++ b/FluidNC/src/NutsBolts.h @@ -6,11 +6,9 @@ // #define false 0 // #define true 1 -#include #include -#include -#include #include "Logging.h" +#include "Driver/delay_usecs.h" enum class DwellMode : uint8_t { Dwell = 0, // (Default: Must be zero) @@ -94,41 +92,6 @@ void swap(T& a, T& b) { b = c; } -// Short delays measured using the CPU cycle counter. There is a ROM -// routine "esp_delay_us(us)" that almost does what what we need, -// except that it is in ROM and thus dodgy for use from ISRs. We -// duplicate the esp_delay_us() here, but placed in IRAM, inlined, -// and factored so it can be used in different ways. - -inline int32_t IRAM_ATTR getCpuTicks() { - return XTHAL_GET_CCOUNT(); -} - -extern uint32_t g_ticks_per_us_pro; // For CPU 0 - typically 240 MHz -extern uint32_t g_ticks_per_us_app; // For CPU 1 - typically 240 MHz - -inline int32_t IRAM_ATTR usToCpuTicks(int32_t us) { - return us * g_ticks_per_us_pro; -} - -inline int32_t IRAM_ATTR usToEndTicks(int32_t us) { - return getCpuTicks() + usToCpuTicks(us); -} - -// At the usual ESP32 clock rate of 240MHz, the range of this is -// just under 18 seconds, but it really should be used only for -// short delays up to a few tens of microseconds. - -inline void IRAM_ATTR spinUntil(int32_t endTicks) { - while ((getCpuTicks() - endTicks) < 0) { -#ifdef ESP32 - asm volatile("nop"); -#endif - } -} - -void delay_us(int32_t us); - template T myMap(T x, T in_min, T in_max, T out_min, T out_max) { // DrawBot_Badge return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; @@ -164,4 +127,8 @@ bool constrain_with_message(T& value, T min, T max, const char* name = "") { bool multiple_bits_set(uint32_t val); -String formatBytes(uint64_t bytes); +std::string formatBytes(uint64_t bytes); + +std::string IP_string(uint32_t ipaddr); + +void replace_string_in_place(std::string& subject, const std::string& search, const std::string& replace); diff --git a/FluidNC/src/OLED.cpp b/FluidNC/src/OLED.cpp index c23aa414a..ec7c9a501 100644 --- a/FluidNC/src/OLED.cpp +++ b/FluidNC/src/OLED.cpp @@ -2,7 +2,7 @@ #include "Machine/MachineConfig.h" -void OLED::show(Layout& layout, const String& msg) { +void OLED::show(Layout& layout, const char* msg) { if (_width < layout._width_required) { return; } @@ -118,7 +118,7 @@ void OLED::show_file() { return; } if (_width == 128) { - show(percentLayout128, String(pct) + '%'); + show(percentLayout128, std::to_string(pct) + '%'); _ticker += "-"; if (_ticker.length() >= 12) { @@ -126,11 +126,11 @@ void OLED::show_file() { } show(tickerLayout, _ticker); - wrapped_draw_string(14, _filename.c_str(), ArialMT_Plain_16); + wrapped_draw_string(14, _filename, ArialMT_Plain_16); _oled->drawProgressBar(0, 45, 120, 10, pct); } else { - show(percentLayout64, String(pct) + '%'); + show(percentLayout64, std::to_string(pct) + '%'); } } void OLED::show_dro(const float* axes, bool isMpos, bool* limits) { @@ -153,16 +153,16 @@ void OLED::show_dro(const float* axes, bool isMpos, bool* limits) { for (uint8_t axis = X_AXIS; axis < n_axis; axis++) { oled_y_pos = ((_height == 64) ? 24 : 17) + (axis * 10); - String axis_letter = String(Machine::Axes::_names[axis]); + std::string axis_msg(1, Machine::Axes::_names[axis]); if (_width == 128) { - axis_letter += ":"; + axis_msg += ":"; } else { // For small displays there isn't room for separate limit boxes // so we put it after the label - axis_letter += limits[axis] ? "L" : ":"; + axis_msg += limits[axis] ? "L" : ":"; } _oled->setTextAlignment(TEXT_ALIGN_LEFT); - _oled->drawString(0, oled_y_pos, axis_letter); + _oled->drawString(0, oled_y_pos, axis_msg.c_str()); _oled->setTextAlignment(TEXT_ALIGN_RIGHT); snprintf(axisVal, 20 - 1, "%.3f", axes[axis]); @@ -386,9 +386,8 @@ void OLED::parse_gcode_report() { // [MSG:INFO: Connecting to STA:SSID foo] void OLED::parse_STA() { - size_t start = strlen("[MSG:INFO: Connecting to STA SSID:"); - std::string ssid = _report.substr(start, _report.size() - start - 1); - _radio_info = String(ssid.c_str()); + size_t start = strlen("[MSG:INFO: Connecting to STA SSID:"); + _radio_info = _report.substr(start, _report.size() - start - 1); _oled->clear(); auto fh = font_height(ArialMT_Plain_10); @@ -398,9 +397,8 @@ void OLED::parse_STA() { // [MSG:INFO: Connected - IP is 192.168.68.134] void OLED::parse_IP() { - size_t start = _report.rfind(" ") + 1; - std::string ipaddr = _report.substr(start, _report.size() - start - 1); - _radio_addr = String(ipaddr.c_str()); + size_t start = _report.rfind(" ") + 1; + _radio_addr = _report.substr(start, _report.size() - start - 1); _oled->clear(); auto fh = font_height(ArialMT_Plain_10); @@ -412,15 +410,14 @@ void OLED::parse_IP() { // [MSG:INFO: AP SSID foo IP 192.168.68.134 mask foo channel foo] void OLED::parse_AP() { - size_t start = strlen("[MSG:INFO: AP SSID "); - size_t ssid_end = _report.rfind(" IP "); - size_t ip_end = _report.rfind(" mask "); - std::string ssid = _report.substr(start, ssid_end - start); - size_t ip_start = ssid_end + strlen(" IP "); - std::string ipaddr = _report.substr(ip_start, ip_end - ip_start); - _radio_info = "AP: "; - _radio_info += ssid.c_str(); - _radio_addr = String(ipaddr.c_str()); + size_t start = strlen("[MSG:INFO: AP SSID "); + size_t ssid_end = _report.rfind(" IP "); + size_t ip_end = _report.rfind(" mask "); + size_t ip_start = ssid_end + strlen(" IP "); + + _radio_info = "AP: "; + _radio_info += _report.substr(start, ssid_end - start); + _radio_addr = _report.substr(ip_start, ip_end - ip_start); _oled->clear(); auto fh = font_height(ArialMT_Plain_10); @@ -512,7 +509,7 @@ size_t OLED::char_width(char c, font_t font) { return (index < 0) ? 0 : xf->glyphs[index].width; } -void OLED::wrapped_draw_string(int16_t y, const String& s, font_t font) { +void OLED::wrapped_draw_string(int16_t y, const std::string& s, font_t font) { _oled->setFont(font); _oled->setTextAlignment(TEXT_ALIGN_LEFT); @@ -526,10 +523,10 @@ void OLED::wrapped_draw_string(int16_t y, const String& s, font_t font) { } } if (swidth < _width) { - _oled->drawString(0, y, s); + _oled->drawString(0, y, s.c_str()); } else { - _oled->drawString(0, y, s.substring(0, i)); - _oled->drawString(0, y + font_height(font) - 1, s.substring(i, slen)); + _oled->drawString(0, y, s.substr(0, i).c_str()); + _oled->drawString(0, y + font_height(font) - 1, s.substr(i, slen).c_str()); } } diff --git a/FluidNC/src/OLED.h b/FluidNC/src/OLED.h index 7ff04c611..c9937ba89 100644 --- a/FluidNC/src/OLED.h +++ b/FluidNC/src/OLED.h @@ -32,8 +32,8 @@ class OLED : public Channel, public Configuration::Configurable { private: std::string _report; - String _radio_info; - String _radio_addr; + std::string _radio_info; + std::string _radio_addr; std::string _state; std::string _filename; @@ -63,17 +63,10 @@ class OLED : public Channel, public Configuration::Configurable { void show_radio_info(); void draw_checkbox(int16_t x, int16_t y, int16_t width, int16_t height, bool checked); - void wrapped_draw_string(int16_t y, const String& s, font_t font); + void wrapped_draw_string(int16_t y, const std::string& s, font_t font); - void show(Layout& layout, const String& msg); - void show(Layout& layout, const char* msg) { - String s(msg); - show(layout, s); - } - void show(Layout& layout, const std::string& msg) { - String s(msg.c_str()); - show(layout, s); - } + void show(Layout& layout, const std::string& msg) { show(layout, msg.c_str()); } + void show(Layout& layout, const char* msg); uint8_t font_width(font_t font); uint8_t font_height(font_t font); @@ -117,7 +110,7 @@ class OLED : public Channel, public Configuration::Configurable { size_t timedReadBytes(char* buffer, size_t length, TickType_t timeout) override { return 0; } // Configuration handlers: - void validate() const override {} + void validate() override {} void afterParse() override; diff --git a/FluidNC/src/Pin.cpp b/FluidNC/src/Pin.cpp index 074db9d07..4cd075d61 100644 --- a/FluidNC/src/Pin.cpp +++ b/FluidNC/src/Pin.cpp @@ -16,65 +16,55 @@ Pins::PinDetail* Pin::undefinedPin = new Pins::VoidPinDetail(); Pins::PinDetail* Pin::errorPin = new Pins::ErrorPinDetail("unknown"); const char* Pin::parse(StringRange tmp, Pins::PinDetail*& pinImplementation) { - String str = tmp.str(); - // Initialize pinImplementation first! Callers might want to delete it, and we don't want a random pointer. pinImplementation = nullptr; // Parse the definition: [GPIO].[pinNumber]:[attributes] + const char* end = tmp.end(); + // Skip whitespaces at the start - auto nameStart = str.begin(); - for (; nameStart != str.end() && ::isspace(*nameStart); ++nameStart) {} + auto nameStart = tmp.begin(); + for (; nameStart != end && ::isspace(*nameStart); ++nameStart) {} - if (nameStart == str.end()) { + if (nameStart == end) { // Re-use undefined pins happens in 'create': pinImplementation = new Pins::VoidPinDetail(); return nullptr; } auto idx = nameStart; - for (; idx != str.end() && *idx != '.' && *idx != ':'; ++idx) { - *idx = char(::tolower(*idx)); - } + for (; idx != end && *idx != '.' && *idx != ':'; ++idx) {} - String prefix = str.substring(0, int(idx - str.begin())); + StringRange prefix(tmp.begin(), idx); - if (idx != str.end()) { // skip '.' + if (idx != end) { // skip '.' ++idx; } int pinNumber = 0; if (prefix != "") { - if (idx != str.end()) { - for (int n = 0; idx != str.end() && n <= 4 && *idx >= '0' && *idx <= '9'; ++idx, ++n) { + if (idx != end) { + for (int n = 0; idx != end && n <= 4 && *idx >= '0' && *idx <= '9'; ++idx, ++n) { pinNumber = pinNumber * 10 + int(*idx - '0'); } } } - while (idx != str.end() && ::isspace(*idx)) { + while (idx != end && ::isspace(*idx)) { ++idx; } - String options; - if (idx != str.end()) { + if (idx != end) { if (*idx != ':') { // Pin definition attributes or EOF expected. return "Pin attributes (':') were expected."; } ++idx; - - options = str.substring(int(idx - str.begin())); } - // What would be a simple, practical way to parse the options? I figured, why not - // just use the C-style string, convert it to lower case, and change the separators - // into 'nul' tokens. We then pass the number of 'nul' tokens, and the first char* - // which is pretty easy to parse. - // Build an options parser: - Pins::PinOptionsParser parser(options.begin(), options.end()); + Pins::PinOptionsParser parser(idx, end); // Build this pin: if (prefix == "gpio") { @@ -106,10 +96,6 @@ const char* Pin::parse(StringRange tmp, Pins::PinDetail*& pinImplementation) { } } -Pin Pin::create(const String& str) { - return create(StringRange(str)); -} - Pin Pin::create(const StringRange& str) { Pins::PinDetail* pinImplementation = nullptr; try { @@ -120,7 +106,7 @@ Pin Pin::create(const StringRange& str) { } log_error("Setting up pin:" << str.str() << " failed:" << err); - return Pin(new Pins::ErrorPinDetail(str.str())); + return Pin(new Pins::ErrorPinDetail(str.str().c_str())); } else { return Pin(pinImplementation); } @@ -139,7 +125,7 @@ Pin Pin::create(const StringRange& str) { } } -bool Pin::validate(const String& str) { +bool Pin::validate(const char* str) { Pins::PinDetail* pinImplementation; auto valid = parse(str, pinImplementation); diff --git a/FluidNC/src/Pin.h b/FluidNC/src/Pin.h index f60a8cf7a..e40e38d4e 100644 --- a/FluidNC/src/Pin.h +++ b/FluidNC/src/Pin.h @@ -10,15 +10,13 @@ #include // IRAM_ATTR #include +#include #include #include #include "Assert.h" // #define DEBUG_PIN_DUMP // Pin debugging. WILL spam you with a lot of data! -// Forward declarations: -class String; - // Yuck, yuck, yuck... apparently we can't create a template with an IRAM_ATTR, because GCC refuses to obide // by the attributes. In other words, _all_ templates are out when using an ISR! This define makes an anonymous // method in the class, which can be used to wrap a single function. It can then be used by running the attachInterrupt @@ -105,8 +103,7 @@ class Pin { static Pin create(const char* str) { return create(StringRange(str)); } // ensure it's not ambiguous static Pin create(const StringRange& str); - static Pin create(const String& str); - static bool validate(const String& str); + static bool validate(const char* str); // We delete the copy constructor, and implement the move constructor. The move constructor is required to support // the correct execution of 'return' in f.ex. `create` calls. It basically transfers ownership from the callee to the @@ -158,10 +155,10 @@ class Pin { // Other functions: Capabilities capabilities() const { return _detail->capabilities(); } - inline String name() const { return _detail->toString(); } + inline std::string name() const { return _detail->toString(); } void report(const char* legend); - void report(String legend) { report(legend.c_str()); } + void report(std::string legend) { report(legend.c_str()); } inline void swap(Pin& o) { std::swap(o._detail, _detail); } diff --git a/FluidNC/src/Pins/DebugPinDetail.cpp b/FluidNC/src/Pins/DebugPinDetail.cpp index 4d40b4b0b..63e72d236 100644 --- a/FluidNC/src/Pins/DebugPinDetail.cpp +++ b/FluidNC/src/Pins/DebugPinDetail.cpp @@ -26,7 +26,7 @@ namespace Pins { if (high != int(_isHigh)) { _isHigh = bool(high); if (shouldEvent()) { - WriteSerial("Write %s < %d", toString().c_str(), high); + WriteSerial("Write %s < %d", toString(), high); } } _implementation->write(high); @@ -35,7 +35,7 @@ namespace Pins { int DebugPinDetail::read() { auto result = _implementation->read(); if (shouldEvent()) { - WriteSerial("Read %s > %d", toString().c_str(), result); + WriteSerial("Read %s > %d", toString(), result); } return result; } @@ -66,7 +66,7 @@ namespace Pins { buf[n++] = 0; if (shouldEvent()) { - WriteSerial("Set pin attr %s = %s", toString().c_str(), buf); + WriteSerial("Set pin attr %s = %s", toString(), buf); } _implementation->setAttr(value); } @@ -76,7 +76,7 @@ namespace Pins { void DebugPinDetail::CallbackHandler::handle(void* arg) { auto handler = static_cast(arg); if (handler->_myPin->shouldEvent()) { - WriteSerial("Received ISR on %s", handler->_myPin->toString().c_str()); + WriteSerial("Received ISR on %s", handler->_myPin->toString()); } handler->callback(handler->argument); } @@ -88,7 +88,7 @@ namespace Pins { _isrHandler.callback = callback; if (shouldEvent()) { - WriteSerial("Attaching interrupt to pin %s, mode %d", toString().c_str(), mode); + WriteSerial("Attaching interrupt to pin %s, mode %d", toString(), mode); } _implementation->attachInterrupt(_isrHandler.handle, &_isrHandler, mode); } diff --git a/FluidNC/src/Pins/DebugPinDetail.h b/FluidNC/src/Pins/DebugPinDetail.h index 8914af3af..b6b10ff84 100644 --- a/FluidNC/src/Pins/DebugPinDetail.h +++ b/FluidNC/src/Pins/DebugPinDetail.h @@ -42,7 +42,7 @@ namespace Pins { void attachInterrupt(void (*callback)(void*), void* arg, int mode) override; void detachInterrupt() override; - String toString() override { return _implementation->toString(); } + std::string toString() override { return _implementation->toString(); } ~DebugPinDetail() override {} }; diff --git a/FluidNC/src/Pins/ErrorPinDetail.cpp b/FluidNC/src/Pins/ErrorPinDetail.cpp index 82640f3d5..996ebe342 100644 --- a/FluidNC/src/Pins/ErrorPinDetail.cpp +++ b/FluidNC/src/Pins/ErrorPinDetail.cpp @@ -6,7 +6,7 @@ #include "../Assert.h" namespace Pins { - ErrorPinDetail::ErrorPinDetail(const String& descr) : PinDetail(0), _description(descr) {} + ErrorPinDetail::ErrorPinDetail(const char* descr) : PinDetail(0), _description(descr) {} PinCapabilities ErrorPinDetail::capabilities() const { return PinCapabilities::Error; } @@ -33,5 +33,8 @@ namespace Pins { PinAttributes ErrorPinDetail::getAttr() const { return PinAttributes::None; } - String ErrorPinDetail::toString() { return "ERROR_PIN (for " + _description + ")"; } + std::string ErrorPinDetail::toString() { + std::string s("ERROR_PIN (for "); + return s + _description + ")"; + } } diff --git a/FluidNC/src/Pins/ErrorPinDetail.h b/FluidNC/src/Pins/ErrorPinDetail.h index dbc1a6b27..68eea1cc0 100644 --- a/FluidNC/src/Pins/ErrorPinDetail.h +++ b/FluidNC/src/Pins/ErrorPinDetail.h @@ -8,10 +8,10 @@ namespace Pins { class ErrorPinDetail : public PinDetail { - String _description; + std::string _description; public: - ErrorPinDetail(const String& descr); + ErrorPinDetail(const char* descr); PinCapabilities capabilities() const override; @@ -21,7 +21,7 @@ namespace Pins { void setAttr(PinAttributes value) override; PinAttributes getAttr() const override; - String toString() override; + std::string toString() override; ~ErrorPinDetail() override {} }; diff --git a/FluidNC/src/Pins/GPIOPinDetail.cpp b/FluidNC/src/Pins/GPIOPinDetail.cpp index 69f30df6d..fa1c428d0 100644 --- a/FluidNC/src/Pins/GPIOPinDetail.cpp +++ b/FluidNC/src/Pins/GPIOPinDetail.cpp @@ -179,8 +179,9 @@ namespace Pins { ::detachInterrupt(_index); } - String GPIOPinDetail::toString() { - auto s = String("gpio.") + int(_index); + std::string GPIOPinDetail::toString() { + std::string s("gpio."); + s += std::to_string(_index); if (_attributes.has(PinAttributes::ActiveLow)) { s += ":low"; } diff --git a/FluidNC/src/Pins/GPIOPinDetail.h b/FluidNC/src/Pins/GPIOPinDetail.h index 61af9696c..c4f04ef53 100644 --- a/FluidNC/src/Pins/GPIOPinDetail.h +++ b/FluidNC/src/Pins/GPIOPinDetail.h @@ -34,7 +34,7 @@ namespace Pins { void attachInterrupt(void (*callback)(void*), void* arg, int mode) override; void detachInterrupt() override; - String toString() override; + std::string toString() override; ~GPIOPinDetail() override { _claimed[_index] = false; } }; diff --git a/FluidNC/src/Pins/I2SOPinDetail.cpp b/FluidNC/src/Pins/I2SOPinDetail.cpp index f9667f0fe..02fad8e6a 100644 --- a/FluidNC/src/Pins/I2SOPinDetail.cpp +++ b/FluidNC/src/Pins/I2SOPinDetail.cpp @@ -79,8 +79,9 @@ namespace Pins { PinAttributes I2SOPinDetail::getAttr() const { return _attributes; } - String I2SOPinDetail::toString() { - auto s = String("I2SO.") + int(_index); + std::string I2SOPinDetail::toString() { + std::string s("I2SO."); + s += std::to_string(_index); if (_attributes.has(PinAttributes::ActiveLow)) { s += ":low"; } diff --git a/FluidNC/src/Pins/I2SOPinDetail.h b/FluidNC/src/Pins/I2SOPinDetail.h index e413200cd..e47e3d4fe 100644 --- a/FluidNC/src/Pins/I2SOPinDetail.h +++ b/FluidNC/src/Pins/I2SOPinDetail.h @@ -29,7 +29,7 @@ namespace Pins { void setAttr(PinAttributes value) override; PinAttributes getAttr() const override; - String toString() override; + std::string toString() override; ~I2SOPinDetail() override { _claimed[_index] = false; } }; diff --git a/FluidNC/src/Pins/PinDetail.h b/FluidNC/src/Pins/PinDetail.h index 3100945af..c0cf4cccb 100644 --- a/FluidNC/src/Pins/PinDetail.h +++ b/FluidNC/src/Pins/PinDetail.h @@ -7,9 +7,9 @@ #include "PinAttributes.h" #include "PinOptionsParser.h" -#include #include #include +#include #include typedef uint8_t pinnum_t; @@ -41,7 +41,7 @@ namespace Pins { virtual void attachInterrupt(void (*callback)(void*), void* arg, int mode); virtual void detachInterrupt(); - virtual String toString() = 0; + virtual std::string toString() = 0; inline int number() const { return _index; } diff --git a/FluidNC/src/Pins/PinOptionsParser.cpp b/FluidNC/src/Pins/PinOptionsParser.cpp index a7d21c2cb..e94e82933 100644 --- a/FluidNC/src/Pins/PinOptionsParser.cpp +++ b/FluidNC/src/Pins/PinOptionsParser.cpp @@ -8,63 +8,89 @@ #include namespace Pins { - PinOption::PinOption(char* start, const char* end) : _start(start), _end(end), _key(start), _value(start) { tokenize(); } + PinOption::PinOption(const char* start, const char* end) : _start(start), _end(end), _key(start), _value(start) { tokenize(); } + + // Copy the value into a null-terminated string, converting to lower case + const char* PinOption::value() const { + static char str[100]; + + int valuelen = _valueend - _value; + if (valuelen > 100) { + valuelen = 99; + } + const char* p = _value; + int i; + for (i = 0; i < valuelen; i++) { + str[i] = ::tolower(*p++); + } + str[i] = '\0'; + return str; + } void PinOption::tokenize() { if (_start != _end) { _key = _start; auto i = _start; - for (; i != _end && (*i) != ':' && (*i) != ';' && (*i) != '='; ++i) { - *i = ::tolower(*i); - } + for (; i != _end && (*i) != ':' && (*i) != ';' && (*i) != '='; ++i) {} if (i == _end) { // [start, end> is a key; value is nul - _value = _end; - _start = i; + _value = _end; + _valueend = _end; + _keyend = _end; + _start = i; } else if (*i == '=') { // Parsing a key-value pair. // // Mark end of the key, which is now in [start, end> - *i = '\0'; + _keyend = i; ++i; _value = i; // Parse the value: - for (; i != _end && (*i) != ':' && (*i) != ';'; ++i) { - *i = ::tolower(*i); - } + for (; i != _end && (*i) != ':' && (*i) != ';'; ++i) {} + _valueend = i; if (i != _end) { - *i = '\0'; _start = i + 1; } else { _start = i; } } else { // must be ':' or ';' - // [start, i> is a key; value is nul - _value = i; - *i = '\0'; - _start = i + 1; + // [start, i> is a key; value is nul + _keyend = _value = _valueend = i; + _start = i + 1; } } else { // Both key and value are nul. _key = _value = _end; + _keyend = _valueend = _end; } } - bool PinOption::is(const char* option) const { return !::strcmp(option, _key); } + bool PinOption::is(const char* option) const { + const char* k = _key; + while (*option && k != _keyend) { + if (::tolower(*k++) != ::tolower(*option++)) { + return false; + } + } + // If we get here, we have reached the end of either option or key + // and the initial substrings match ignoring case. + // If we are at the end of both, we have a match + return !*option && k == _keyend; + } int PinOption::iValue() const { // Parse to integer - return ::atoi(_value); + return ::atoi(value()); } double PinOption::dValue() const { // Parse to integer - return ::atof(_value); + return ::atof(value()); } PinOption& PinOption ::operator++() { @@ -72,7 +98,7 @@ namespace Pins { return *this; } - PinOptionsParser::PinOptionsParser(char* buffer, char* endBuffer) : _buffer(buffer), _bufferEnd(endBuffer) { + PinOptionsParser::PinOptionsParser(const char* buffer, const char* endBuffer) : _buffer(buffer), _bufferEnd(endBuffer) { // trim whitespaces: while (buffer != endBuffer && ::isspace(*buffer)) { ++buffer; @@ -81,7 +107,6 @@ namespace Pins { while (buffer - 1 != endBuffer && ::isspace(endBuffer[-1])) { --endBuffer; } - *endBuffer = '\0'; } _buffer = buffer; _bufferEnd = endBuffer; diff --git a/FluidNC/src/Pins/PinOptionsParser.h b/FluidNC/src/Pins/PinOptionsParser.h index a44fe20c3..73c430477 100644 --- a/FluidNC/src/Pins/PinOptionsParser.h +++ b/FluidNC/src/Pins/PinOptionsParser.h @@ -25,13 +25,15 @@ namespace Pins { class PinOption { friend class PinOptionsParser; - char* _start; + const char* _start; const char* _end; const char* _key; + const char* _keyend; const char* _value; + const char* _valueend; - PinOption(char* start, const char* end); + PinOption(const char* start, const char* end); void tokenize(); @@ -39,28 +41,27 @@ namespace Pins { inline const char* operator()() const { return _key; } bool is(const char* option) const; - int iValue() const; - double dValue() const; - inline const char* value() const { return _value; } + int iValue() const; + double dValue() const; + + const char* value() const; // Iterator support: inline PinOption const* operator->() const { return this; } inline PinOption operator*() const { return *this; } PinOption& operator++(); - bool operator==(const PinOption& o) const { return _key == o._key; } - bool operator!=(const PinOption& o) const { return _key != o._key; } + bool operator==(const PinOption& o) const { return _key == o._key && _keyend == o._keyend; } + bool operator!=(const PinOption& o) const { return _key != o._key || _keyend != o._keyend; } }; - // Options parser. This basically parses the options passed to the Pin class. Destroys - // the original options, and passes the options as lower case items to the enumerator. - // For the lazy people that want safe, reliable, easy parsing :-) + // This parses the options passed to the Pin class. class PinOptionsParser { - char* _buffer; - char* _bufferEnd; + const char* _buffer; + const char* _bufferEnd; public: - PinOptionsParser(char* buffer, char* endBuffer); + PinOptionsParser(const char* buffer, const char* endBuffer); inline PinOption begin() const { return PinOption(_buffer, _bufferEnd); } inline PinOption end() const { return PinOption(_bufferEnd, _bufferEnd); } diff --git a/FluidNC/src/Pins/VoidPinDetail.cpp b/FluidNC/src/Pins/VoidPinDetail.cpp index d92a8dbba..fa2df69bb 100644 --- a/FluidNC/src/Pins/VoidPinDetail.cpp +++ b/FluidNC/src/Pins/VoidPinDetail.cpp @@ -18,6 +18,6 @@ namespace Pins { void VoidPinDetail::setAttr(PinAttributes value) {} PinAttributes VoidPinDetail::getAttr() const { return PinAttributes::None; } - String VoidPinDetail::toString() { return "NO_PIN"; } + std::string VoidPinDetail::toString() { return std::string("NO_PIN"); } } diff --git a/FluidNC/src/Pins/VoidPinDetail.h b/FluidNC/src/Pins/VoidPinDetail.h index 988caceba..b4710bd48 100644 --- a/FluidNC/src/Pins/VoidPinDetail.h +++ b/FluidNC/src/Pins/VoidPinDetail.h @@ -20,7 +20,7 @@ namespace Pins { void setAttr(PinAttributes value) override; PinAttributes getAttr() const override; - String toString() override; + std::string toString() override; ~VoidPinDetail() override {} }; diff --git a/FluidNC/src/Probe.cpp b/FluidNC/src/Probe.cpp index 52435a697..0017cb70c 100644 --- a/FluidNC/src/Probe.cpp +++ b/FluidNC/src/Probe.cpp @@ -36,7 +36,7 @@ bool IRAM_ATTR Probe::tripped() { return _probePin.read() ^ _isProbeAway; } -void Probe::validate() const {} +void Probe::validate() {} void Probe::group(Configuration::HandlerBase& handler) { handler.item("pin", _probePin); diff --git a/FluidNC/src/Probe.h b/FluidNC/src/Probe.h index 7ead4ecad..14c5176c2 100644 --- a/FluidNC/src/Probe.h +++ b/FluidNC/src/Probe.h @@ -46,7 +46,7 @@ class Probe : public Configuration::Configurable { bool IRAM_ATTR tripped(); // Configuration handlers. - void validate() const override; + void validate() override; void group(Configuration::HandlerBase& handler) override; ~Probe() = default; diff --git a/FluidNC/src/ProcessSettings.cpp b/FluidNC/src/ProcessSettings.cpp index 884045f10..892293d18 100644 --- a/FluidNC/src/ProcessSettings.cpp +++ b/FluidNC/src/ProcessSettings.cpp @@ -580,7 +580,7 @@ static Error motor_control(const char* value, bool disable) { char axisName = axes->axisName(i); if (strchr(value, axisName) || strchr(value, tolower(axisName))) { - log_info((disable ? "Dis" : "En") << "abling " << String(axisName) << " motors"); + log_info((disable ? "Dis" : "En") << "abling " << axisName << " motors"); axes->set_disable(i, disable); } } @@ -927,16 +927,16 @@ Error do_command_or_setting(const char* key, char* value, WebUI::AuthenticationL // text form of the name, not to the nnn and ESPnnn forms. Error retval = Error::InvalidStatement; if (!value) { - auto lcKey = String(key); - lcKey.toLowerCase(); bool found = false; for (Setting* s = Setting::List; s; s = s->next()) { - auto lcTest = String(s->getName()); - lcTest.toLowerCase(); - - if (regexMatch(lcKey.c_str(), lcTest.c_str())) { + auto test = s->getName(); + // The C++ standard regular expression library supports many more + // regular expression forms than the simple one in Regex.cpp, but + // consumes a lot of FLASH. The extra capability is rarely useful + // especially now that there are only a few NVS settings. + if (regexMatch(key, test, false)) { const char* displayValue = auth_failed(s, value, auth_level) ? "" : s->getStringValue(); - show_setting(s->getName(), displayValue, NULL, out); + show_setting(test, displayValue, NULL, out); found = true; } } @@ -985,7 +985,7 @@ Error settings_execute_line(char* line, Channel& out, WebUI::AuthenticationLevel void settings_execute_startup() { Error status_code; for (int i = 0; i < config->_macros->n_startup_lines; i++) { - String str = config->_macros->startup_line(i); + auto str = config->_macros->startup_line(i); if (str.length()) { // We have to copy this to a mutable array because // gc_execute_line modifies the line while parsing. diff --git a/FluidNC/src/Protocol.cpp b/FluidNC/src/Protocol.cpp index c1307452f..3937c9c6d 100644 --- a/FluidNC/src/Protocol.cpp +++ b/FluidNC/src/Protocol.cpp @@ -232,13 +232,13 @@ static void check_startup_state() { // NOTE: Sleep mode disables the stepper drivers and position can't be guaranteed. // Re-initialize the sleep state as an ALARM mode to ensure user homes or acknowledges. if (sys.state == State::ConfigAlarm) { - report_feedback_message(Message::ConfigAlarmLock); + report_error_message(Message::ConfigAlarmLock); } else { // Perform some machine checks to make sure everything is good to go. if (config->_start->_checkLimits && config->_axes->hasHardLimits()) { if (limits_get_state()) { sys.state = State::Alarm; // Ensure alarm state is active. - report_feedback_message(Message::CheckLimits); + report_error_message(Message::CheckLimits); } } if (config->_control->startup_check()) { @@ -275,7 +275,6 @@ void protocol_main_loop() { #ifdef DEBUG_REPORT_ECHO_RAW_LINE_RECEIVED report_echo_line_received(activeLine, allChannels); #endif - display("GCODE", activeLine); Error status_code = execute_line(activeLine, *activeChannel, WebUI::AuthenticationLevel::LEVEL_GUEST); @@ -376,7 +375,7 @@ static void protocol_do_alarm() { sys.state = State::Alarm; // Set system alarm state alarm_msg(rtAlarm); if (rtAlarm == ExecAlarm::HardLimit || rtAlarm == ExecAlarm::SoftLimit) { - report_feedback_message(Message::CriticalEvent); + report_error_message(Message::CriticalEvent); protocol_disable_steppers(); rtReset = false; // Disable any existing reset do { diff --git a/FluidNC/src/Regex.cpp b/FluidNC/src/Regex.cpp index 7f942ccc2..1d625b85c 100644 --- a/FluidNC/src/Regex.cpp +++ b/FluidNC/src/Regex.cpp @@ -16,12 +16,14 @@ // "bare * wildcard" is similar to filename wildcarding in many shells // and CLIs. -static bool matchHere(const char* regexp, const char* text); +#include + +static bool matchHere(const char* regexp, const char* text, bool case_sensitive); // matchStar - search for *regexp at beginning of text -static bool matchStar(const char* regexp, const char* text) { +static bool matchStar(const char* regexp, const char* text, bool case_sensitive) { do { - if (matchHere(regexp, text)) { + if (matchHere(regexp, text, case_sensitive)) { return true; } } while (*text++ != '\0'); @@ -29,30 +31,30 @@ static bool matchStar(const char* regexp, const char* text) { } // matchHere - search for regex at beginning of text -static bool matchHere(const char* regexp, const char* text) { +static bool matchHere(const char* regexp, const char* text, bool case_sensitive) { if (regexp[0] == '\0') { return true; } if (regexp[0] == '*') { - return matchStar(regexp + 1, text); + return matchStar(regexp + 1, text, case_sensitive); } if (regexp[0] == '$' && regexp[1] == '\0') { return *text == '\0'; } - if (*text != '\0' && (regexp[0] == *text)) { - return matchHere(++regexp, ++text); + if (*text != '\0' && (case_sensitive ? regexp[0] == *text : tolower(regexp[0]) == tolower(*text))) { + return matchHere(++regexp, ++text, case_sensitive); } return false; } // match - search for regular expression anywhere in text // Returns true if text contains the regular expression regexp -bool regexMatch(const char* regexp, const char* text) { +bool regexMatch(const char* regexp, const char* text, bool case_sensitive) { if (regexp[0] == '^') { - return matchHere(++regexp, text); + return matchHere(++regexp, text, case_sensitive); } do { - if (matchHere(regexp, text)) { + if (matchHere(regexp, text, case_sensitive)) { return true; } } while (*text++ != '\0'); diff --git a/FluidNC/src/Regex.h b/FluidNC/src/Regex.h index 9c0c5d131..e98d26a7e 100644 --- a/FluidNC/src/Regex.h +++ b/FluidNC/src/Regex.h @@ -2,4 +2,4 @@ // See Regex.cpp for attribution, description and discussion // Returns true if text contains the regular expression regexp -bool regexMatch(const char* regexp, const char* text); +bool regexMatch(const char* regexp, const char* text, bool case_sensitive = true); diff --git a/FluidNC/src/Report.cpp b/FluidNC/src/Report.cpp index c00a038fa..dc43c3dec 100644 --- a/FluidNC/src/Report.cpp +++ b/FluidNC/src/Report.cpp @@ -137,6 +137,12 @@ void report_feedback_message(Message message) { // ok to send to all channels log_info(it->second); } } +void report_error_message(Message message) { // ok to send to all channels + auto it = MessageText.find(message); + if (it != MessageText.end()) { + log_error(it->second); + } +} const char* radio = #if defined(ENABLE_WIFI) || defined(ENABLE_BLUETOOTH) @@ -223,9 +229,9 @@ void report_ngc_coord(CoordIndex coord, Channel& channel) { return; } // Persistent offsets G54 - G59, G28, and G30 - String name = coords[coord]->getName(); + std::string name(coords[coord]->getName()); name += ":"; - log_to(channel, "[", name.c_str() << report_util_axis_values(coords[coord]->get())); + log_to(channel, "[", name << report_util_axis_values(coords[coord]->get())); } void report_ngc_parameters(Channel& channel) { for (auto coord = CoordIndex::Begin; coord < CoordIndex::End; ++coord) { @@ -667,4 +673,3 @@ void reportTaskStackSize(UBaseType_t& saved) { } void WEAK_LINK display_init() {} -void WEAK_LINK display(const char* tag, String s) {} diff --git a/FluidNC/src/Report.h b/FluidNC/src/Report.h index d00fc69d4..1c1885a80 100644 --- a/FluidNC/src/Report.h +++ b/FluidNC/src/Report.h @@ -51,6 +51,7 @@ void _notifyf(const char* title, const char* format, ...); // Prints miscellaneous feedback messages. void report_feedback_message(Message message); +void report_error_message(Message message); // Prints welcome message void report_init_message(Channel& channel); @@ -93,6 +94,5 @@ extern const char* git_info; // Callout to custom code void display_init(); -void display(const char* tag, String s); extern bool readyNext; diff --git a/FluidNC/src/SDCard.h b/FluidNC/src/SDCard.h index f016ff931..e6f5b148d 100644 --- a/FluidNC/src/SDCard.h +++ b/FluidNC/src/SDCard.h @@ -42,7 +42,7 @@ class SDCard : public Configuration::Configurable { Pin _cardDetect; Pin _cs; - uint32_t _frequency_hz = 0; // Set to nonzero to override the default + uint32_t _frequency_hz = 8000000; // Set to nonzero to override the default public: SDCard(); @@ -60,7 +60,7 @@ class SDCard : public Configuration::Configurable { void group(Configuration::HandlerBase& handler) override { handler.item("cs_pin", _cs); handler.item("card_detect_pin", _cardDetect); - handler.item("frequency_hz", _frequency_hz); + handler.item("frequency_hz", _frequency_hz, 400000, 20000000); } ~SDCard(); diff --git a/FluidNC/src/Settings.cpp b/FluidNC/src/Settings.cpp index ad1ab0758..ded02e2a3 100644 --- a/FluidNC/src/Settings.cpp +++ b/FluidNC/src/Settings.cpp @@ -220,7 +220,7 @@ void StringSetting::load() { _currentValue = _defaultValue; return; } - _storedValue = String(buf); + _storedValue = buf; _currentValue = _storedValue; } @@ -376,7 +376,7 @@ const char* EnumSetting::getStringValue() { } void EnumSetting::showList() { - String optList = ""; + std::string optList = ""; for (enum_opt_t::iterator it = _options->begin(); it != _options->end(); it++) { optList = optList + " " + it->first; } @@ -387,7 +387,7 @@ void EnumSetting::addWebui(WebUI::JSONencoder* j) { if (!getDescription()) { return; } - j->begin_webui(getName(), getName(), "B", String(get()).c_str()); + j->begin_webui(getName(), getName(), "B", get()); j->begin_array("O"); for (enum_opt_t::iterator it = _options->begin(); it != _options->end(); it++) { j->begin_object(); @@ -505,14 +505,14 @@ Error IPaddrSetting::setStringValue(char* s) { } const char* IPaddrSetting::getDefaultString() { - static String s; - s = IPAddress(_defaultValue).toString(); - return s.c_str(); + static char ipstr[50]; + strncpy(ipstr, IP_string(IPAddress(_defaultValue)).c_str(), 50); + return ipstr; } const char* IPaddrSetting::getStringValue() { - static String s; - s = IPAddress(get()).toString(); - return s.c_str(); + static char ipstr[50]; + strncpy(ipstr, IP_string(IPAddress(get())).c_str(), 50); + return ipstr; } void IPaddrSetting::addWebui(WebUI::JSONencoder* j) { diff --git a/FluidNC/src/Settings.h b/FluidNC/src/Settings.h index fdaa71b48..19577f3cb 100644 --- a/FluidNC/src/Settings.h +++ b/FluidNC/src/Settings.h @@ -154,8 +154,7 @@ class Setting : public Word { virtual void addWebui(WebUI::JSONencoder*) {}; virtual Error setStringValue(char* value) = 0; - Error setStringValue(String s) { return setStringValue(s.c_str()); } - virtual const char* getStringValue() = 0; + virtual const char* getStringValue() = 0; virtual const char* getCompatibleValue() { return getStringValue(); } virtual const char* getDefaultString() = 0; }; @@ -229,12 +228,12 @@ extern Coordinates* coords[CoordIndex::End]; class StringSetting : public Setting { private: - String _defaultValue; - String _currentValue; - String _storedValue; - int _minLength; - int _maxLength; - void _setStoredValue(const char* s); + std::string _defaultValue; + std::string _currentValue; + std::string _storedValue; + int _minLength; + int _maxLength; + void _setStoredValue(const char* s); public: StringSetting(const char* description, diff --git a/FluidNC/src/Spindles/10vSpindle.h b/FluidNC/src/Spindles/10vSpindle.h index 607636b21..c9143fdb0 100644 --- a/FluidNC/src/Spindles/10vSpindle.h +++ b/FluidNC/src/Spindles/10vSpindle.h @@ -33,7 +33,7 @@ namespace Spindles { void deinit() override; // Configuration handlers: - void validate() const override { PWM::validate(); } + void validate() override { PWM::validate(); } void group(Configuration::HandlerBase& handler) override { handler.item("forward_pin", _forward_pin); diff --git a/FluidNC/src/Spindles/BESCSpindle.h b/FluidNC/src/Spindles/BESCSpindle.h index 912635738..98b4cb579 100644 --- a/FluidNC/src/Spindles/BESCSpindle.h +++ b/FluidNC/src/Spindles/BESCSpindle.h @@ -56,7 +56,7 @@ namespace Spindles { void set_output(uint32_t duty) override; // Configuration handlers: - void validate() const override { PWM::validate(); } + void validate() override { PWM::validate(); } void group(Configuration::HandlerBase& handler) override { PWM::group(handler); diff --git a/FluidNC/src/Spindles/DacSpindle.cpp b/FluidNC/src/Spindles/DacSpindle.cpp index 1c5979e3b..dedf15131 100644 --- a/FluidNC/src/Spindles/DacSpindle.cpp +++ b/FluidNC/src/Spindles/DacSpindle.cpp @@ -9,9 +9,12 @@ a 0-5V or 0-10V value to control the spindle. You would use an Op Amp type circuit to get from the 0.3.3V of the ESP32 to that voltage. */ -#include "DacSpindle.h" +#include +#ifdef CONFIG_IDF_TARGET_ESP32 -#include // dacWrite +# include "DacSpindle.h" + +# include // dacWrite namespace Spindles { // ======================================== Dac ====================================== @@ -58,3 +61,4 @@ namespace Spindles { SpindleFactory::InstanceBuilder registration("DAC"); } } +#endif diff --git a/FluidNC/src/Spindles/HBridgeSpindle.h b/FluidNC/src/Spindles/HBridgeSpindle.h index 0784b81fd..88caa25bb 100644 --- a/FluidNC/src/Spindles/HBridgeSpindle.h +++ b/FluidNC/src/Spindles/HBridgeSpindle.h @@ -47,7 +47,7 @@ namespace Spindles { void setState(SpindleState state, SpindleSpeed speed) override; void config_message() override; // Configuration handlers: - void validate() const override { Spindle::validate(); } + void validate() override { Spindle::validate(); } void group(Configuration::HandlerBase& handler) override { // The APB clock frequency is 80MHz and the maximum divisor diff --git a/FluidNC/src/Spindles/OnOffSpindle.h b/FluidNC/src/Spindles/OnOffSpindle.h index 7d4dcee81..85389b7e9 100644 --- a/FluidNC/src/Spindles/OnOffSpindle.h +++ b/FluidNC/src/Spindles/OnOffSpindle.h @@ -46,7 +46,7 @@ namespace Spindles { virtual void set_enable(bool enable); // Configuration handlers: - void validate() const override { Spindle::validate(); } + void validate() override { Spindle::validate(); } void group(Configuration::HandlerBase& handler) override { handler.item("direction_pin", _direction_pin); diff --git a/FluidNC/src/Spindles/PWMSpindle.h b/FluidNC/src/Spindles/PWMSpindle.h index 7e0f3385c..9e3273847 100644 --- a/FluidNC/src/Spindles/PWMSpindle.h +++ b/FluidNC/src/Spindles/PWMSpindle.h @@ -33,7 +33,7 @@ namespace Spindles { void setState(SpindleState state, SpindleSpeed speed) override; void config_message() override; // Configuration handlers: - void validate() const override { Spindle::validate(); } + void validate() override { Spindle::validate(); } void group(Configuration::HandlerBase& handler) override { // The APB clock frequency is 80MHz and the maximum divisor diff --git a/FluidNC/src/Spindles/Spindle.h b/FluidNC/src/Spindles/Spindle.h index c9d88acfa..ccf5d3255 100644 --- a/FluidNC/src/Spindles/Spindle.h +++ b/FluidNC/src/Spindles/Spindle.h @@ -73,7 +73,7 @@ namespace Spindles { virtual const char* name() const = 0; // Configuration handlers: - void validate() const override { + void validate() override { // TODO: Validate spinup/spindown delay? } diff --git a/FluidNC/src/Spindles/VFDSpindle.h b/FluidNC/src/Spindles/VFDSpindle.h index 68a85884b..d7f0aa816 100644 --- a/FluidNC/src/Spindles/VFDSpindle.h +++ b/FluidNC/src/Spindles/VFDSpindle.h @@ -94,7 +94,7 @@ namespace Spindles { SpindleSpeed _slop; // Configuration handlers: - void validate() const override { + void validate() override { Spindle::validate(); Assert(_uart != nullptr || _uart_num != -1, "VFD: missing UART configuration"); Assert(!(_uart != nullptr && _uart_num != -1), "VFD: conflicting UART configuration"); diff --git a/FluidNC/src/StackTrace/AssertionFailed.cpp b/FluidNC/src/StackTrace/AssertionFailed.cpp index 0a29eac30..2a093aaed 100644 --- a/FluidNC/src/StackTrace/AssertionFailed.cpp +++ b/FluidNC/src/StackTrace/AssertionFailed.cpp @@ -11,11 +11,10 @@ # ifdef BACKTRACE_ON_ASSERT # include "esp_debug_helpers.h" # endif -# include "WString.h" # include "stdio.h" AssertionFailed AssertionFailed::create(const char* condition, const char* msg, ...) { - String st = condition; + std::string st = condition; st += ": "; char tmp[255]; @@ -40,11 +39,10 @@ AssertionFailed AssertionFailed::create(const char* condition, const char* msg, # include # include # include -# include "WString.h" extern void DumpStackTrace(std::ostringstream& builder); -String stackTrace; +std::string stackTrace; std::exception AssertionFailed::create(const char* condition, const char* msg, ...) { std::ostringstream oss; diff --git a/FluidNC/src/StackTrace/AssertionFailed.h b/FluidNC/src/StackTrace/AssertionFailed.h index e438ded22..a3cc5bc4a 100644 --- a/FluidNC/src/StackTrace/AssertionFailed.h +++ b/FluidNC/src/StackTrace/AssertionFailed.h @@ -3,15 +3,15 @@ #pragma once -#include "WString.h" +#include #ifdef ESP32 class AssertionFailed { public: - String stackTrace; - String msg; + std::string stackTrace; + std::string msg; - AssertionFailed(String st, String message) : stackTrace(st), msg(message) {} + AssertionFailed(std::string st, std::string message) : stackTrace(st), msg(message) {} static AssertionFailed create(const char* condition) { return create(condition, "Assertion failed"); } static AssertionFailed create(const char* condition, const char* msg, ...); @@ -24,8 +24,8 @@ class AssertionFailed { class AssertionFailed { public: - String stackTrace; - String msg; + std::string stackTrace; + std::string msg; static std::exception create(const char* condition) { return create(condition, "Assertion failed"); } static std::exception create(const char* condition, const char* msg, ...); diff --git a/FluidNC/src/StringRange.h b/FluidNC/src/StringRange.h index f18e3c94b..9e81c74a0 100644 --- a/FluidNC/src/StringRange.h +++ b/FluidNC/src/StringRange.h @@ -5,8 +5,6 @@ #include -#include "WString.h" - class StringRange { const char* start_; const char* end_; @@ -21,10 +19,26 @@ class StringRange { StringRange(const StringRange& o) = default; StringRange(StringRange&& o) = default; - StringRange(const String& str) : StringRange(str.begin(), str.end()) {} - StringRange& operator=(const StringRange& o) = default; StringRange& operator=(StringRange&& o) = default; + inline bool operator==(const char* s) { + const char* p = start_; + while (p != end_ && *s) { + if (::tolower(*p++) != ::tolower(*s++)) { + return false; + } + } + return !*s && p == end_; + } + inline bool operator!=(const char* s) { + const char* p = start_; + while (p != end_ && *s) { + if (::tolower(*p++) != ::tolower(*s++)) { + return true; + } + } + return *s || p != end_; + } int find(char c) const { const char* s = start_; @@ -32,7 +46,7 @@ class StringRange { return (*s) ? (s - start_) : -1; } - StringRange subString(int index, int length) const { + StringRange substr(int index, int length) const { const char* s = start_ + index; if (s > end_) { s = end_; @@ -89,16 +103,16 @@ class StringRange { const char* begin() const { return start_; } const char* end() const { return end_; } - String str() const { + std::string str() const { // TODO: Check if we can eliminate this function. I'm pretty sure we can. auto len = length(); if (len == 0) { - return String(); + return std::string(); } else { char* buf = new char[len + 1]; memcpy(buf, begin(), len); buf[len] = 0; - String tmp(buf); + std::string tmp(buf); delete[] buf; return tmp; } diff --git a/FluidNC/src/Uart.cpp b/FluidNC/src/Uart.cpp index 21cc42cd8..5e31104b3 100644 --- a/FluidNC/src/Uart.cpp +++ b/FluidNC/src/Uart.cpp @@ -49,7 +49,7 @@ void Uart::begin() { } begin(_baud, _dataBits, _stopBits, _parity); - config_message("UART", String(_uart_num, 10).c_str()); + config_message("UART", std::to_string(_uart_num).c_str()); } int Uart::read() { diff --git a/FluidNC/src/Uart.h b/FluidNC/src/Uart.h index a494484f0..5bae71528 100644 --- a/FluidNC/src/Uart.h +++ b/FluidNC/src/Uart.h @@ -74,7 +74,7 @@ class Uart : public Stream, public Configuration::Configurable { bool setHalfDuplex(); // Configuration handlers: - void validate() const override { + void validate() override { Assert(!_txd_pin.undefined(), "UART: TXD is undefined"); Assert(!_rxd_pin.undefined(), "UART: RXD is undefined"); // RTS and CTS are optional. diff --git a/FluidNC/src/WebUI/Authentication.cpp b/FluidNC/src/WebUI/Authentication.cpp index b218078fd..4d2086a9f 100644 --- a/FluidNC/src/WebUI/Authentication.cpp +++ b/FluidNC/src/WebUI/Authentication.cpp @@ -12,9 +12,9 @@ namespace WebUI { StringSetting* admin_password; void remove_password(char* str, AuthenticationLevel& auth_level) { - String paramStr = String((const char*)str); - int pos = paramStr.indexOf("pwd="); - if (pos == -1) { + std::string paramStr(str); + size_t pos = paramStr.find("pwd="); + if (pos == std::string::npos) { return; } diff --git a/FluidNC/src/WebUI/BTConfig.cpp b/FluidNC/src/WebUI/BTConfig.cpp index f312e8b08..25a7a6737 100644 --- a/FluidNC/src/WebUI/BTConfig.cpp +++ b/FluidNC/src/WebUI/BTConfig.cpp @@ -166,7 +166,7 @@ namespace WebUI { _btname = bt_name->getStringValue(); if (_btname.length()) { - if (!SerialBT.begin(_btname)) { + if (!SerialBT.begin(_btname.c_str())) { log_error("Bluetooth failed to start"); return false; } diff --git a/FluidNC/src/WebUI/BTConfig.h b/FluidNC/src/WebUI/BTConfig.h index 6a8e59269..3e030472e 100644 --- a/FluidNC/src/WebUI/BTConfig.h +++ b/FluidNC/src/WebUI/BTConfig.h @@ -23,7 +23,6 @@ namespace WebUI { # include "../Settings.h" // ENABLE_* # include "../lineedit.h" -# include # include const char* const DEFAULT_BT_NAME = "FluidNC"; @@ -62,9 +61,9 @@ namespace WebUI { private: static BTConfig* instance; // BT Callback does not support passing parameters. Sigh. - String _btclient = ""; - String _btname; - char _deviceAddrBuffer[18]; + std::string _btclient = ""; + std::string _btname; + char _deviceAddrBuffer[18]; static void my_spp_cb(esp_spp_cb_event_t event, esp_spp_cb_param_t* param); @@ -77,15 +76,15 @@ namespace WebUI { std::string info(); - static bool isBTnameValid(const char* hostname); - const String& BTname() const { return _btname; } - const String& client_name() const { return _btclient; } - const char* device_address(); - bool begin(); - void end(); - void handle(); - void reset_settings(); - bool isOn() const; + static bool isBTnameValid(const char* hostname); + const std::string& BTname() const { return _btname; } + const std::string& client_name() const { return _btclient; } + const char* device_address(); + bool begin(); + void end(); + void handle(); + void reset_settings(); + bool isOn() const; ~BTConfig(); }; diff --git a/FluidNC/src/WebUI/JSONEncoder.cpp b/FluidNC/src/WebUI/JSONEncoder.cpp index 7a72e4775..c2998c5fa 100644 --- a/FluidNC/src/WebUI/JSONEncoder.cpp +++ b/FluidNC/src/WebUI/JSONEncoder.cpp @@ -180,14 +180,8 @@ namespace WebUI { quoted(value.c_str()); } - // Creates a "tag":"value" member from an Arduino string - void JSONencoder::member(const char* tag, String value) { - begin_member(tag); - quoted(value.c_str()); - } - // Creates a "tag":"value" member from an integer - void JSONencoder::member(const char* tag, int value) { member(tag, String(value)); } + void JSONencoder::member(const char* tag, int value) { member(tag, std::to_string(value)); } // Creates an Esp32_WebUI configuration item specification from // a value passed in as a C-style string. @@ -208,7 +202,7 @@ namespace WebUI { // Creates an Esp32_WebUI configuration item specification from // an integer value. void JSONencoder::begin_webui(const char* brief, const char* full, const char* type, int val) { - begin_webui(brief, full, type, String(val).c_str()); + begin_webui(brief, full, type, std::to_string(val).c_str()); } // Creates an Esp32_WebUI configuration item specification from diff --git a/FluidNC/src/WebUI/JSONEncoder.h b/FluidNC/src/WebUI/JSONEncoder.h index 61fafb6f1..8d80a8330 100644 --- a/FluidNC/src/WebUI/JSONEncoder.h +++ b/FluidNC/src/WebUI/JSONEncoder.h @@ -42,7 +42,6 @@ namespace WebUI { // member() creates a "tag":"value" element void member(const char* tag, const char* value); - void member(const char* tag, String value); void member(const char* tag, const std::string& value); void member(const char* tag, int value); @@ -84,6 +83,9 @@ namespace WebUI { // S => 0 .. 255 // A => 7 .. 15 (0.0.0.0 .. 255.255.255.255) // I => 0 .. 2^31-1 + void begin_webui(const char* brief, const char* full, const char* type, const std::string val) { + begin_webui(brief, full, type, val.c_str()); + } void begin_webui(const char* brief, const char* full, const char* type, const char* val); void begin_webui(const char* brief, const char* full, const char* type, const int val); void begin_webui(const char* brief, const char* full, const char* type, const char* val, int min, int max); diff --git a/FluidNC/src/WebUI/NotificationsService.cpp b/FluidNC/src/WebUI/NotificationsService.cpp index b2f85c3ec..c1a5a1e6c 100644 --- a/FluidNC/src/WebUI/NotificationsService.cpp +++ b/FluidNC/src/WebUI/NotificationsService.cpp @@ -134,11 +134,11 @@ namespace WebUI { bool Wait4Answer(WiFiClientSecure& client, const char* linetrigger, const char* expected_answer, uint32_t timeout) { if (client.connected()) { - String answer; - uint32_t start_time = millis(); + std::string answer; + uint32_t start_time = millis(); while (client.connected() && ((millis() - start_time) < timeout)) { - answer = client.readStringUntil('\n'); - if ((answer.indexOf(linetrigger) != -1) || (strlen(linetrigger) == 0)) { + answer = std::string(client.readStringUntil('\n').c_str()); + if ((answer.find(linetrigger) != std::string::npos) || (strlen(linetrigger) == 0)) { break; } delay_ms(10); @@ -146,11 +146,7 @@ namespace WebUI { if (strlen(expected_answer) == 0) { return true; } - if (answer.indexOf(expected_answer) == -1) { - return false; - } else { - return true; - } + return answer.find(expected_answer) != std::string::npos; } return false; } @@ -316,36 +312,38 @@ namespace WebUI { } //Email#serveraddress:port bool NotificationsService::getPortFromSettings() { - String tmp = notification_ts->get(); - int pos = tmp.lastIndexOf(':'); - if (pos == -1) { + std::string tmp(notification_ts->get()); + size_t pos = tmp.rfind(':'); + if (pos == std::string::npos) { return false; } - _port = tmp.substring(pos + 1).toInt(); - return _port > 0; + try { + _port = stoi(tmp.substr(pos + 1)); + } catch (...) { return false; } + return true; } //Email#serveraddress:port bool NotificationsService::getServerAddressFromSettings() { - String tmp = notification_ts->get(); - int pos1 = tmp.indexOf('#'); - int pos2 = tmp.lastIndexOf(':'); - if ((pos1 == -1) || (pos2 == -1)) { + std::string tmp(notification_ts->get()); + int pos1 = tmp.find('#'); + int pos2 = tmp.rfind(':'); + if ((pos1 == std::string::npos) || (pos2 == std::string::npos)) { return false; } //TODO add a check for valid email ? - _serveraddress = tmp.substring(pos1 + 1, pos2).c_str(); + _serveraddress = tmp.substr(pos1 + 1, pos2); return true; } //Email#serveraddress:port bool NotificationsService::getEmailFromSettings() { - String tmp = notification_ts->get(); - int pos = tmp.indexOf('#'); - if (pos == -1) { + std::string tmp(notification_ts->get()); + int pos = tmp.find('#'); + if (pos == std::string::npos) { return false; } - _settings = tmp.substring(0, pos).c_str(); + _settings = tmp.substr(0, pos); //TODO add a check for valid email ? return true; } diff --git a/FluidNC/src/WebUI/NotificationsService.h b/FluidNC/src/WebUI/NotificationsService.h index b8b3682d7..1177c4fbd 100644 --- a/FluidNC/src/WebUI/NotificationsService.h +++ b/FluidNC/src/WebUI/NotificationsService.h @@ -15,7 +15,6 @@ namespace WebUI { extern NotificationsService notificationsService; } #else -# include # include namespace WebUI { diff --git a/FluidNC/src/WebUI/WSChannel.cpp b/FluidNC/src/WebUI/WSChannel.cpp index 6d0b6ba6d..ea7e7857a 100644 --- a/FluidNC/src/WebUI/WSChannel.cpp +++ b/FluidNC/src/WebUI/WSChannel.cpp @@ -56,14 +56,14 @@ namespace WebUI { return true; } - bool WSChannel::push(String& s) { return push((uint8_t*)s.c_str(), s.length()); } + bool WSChannel::push(std::string& s) { return push((uint8_t*)s.c_str(), s.length()); } void WSChannel::handle() { if (_TXbufferSize > 0 && ((_TXbufferSize >= TXBUFFERSIZE) || ((millis() - _lastflush) > FLUSHTIMEOUT))) { flush(); } } - size_t WSChannel::sendTXT(String& s) { return _server->sendTXT(_clientNum, s); } + size_t WSChannel::sendTXT(std::string& s) { return _server->sendTXT(_clientNum, s.c_str()); } void WSChannel::flush(void) { if (_TXbufferSize > 0) { _server->sendBIN(_clientNum, _TXbuffer, _TXbufferSize); diff --git a/FluidNC/src/WebUI/WSChannel.h b/FluidNC/src/WebUI/WSChannel.h index ba670cef0..4ffccfc98 100644 --- a/FluidNC/src/WebUI/WSChannel.h +++ b/FluidNC/src/WebUI/WSChannel.h @@ -37,7 +37,7 @@ namespace WebUI { size_t write(uint8_t c); size_t write(const uint8_t* buffer, size_t size); - size_t sendTXT(String& s); + size_t sendTXT(std::string& s); inline size_t write(const char* s) { return write((uint8_t*)s, ::strlen(s)); } inline size_t write(unsigned long n) { return write((uint8_t)n); } @@ -50,7 +50,7 @@ namespace WebUI { void handle(); bool push(const uint8_t* data, size_t length); - bool push(String& s); + bool push(std::string& s); void pushRT(char ch); void flush(void); diff --git a/FluidNC/src/WebUI/WebServer.cpp b/FluidNC/src/WebUI/WebServer.cpp index 1c199f9e0..6dbca60bd 100644 --- a/FluidNC/src/WebUI/WebServer.cpp +++ b/FluidNC/src/WebUI/WebServer.cpp @@ -75,12 +75,19 @@ namespace WebUI { FileStream* Web_Server::_uploadFile = nullptr; EnumSetting *http_enable, *http_block_during_motion; - IntSetting *http_port; + IntSetting* http_port; Web_Server::Web_Server() { - http_port = new IntSetting("HTTP Port", WEBSET, WA, "ESP121", "HTTP/Port", DEFAULT_HTTP_PORT, MIN_HTTP_PORT, MAX_HTTP_PORT, NULL); - http_enable = new EnumSetting("HTTP Enable", WEBSET, WA, "ESP120", "HTTP/Enable", DEFAULT_HTTP_STATE, &onoffOptions, NULL); - http_block_during_motion = new EnumSetting("Block serving HTTP content during motion", WEBSET, WA, "", "HTTP/BlockDuringMotion", DEFAULT_HTTP_BLOCKED_DURING_MOTION, &onoffOptions, NULL); + http_port = new IntSetting("HTTP Port", WEBSET, WA, "ESP121", "HTTP/Port", DEFAULT_HTTP_PORT, MIN_HTTP_PORT, MAX_HTTP_PORT, NULL); + http_enable = new EnumSetting("HTTP Enable", WEBSET, WA, "ESP120", "HTTP/Enable", DEFAULT_HTTP_STATE, &onoffOptions, NULL); + http_block_during_motion = new EnumSetting("Block serving HTTP content during motion", + WEBSET, + WA, + "", + "HTTP/BlockDuringMotion", + DEFAULT_HTTP_BLOCKED_DURING_MOTION, + &onoffOptions, + NULL); } Web_Server::~Web_Server() { end(); } @@ -232,7 +239,7 @@ namespace WebUI { } // Send a file, either the specified path or path.gz - bool Web_Server::streamFile(String path, bool download) { + bool Web_Server::myStreamFile(const char* path, bool download) { // If you load or reload WebUI while a program is running, there is a high // risk of stalling the motion because serving a file from // the local FLASH filesystem takes away a lot of CPU cycles. If we get @@ -245,33 +252,49 @@ namespace WebUI { return true; } + bool isGzip = false; FileStream* file; try { file = new FileStream(path, "r", ""); } catch (const Error err) { try { - file = new FileStream(path + ".gz", "r", ""); + std::string spath(path); + file = new FileStream(spath + ".gz", "r", ""); + isGzip = true; } catch (const Error err) { return false; } } if (download) { _webserver->sendHeader("Content-Disposition", "attachment"); } - _webserver->streamFile(*file, getContentType(path)); + log_debug("path " << path << " CT " << getContentType(path)); + _webserver->setContentLength(file->size()); + if (isGzip) { + _webserver->sendHeader("Content-Encoding", "gzip"); + } + _webserver->send(200, getContentType(path), ""); + + // This depends on the fact that FileStream inherits from Stream + // The Arduino implementation of WiFiClient::write(Stream*) just + // reads repetitively from the stream in 1360-byte chunks and + // sends the data over the TCP socket. so nothing special. + _webserver->client().write(*file); + delete file; return true; } - void Web_Server::sendWithOurAddress(String content) { - auto ip = WiFi.getMode() == WIFI_STA ? WiFi.localIP() : WiFi.softAPIP(); - String ipstr = ip.toString(); + void Web_Server::sendWithOurAddress(const char* content) { + auto ip = WiFi.getMode() == WIFI_STA ? WiFi.localIP() : WiFi.softAPIP(); + std::string ipstr = IP_string(ip); if (_port != 80) { ipstr += ":"; - ipstr += String(_port); + ipstr += std::to_string(_port); } - content.replace("$WEB_ADDRESS$", ipstr); - content.replace("$QUERY$", _webserver->uri()); - _webserver->send(200, "text/html", content); + std::string scontent(content); + replace_string_in_place(scontent, "$WEB_ADDRESS$", ipstr); + replace_string_in_place(scontent, "$QUERY$", _webserver->uri().c_str()); + _webserver->send(200, "text/html", scontent.c_str()); } // Captive Portal Page for use in AP mode @@ -296,7 +319,7 @@ namespace WebUI { void Web_Server::handle_root() { if (!(_webserver->hasArg("forcefallback") && _webserver->arg("forcefallback") == "yes")) { - if (streamFile("/index.html")) { + if (myStreamFile("/index.html")) { return; } } @@ -309,22 +332,22 @@ namespace WebUI { // Handle filenames and other things that are not explicitly registered void Web_Server::handle_not_found() { if (is_authenticated() == AuthenticationLevel::LEVEL_GUEST) { - _webserver->sendHeader(String(FPSTR(LOCATION_HEADER)), String(F("/"))); + _webserver->sendHeader(LOCATION_HEADER, "/"); _webserver->send(302); //_webserver->client().stop(); return; } - String path = _webserver->urlDecode(_webserver->uri()); + std::string path(_webserver->urlDecode(_webserver->uri()).c_str()); - if (path.startsWith("/api/")) { + if (path.rfind("/api/", 0) == 0) { _webserver->send(404); return; } // Download a file. The true forces a download instead of displaying the file - if (streamFile(path, true)) { + if (myStreamFile(path.c_str(), true)) { return; } @@ -335,7 +358,7 @@ namespace WebUI { // This lets the user customize the not-found page by // putting a "404.htm" file on the local filesystem - if (streamFile("/404.htm")) { + if (myStreamFile("/404.htm")) { return; } @@ -349,60 +372,60 @@ namespace WebUI { _webserver->send(500); return; } - String templ = "" - "" - "" - "1" - "0" - "" - "http://%s:%u/" - "" - "upnp:rootdevice" - "%s" - "/" - "%s" - "ESP32" - "Marlin" - "http://espressif.com/en/products/hardware/esp-wroom-32/overview" - "Espressif Systems" - "http://espressif.com" - "uuid:%s" - "" - "\r\n" - "\r\n"; - char uuid[37]; - String sip = WiFi.localIP().toString(); - uint32_t chipId = (uint16_t)(ESP.getEfuseMac() >> 32); + const char* templ = "" + "" + "" + "1" + "0" + "" + "http://%s:%u/" + "" + "upnp:rootdevice" + "%s" + "/" + "%s" + "ESP32" + "Marlin" + "http://espressif.com/en/products/hardware/esp-wroom-32/overview" + "Espressif Systems" + "http://espressif.com" + "uuid:%s" + "" + "\r\n" + "\r\n"; + char uuid[37]; + const char* sip = IP_string(WiFi.localIP()).c_str(); + uint32_t chipId = (uint16_t)(ESP.getEfuseMac() >> 32); sprintf(uuid, "38323636-4558-4dda-9188-cda0e6%02x%02x%02x", (uint16_t)((chipId >> 16) & 0xff), (uint16_t)((chipId >> 8) & 0xff), (uint16_t)chipId & 0xff); - String serialNumber = String(chipId); - sschema.printf(templ.c_str(), sip.c_str(), _port, wifi_config.Hostname().c_str(), serialNumber.c_str(), uuid); - _webserver->send(200, "text/xml", (String)sschema); + const char* serialNumber = std::to_string(chipId).c_str(); + sschema.printf(templ, sip, _port, wifi_config.Hostname().c_str(), serialNumber, uuid); + _webserver->send(200, "text/xml", sschema); } void Web_Server::_handle_web_command(bool silent) { AuthenticationLevel auth_level = is_authenticated(); - String cmd = ""; + std::string cmd; if (_webserver->hasArg("plain")) { - cmd = _webserver->arg("plain"); + cmd = std::string(_webserver->arg("plain").c_str()); } else if (_webserver->hasArg("commandText")) { - cmd = _webserver->arg("commandText"); + cmd = std::string(_webserver->arg("commandText").c_str()); } else { _webserver->send(200, "text/plain", "Invalid command"); return; } //if it is internal command [ESPXXX] - cmd.trim(); - int ESPpos = cmd.indexOf("[ESP"); - if (ESPpos > -1) { + // cmd.trim(); + int ESPpos = cmd.find("[ESP"); + if (ESPpos != std::string::npos) { char line[256]; strncpy(line, cmd.c_str(), 255); webClient.attachWS(_webserver, silent); - Error err = settings_execute_line(line, webClient, auth_level); - String answer; + Error err = settings_execute_line(line, webClient, auth_level); + std::string answer; if (err == Error::Ok) { answer = "ok\n"; } else { @@ -411,7 +434,7 @@ namespace WebUI { if (msg) { answer += msg; } else { - answer += static_cast(err); + answer += std::to_string(static_cast(err)); } answer += "\n"; } @@ -421,7 +444,7 @@ namespace WebUI { vTaskDelay(10); if (!webClient.anyOutput()) { - _webserver->send(err != Error::Ok ? 500 : 200, "text/plain", answer); + _webserver->send(err != Error::Ok ? 500 : 200, "text/plain", answer.c_str()); } webClient.detachWS(); } else { //execute GCODE @@ -447,7 +470,7 @@ namespace WebUI { } else if (cmd.length() == 1 && is_realtime_command(cmd[0])) { wsChannel->pushRT(cmd[0]); } else { - if (!cmd.endsWith("\n")) { + if (cmd.length() && cmd[cmd.length() - 1] != '\n') { cmd += '\n'; } hasError = !wsChannel->push(cmd); @@ -462,21 +485,21 @@ namespace WebUI { //login status check void Web_Server::handle_login() { # ifdef ENABLE_AUTHENTICATION - String smsg; - String sUser, sPassword; - String auths; - int code = 200; - bool msg_alert_error = false; + const char* smsg; + std::string sUser, sPassword; + const char* auths; + int code = 200; + bool msg_alert_error = false; //disconnect can be done anytime no need to check credential if (_webserver->hasArg("DISCONNECT")) { - String cookie = _webserver->header("Cookie"); - int pos = cookie.indexOf("ESPSESSIONID="); - String sessionID; - if (pos != -1) { - int pos2 = cookie.indexOf(";", pos); - sessionID = cookie.substring(pos + strlen("ESPSESSIONID="), pos2); + std::string cookie(_webserver->header("Cookie").c_str()); + int pos = cookie.find("ESPSESSIONID="); + std::string sessionID; + if (pos != std::string::npos) { + int pos2 = cookie.find(";", pos); + sessionID = cookie.substr(pos + strlen("ESPSESSIONID="), pos2); } - ClearAuthIP(_webserver->client().remoteIP(), sessionID.c_str()); + ClearAuthIP(_webserver->client().remoteIP(), sessionID); _webserver->sendHeader("Set-Cookie", "ESPSESSIONID=0"); _webserver->sendHeader("Cache-Control", "no-cache"); sendAuth("Ok", "guest", ""); @@ -500,7 +523,7 @@ namespace WebUI { //is there a correct list of query? if (_webserver->hasArg("PASSWORD") && _webserver->hasArg("USER")) { //USER - sUser = _webserver->arg("USER"); + sUser = _webserver->arg("USER").c_str(); if (!((sUser == DEFAULT_ADMIN_LOGIN) || (sUser == DEFAULT_USER_LOGIN))) { msg_alert_error = true; smsg = "Error : Incorrect User"; @@ -509,9 +532,9 @@ namespace WebUI { if (msg_alert_error == false) { //Password - sPassword = _webserver->arg("PASSWORD"); - String sadminPassword = admin_password->get(); - String suserPassword = user_password->get(); + sPassword = _webserver->arg("PASSWORD").c_str(); + std::string sadminPassword(admin_password->get()); + std::string suserPassword(user_password->get()); if (!(sUser == DEFAULT_ADMIN_LOGIN && sPassword == sadminPassword) || (sUser == DEFAULT_USER_LOGIN && sPassword == suserPassword)) { @@ -528,7 +551,7 @@ namespace WebUI { //change password if (_webserver->hasArg("PASSWORD") && _webserver->hasArg("USER") && _webserver->hasArg("NEWPASSWORD") && (msg_alert_error == false)) { - String newpassword = _webserver->arg("NEWPASSWORD"); + std::string newpassword(_webserver->arg("NEWPASSWORD").c_str()); char pwdbuf[MAX_LOCAL_PASSWORD_LENGTH + 1]; newpassword.toCharArray(pwdbuf, MAX_LOCAL_PASSWORD_LENGTH + 1); @@ -570,8 +593,8 @@ namespace WebUI { strcpy(current_auth->userID, sUser.c_str()); current_auth->last_time = millis(); if (AddAuthIP(current_auth)) { - String tmps = "ESPSESSIONID="; - tmps += current_auth->sessionID; + std::string tmps = "ESPSESSIONID="; + tmps += current_auth->sessionID.c_str(); _webserver->sendHeader("Set-Cookie", tmps); _webserver->sendHeader("Cache-Control", "no-cache"); switch (current_auth->level) { @@ -600,12 +623,12 @@ namespace WebUI { sendAuth("Ok", "guest", ""); } else { if (auth_level != AuthenticationLevel::LEVEL_GUEST) { - String cookie = _webserver->header("Cookie"); - int pos = cookie.indexOf("ESPSESSIONID="); - String sessionID; - if (pos != -1) { - int pos2 = cookie.indexOf(";", pos); - sessionID = cookie.substring(pos + strlen("ESPSESSIONID="), pos2); + std::string cookie(_webserver->header("Cookie").c_str()); + int pos = cookie.find("ESPSESSIONID="); + std::string sessionID; + if (pos != std::string::npos) { + int pos2 = cookie.find(";", pos); + sessionID = cookie.substr(pos + strlen("ESPSESSIONID="), pos2); AuthenticationIP* current_auth_info = GetAuth(_webserver->client().remoteIP(), sessionID.c_str()); if (current_auth_info != NULL) { sUser = current_auth_info->userID; @@ -637,14 +660,15 @@ namespace WebUI { void Web_Server::handleFeedholdReload() { protocol_send_event(&feedHoldEvent); // Go to the main page - _webserver->sendHeader(String(FPSTR(LOCATION_HEADER)), String(F("/"))); + _webserver->sendHeader(LOCATION_HEADER, "/"); _webserver->send(302); } //push error code and message to websocket. Used by upload code void Web_Server::pushError(int code, const char* st, bool web_error, uint16_t timeout) { if (_socket_server && st) { - String s = "ERROR:" + String(code) + ":"; + std::string s("ERROR:"); + s += std::to_string(code) + ":"; s += st; try { @@ -688,14 +712,16 @@ namespace WebUI { } else { if ((_upload_status != UploadStatus::FAILED) || (upload.status == UPLOAD_FILE_START)) { if (upload.status == UPLOAD_FILE_START) { - String sizeargname = upload.filename + "S"; - size_t filesize = _webserver->hasArg(sizeargname) ? _webserver->arg(sizeargname).toInt() : 0; - uploadStart(upload.filename, filesize, fs); + std::string sizeargname(upload.filename.c_str()); + sizeargname += "S"; + size_t filesize = _webserver->hasArg(sizeargname.c_str()) ? _webserver->arg(sizeargname.c_str()).toInt() : 0; + uploadStart(upload.filename.c_str(), filesize, fs); } else if (upload.status == UPLOAD_FILE_WRITE) { uploadWrite(upload.buf, upload.currentSize); } else if (upload.status == UPLOAD_FILE_END) { - String sizeargname = upload.filename + "S"; - size_t filesize = _webserver->hasArg(sizeargname) ? _webserver->arg(sizeargname).toInt() : 0; + std::string sizeargname(upload.filename.c_str()); + sizeargname += "S"; + size_t filesize = _webserver->hasArg(sizeargname.c_str()) ? _webserver->arg(sizeargname.c_str()).toInt() : 0; uploadEnd(filesize); } else { //Upload cancelled uploadStop(); @@ -703,36 +729,36 @@ namespace WebUI { } } } - uploadCheck(upload.filename); + uploadCheck(); } - void Web_Server::sendJSON(int code, const String& s) { + void Web_Server::sendJSON(int code, const char* s) { _webserver->sendHeader("Cache-Control", "no-cache"); _webserver->send(200, "application/json", s); } - void Web_Server::sendAuth(const String& status, const String& level, const String& user) { + void Web_Server::sendAuth(const char* status, const char* level, const char* user) { std::string s; JSONencoder j(false, &s); j.begin(); j.member("status", status); - if (level != "") { + if (*level != '\0') { j.member("authentication_lvl", level); } - if (user != "") { + if (*user != '\0') { j.member("user", user); } j.end(); - sendJSON(200, s.c_str()); + sendJSON(200, s); } - void Web_Server::sendStatus(int code, const String& status) { + void Web_Server::sendStatus(int code, const char* status) { std::string s; JSONencoder j(false, &s); j.begin(); j.member("status", status); j.end(); - sendJSON(code, s.c_str()); + sendJSON(code, s); } void Web_Server::sendAuthFailed() { sendStatus(401, "Authentication failed"); } @@ -749,7 +775,7 @@ namespace WebUI { return; } - sendStatus(200, String(int(_upload_status))); + sendStatus(200, std::to_string(int(_upload_status)).c_str()); //if success restart if (_upload_status == UploadStatus::SUCCESSFUL) { @@ -779,10 +805,11 @@ namespace WebUI { //************** if (upload.status == UPLOAD_FILE_START) { log_info("Update Firmware"); - _upload_status = UploadStatus::ONGOING; - String sizeargname = upload.filename + "S"; - if (_webserver->hasArg(sizeargname)) { - maxSketchSpace = _webserver->arg(sizeargname).toInt(); + _upload_status = UploadStatus::ONGOING; + std::string sizeargname(upload.filename.c_str()); + sizeargname += "S"; + if (_webserver->hasArg(sizeargname.c_str())) { + maxSketchSpace = _webserver->arg(sizeargname.c_str()).toInt(); } //check space size_t flashsize = 0; @@ -864,8 +891,8 @@ namespace WebUI { std::error_code ec; - String path = ""; - String sstatus = "Ok"; + std::string path(""); + std::string sstatus("Ok"); if ((_upload_status == UploadStatus::FAILED) || (_upload_status == UploadStatus::FAILED)) { sstatus = "Upload failed"; } @@ -876,14 +903,14 @@ namespace WebUI { //get current path if (_webserver->hasArg("path")) { - path += _webserver->arg("path"); - path.trim(); - path.replace("//", "/"); + path += _webserver->arg("path").c_str(); + // path.trim(); + replace_string_in_place(path, "//", "/"); if (path[path.length() - 1] == '/') { - path = path.substring(0, path.length() - 1); + path = path.substr(0, path.length() - 1); } - if (path.startsWith("/")) { - path = path.substring(1); + if (path.length() & path[0] == '/') { + path = path.substr(1); } } @@ -895,28 +922,28 @@ namespace WebUI { // Handle deletions and directory creation if (_webserver->hasArg("action") && _webserver->hasArg("filename")) { - String action = _webserver->arg("action"); - String filename = _webserver->arg("filename"); + std::string action(_webserver->arg("action").c_str()); + std::string filename = std::string(_webserver->arg("filename").c_str()); if (action == "delete") { if (stdfs::remove(fpath / filename.c_str(), ec)) { sstatus = filename + " deleted"; } else { sstatus = "Cannot delete "; - sstatus += filename + " " + ec.message().c_str(); + sstatus += filename + " " + ec.message(); } } else if (action == "deletedir") { if (stdfs::remove_all(fpath / filename.c_str(), ec)) { sstatus = filename + " deleted"; } else { sstatus = "Cannot delete "; - sstatus += filename + " " + ec.message().c_str(); + sstatus += filename + " " + ec.message(); } } else if (action == "createdir") { - if (stdfs::create_directory(fpath / filename.c_str(), ec)) { + if (stdfs::create_directory(fpath / filename, ec)) { sstatus = filename + " created"; } else { sstatus = "Cannot create "; - sstatus += filename + " " + ec.message().c_str(); + sstatus += filename + " " + ec.message(); } } } @@ -936,8 +963,8 @@ namespace WebUI { j.begin_array("files"); for (auto const& dir_entry : iter) { j.begin_object(); - j.member("name", dir_entry.path().filename().c_str()); - j.member("shortname", dir_entry.path().filename().c_str()); + j.member("name", dir_entry.path().filename()); + j.member("shortname", dir_entry.path().filename()); j.member("size", dir_entry.is_directory() ? -1 : dir_entry.file_size()); j.member("datetime", ""); j.end_object(); @@ -946,28 +973,27 @@ namespace WebUI { } } - String stotalspace, susedspace; - auto space = stdfs::space(fpath, ec); - totalspace = space.capacity; - usedspace = totalspace - space.available; + auto space = stdfs::space(fpath, ec); + totalspace = space.capacity; + usedspace = totalspace - space.available; - j.member("path", path); + j.member("path", path.c_str()); j.member("total", formatBytes(totalspace)); j.member("used", formatBytes(usedspace + 1)); uint32_t percent = totalspace ? (usedspace * 100) / totalspace : 100; - j.member("occupation", String(percent)); + j.member("occupation", percent); j.member("status", sstatus); j.end(); - sendJSON(200, s.c_str()); + sendJSON(200, s); } void Web_Server::handle_direct_SDFileList() { handleFileOps(sdName); } void Web_Server::handleFileList() { handleFileOps(localfsName); } // File upload - void Web_Server::uploadStart(String filename, size_t filesize, const char* fs) { + void Web_Server::uploadStart(const char* filename, size_t filesize, const char* fs) { std::error_code ec; FluidPath fpath { filename, fs, ec }; @@ -1027,7 +1053,6 @@ namespace WebUI { // delete _uploadFile; // _uploadFile = nullptr; - // String path = _uploadFile->path().c_str(); auto fpath = _uploadFile->fpath(); delete _uploadFile; _uploadFile = nullptr; @@ -1065,7 +1090,7 @@ namespace WebUI { _uploadFile = nullptr; } } - void Web_Server::uploadCheck(String filename) { + void Web_Server::uploadCheck() { std::error_code error_code; if (_upload_status == UploadStatus::FAILED) { cancelUpload(); @@ -1091,8 +1116,8 @@ namespace WebUI { } if ((millis() - start_time) > 10000 && _socket_server) { for (WSChannel* wsChannel : webWsChannels) { - String s = "PING:"; - s += String(wsChannel->id()); + std::string s("PING:"); + s += wsChannel->id(); wsChannel->sendTXT(s); } @@ -1127,11 +1152,13 @@ namespace WebUI { wsChannels[num] = wsChannel; if (strcmp(data, "/") == 0) { - String s = "CURRENT_ID:" + String(num); + std::string s("CURRENT_ID:"); + s += std::to_string(num); // send message to client webWsChannels.push_front(wsChannel); wsChannel->sendTXT(s); - s = "ACTIVE_ID:" + String(wsChannel->id()); + s = "ACTIVE_ID:"; + s += std::to_string(wsChannel->id()); wsChannel->sendTXT(s); } } @@ -1147,53 +1174,51 @@ namespace WebUI { } } - //helper to extract content type from file extension - //Check what is the content tye according extension file - String Web_Server::getContentType(String filename) { - String file_name = filename; - file_name.toLowerCase(); - if (filename.endsWith(".htm")) { - return "text/html"; - } else if (file_name.endsWith(".html")) { - return "text/html"; - } else if (file_name.endsWith(".css")) { - return "text/css"; - } else if (file_name.endsWith(".js")) { - return "application/javascript"; - } else if (file_name.endsWith(".png")) { - return "image/png"; - } else if (file_name.endsWith(".gif")) { - return "image/gif"; - } else if (file_name.endsWith(".jpeg")) { - return "image/jpeg"; - } else if (file_name.endsWith(".jpg")) { - return "image/jpeg"; - } else if (file_name.endsWith(".ico")) { - return "image/x-icon"; - } else if (file_name.endsWith(".xml")) { - return "text/xml"; - } else if (file_name.endsWith(".pdf")) { - return "application/x-pdf"; - } else if (file_name.endsWith(".zip")) { - return "application/x-zip"; - } else if (file_name.endsWith(".gz")) { - return "application/x-gzip"; - } else if (file_name.endsWith(".txt")) { - return "text/plain"; - } - return "application/octet-stream"; + //Convert file extension to content type + struct mime_type { + const char* suffix; + const char* mime_type; + } mime_types[] = { + { ".htm", "text/html" }, { ".html", "text/html" }, { ".css", "text/css" }, { ".js", "application/javascript" }, + { ".htm", "text/html" }, { ".png", "image/png" }, { ".gif", "image/gif" }, { ".jpeg", "image/jpeg" }, + { ".jpg", "image/jpeg" }, { ".ico", "image/x-icon" }, { ".xml", "text/xml" }, { ".pdf", "application/x-pdf" }, + { ".zip", "application/x-zip" }, { ".gz", "application/x-gzip" }, { ".txt", "text/plain" }, { "", "application/octet-stream" } + }; + static bool endsWithCI(const char* suffix, const char* test) { + size_t slen = strlen(suffix); + size_t tlen = strlen(test); + if (slen > tlen) { + return false; + } + const char* s = suffix + slen; + const char* t = test + tlen; + while (--s != s) { + if (tolower(*s) != tolower(*--t)) { + return false; + } + } + return true; + } + const char* Web_Server::getContentType(const char* filename) { + mime_type* m; + for (m = mime_types; *(m->suffix) != '\0'; ++m) { + if (endsWithCI(m->suffix, filename)) { + return m->mime_type; + } + } + return m->mime_type; } //check authentification AuthenticationLevel Web_Server::is_authenticated() { # ifdef ENABLE_AUTHENTICATION if (_webserver->hasHeader("Cookie")) { - String cookie = _webserver->header("Cookie"); - int pos = cookie.indexOf("ESPSESSIONID="); - if (pos != -1) { - int pos2 = cookie.indexOf(";", pos); - String sessionID = cookie.substring(pos + strlen("ESPSESSIONID="), pos2); - IPAddress ip = _webserver->client().remoteIP(); + std::string cookie(_webserver->header("Cookie").c_str()); + size_t pos = cookie.find("ESPSESSIONID="); + if (pos != std::string::npos) { + size_t pos2 = cookie.find(";", pos); + std::string sessionID = cookie.substr(pos + strlen("ESPSESSIONID="), pos2); + IPAddress ip = _webserver->client().remoteIP(); //check if cookie can be reset and clean table in same time return ResetAuthIP(ip, sessionID.c_str()); } diff --git a/FluidNC/src/WebUI/WebServer.h b/FluidNC/src/WebUI/WebServer.h index 863193fe7..9434b8d60 100644 --- a/FluidNC/src/WebUI/WebServer.h +++ b/FluidNC/src/WebUI/WebServer.h @@ -17,9 +17,9 @@ class WebSocketsServer; class WebServer; namespace WebUI { - static const int DEFAULT_HTTP_STATE = 1; + static const int DEFAULT_HTTP_STATE = 1; static const int DEFAULT_HTTP_BLOCKED_DURING_MOTION = 1; - static const int DEFAULT_HTTP_PORT = 80; + static const int DEFAULT_HTTP_PORT = 80; static const int MIN_HTTP_PORT = 1; static const int MAX_HTTP_PORT = 65001; @@ -62,7 +62,7 @@ namespace WebUI { static UploadStatus _upload_status; static FileStream* _uploadFile; - static String getContentType(String filename); + static const char* getContentType(const char* filename); static AuthenticationLevel is_authenticated(); # ifdef ENABLE_AUTHENTICATION @@ -89,7 +89,7 @@ namespace WebUI { static void handleUpdate(); static void WebUpdateUpload(); - static bool streamFile(String path, bool download = false); + static bool myStreamFile(const char* path, bool download = false); static void pushError(int code, const char* st, bool web_error = 500, uint16_t timeout = 1000); @@ -98,19 +98,20 @@ namespace WebUI { static void handle_direct_SDFileList(); static void fileUpload(const char* fs); static void SDFileUpload(); - static void uploadStart(String filename, size_t filesize, const char* fs); + static void uploadStart(const char* filename, size_t filesize, const char* fs); static void uploadWrite(uint8_t* buffer, size_t length); static void uploadEnd(size_t filesize); static void uploadStop(); - static void uploadCheck(String filename); + static void uploadCheck(); static void sendFSError(Error err); - static void sendJSON(int code, const String& s); - static void sendAuth(const String& status, const String& level, const String& user); + static void sendJSON(int code, const char* s); + static void sendJSON(int code, const std::string& s) { sendJSON(code, s.c_str()); } + static void sendAuth(const char* status, const char* level, const char* user); static void sendAuthFailed(); - static void sendStatus(int code, const String& str); + static void sendStatus(int code, const char* str); - static void sendWithOurAddress(String s); + static void sendWithOurAddress(const char* s); static void sendCaptivePortal(); static void send404Page(); diff --git a/FluidNC/src/WebUI/WebSettings.cpp b/FluidNC/src/WebUI/WebSettings.cpp index a1e5164f7..45f9ab4d8 100644 --- a/FluidNC/src/WebUI/WebSettings.cpp +++ b/FluidNC/src/WebUI/WebSettings.cpp @@ -23,6 +23,8 @@ #include "WifiConfig.h" #include +#include +#include namespace WebUI { @@ -193,7 +195,9 @@ namespace WebUI { log_to(out, "Chip ID: ", (uint16_t)(ESP.getEfuseMac() >> 32)); log_to(out, "CPU Cores: ", ESP.getChipCores()); log_to(out, "CPU Frequency: ", ESP.getCpuFreqMHz() << "Mhz"); - log_to(out, "CPU Temperature: ", String(temperatureRead(), 1) << "°C"); + std::ostringstream msg; + msg << std::fixed << std::setprecision(1) << temperatureRead() << "°C"; + log_to(out, "CPU Temperature: ", msg.str()); log_to(out, "Free memory: ", formatBytes(ESP.getFreeHeap())); log_to(out, "SDK: ", ESP.getSdkVersion()); log_to(out, "Flash Size: ", formatBytes(ESP.getFlashChipSize())); @@ -271,7 +275,7 @@ namespace WebUI { log_to(out, "Missing file name!"); return Error::InvalidValue; } - String path = trim(parameter); + std::string path(parameter); if (path[0] != '/') { path = "/" + path; } @@ -444,7 +448,7 @@ namespace WebUI { } return Error::Ok; } - static Error copyDir(String iDir, String oDir, Channel& out) { // No ESP command + static Error copyDir(const char* iDir, const char* oDir, Channel& out) { // No ESP command std::error_code ec; { // Block to manage scope of outDir @@ -479,8 +483,12 @@ namespace WebUI { if (dir_entry.is_directory()) { log_error("Not handling localfs subdirectories"); } else { - String opath = oDir + "/" + dir_entry.path().filename().c_str(); - String ipath = iDir + "/" + dir_entry.path().filename().c_str(); + std::string opath(oDir); + opath += "/"; + opath += dir_entry.path().filename().c_str(); + std::string ipath(iDir); + ipath += "/"; + ipath += dir_entry.path().filename().c_str(); log_info_to(out, ipath << " -> " << opath); auto err1 = copyFile(ipath.c_str(), opath.c_str(), out); if (err1 != Error::Ok) { @@ -497,12 +505,16 @@ namespace WebUI { return copyDir("/sd/localfs", "/localfs", out); } static Error migrateLocalFS(char* parameter, AuthenticationLevel auth_level, Channel& out) { // No ESP command + const char* newfs = parameter && *parameter ? parameter : "littlefs"; + if (strcmp(newfs, localfsName) == 0) { + log_error("localfs format is already " << newfs); + return Error::InvalidValue; + } log_info("Backing up local filesystem contents to SD"); Error err = copyDir("/localfs", "/sd/localfs", out); if (err != Error::Ok) { return err; } - const char* newfs = parameter && *parameter ? parameter : "littlefs"; log_info("Reformatting local filesystem to " << newfs); if (localfs_format(newfs)) { return Error::FsFailedFormat; diff --git a/FluidNC/src/WebUI/WifiConfig.cpp b/FluidNC/src/WebUI/WifiConfig.cpp index c73350908..dd589533e 100644 --- a/FluidNC/src/WebUI/WifiConfig.cpp +++ b/FluidNC/src/WebUI/WifiConfig.cpp @@ -12,7 +12,7 @@ WebUI::WiFiConfig wifi_config; #ifdef ENABLE_WIFI # include "../Config.h" -# include "../Main.h" // display() +# include "../Main.h" # include "Commands.h" // COMMANDS # include "WifiServices.h" // wifi_services.start() etc. # include "WebSettings.h" // split_params(), get_params() @@ -44,7 +44,7 @@ namespace WebUI { }; enum WiFiContry { - WiFiCountry01 = 0, // country "01" is the safest set of settings which complies with all regulatory domains + WiFiCountry01 = 0, // country "01" is the safest set of settings which complies with all regulatory domains WiFiCountryAT, WiFiCountryAU, WiFiCountryBE, @@ -92,51 +92,15 @@ namespace WebUI { }; enum_opt_t wifiContryOptions = { - { "01", WiFiCountry01 }, - { "AT", WiFiCountryAT }, - { "AU", WiFiCountryAU }, - { "BE", WiFiCountryBE }, - { "BG", WiFiCountryBG }, - { "BR", WiFiCountryBR }, - { "CA", WiFiCountryCA }, - { "CH", WiFiCountryCH }, - { "CN", WiFiCountryCN }, - { "CY", WiFiCountryCY }, - { "CZ", WiFiCountryCZ }, - { "DE", WiFiCountryDE }, - { "DK", WiFiCountryDK }, - { "EE", WiFiCountryEE }, - { "ES", WiFiCountryES }, - { "FI", WiFiCountryFI }, - { "FR", WiFiCountryFR }, - { "GB", WiFiCountryGB }, - { "GR", WiFiCountryGR }, - { "HK", WiFiCountryHK }, - { "HR", WiFiCountryHR }, - { "HU", WiFiCountryHU }, - { "IE", WiFiCountryIE }, - { "IN", WiFiCountryIN }, - { "IS", WiFiCountryIS }, - { "IT", WiFiCountryIT }, - { "JP", WiFiCountryJP }, - { "KR", WiFiCountryKR }, - { "LI", WiFiCountryLI }, - { "LT", WiFiCountryLT }, - { "LU", WiFiCountryLU }, - { "LV", WiFiCountryLV }, - { "MT", WiFiCountryMT }, - { "MX", WiFiCountryMX }, - { "NL", WiFiCountryNL }, - { "NO", WiFiCountryNO }, - { "NZ", WiFiCountryNZ }, - { "PL", WiFiCountryPL }, - { "PT", WiFiCountryPT }, - { "RO", WiFiCountryRO }, - { "SE", WiFiCountrySE }, - { "SI", WiFiCountrySI }, - { "SK", WiFiCountrySK }, - { "TW", WiFiCountryTW }, - { "US", WiFiCountryUS }, + { "01", WiFiCountry01 }, { "AT", WiFiCountryAT }, { "AU", WiFiCountryAU }, { "BE", WiFiCountryBE }, { "BG", WiFiCountryBG }, + { "BR", WiFiCountryBR }, { "CA", WiFiCountryCA }, { "CH", WiFiCountryCH }, { "CN", WiFiCountryCN }, { "CY", WiFiCountryCY }, + { "CZ", WiFiCountryCZ }, { "DE", WiFiCountryDE }, { "DK", WiFiCountryDK }, { "EE", WiFiCountryEE }, { "ES", WiFiCountryES }, + { "FI", WiFiCountryFI }, { "FR", WiFiCountryFR }, { "GB", WiFiCountryGB }, { "GR", WiFiCountryGR }, { "HK", WiFiCountryHK }, + { "HR", WiFiCountryHR }, { "HU", WiFiCountryHU }, { "IE", WiFiCountryIE }, { "IN", WiFiCountryIN }, { "IS", WiFiCountryIS }, + { "IT", WiFiCountryIT }, { "JP", WiFiCountryJP }, { "KR", WiFiCountryKR }, { "LI", WiFiCountryLI }, { "LT", WiFiCountryLT }, + { "LU", WiFiCountryLU }, { "LV", WiFiCountryLV }, { "MT", WiFiCountryMT }, { "MX", WiFiCountryMX }, { "NL", WiFiCountryNL }, + { "NO", WiFiCountryNO }, { "NZ", WiFiCountryNZ }, { "PL", WiFiCountryPL }, { "PT", WiFiCountryPT }, { "RO", WiFiCountryRO }, + { "SE", WiFiCountrySE }, { "SI", WiFiCountrySI }, { "SK", WiFiCountrySK }, { "TW", WiFiCountryTW }, { "US", WiFiCountryUS }, }; EnumSetting* wifi_mode; @@ -175,10 +139,10 @@ namespace WebUI { { "WPA2-ENTERPRISE", WIFI_AUTH_WPA2_ENTERPRISE }, }; - static void print_mac(Channel& out, const char* prefix, String mac) { log_to(out, prefix, " (" << mac << ")"); } + static void print_mac(Channel& out, const char* prefix, const char* mac) { log_to(out, prefix, " (" << mac << ")"); } static Error showIP(char* parameter, AuthenticationLevel auth_level, Channel& out) { // ESP111 - log_to(out, parameter, (WiFi.getMode() == WIFI_STA ? WiFi.localIP() : WiFi.softAPIP()).toString()); + log_to(out, parameter, IP_string(WiFi.getMode() == WIFI_STA ? WiFi.localIP() : WiFi.softAPIP())); return Error::Ok; } @@ -228,10 +192,10 @@ namespace WebUI { switch (mode) { case WIFI_STA: - print_mac(out, "Current WiFi Mode: STA", WiFi.macAddress()); + print_mac(out, "Current WiFi Mode: STA", WiFi.macAddress().c_str()); if (WiFi.isConnected()) { //in theory no need but ... - log_to(out, "Connected to: ", WiFi.SSID()); + log_to(out, "Connected to: ", WiFi.SSID().c_str()); log_to(out, "Signal: ", wifi_config.getSignal(WiFi.RSSI()) << "%"); uint8_t PhyMode; @@ -256,24 +220,27 @@ namespace WebUI { tcpip_adapter_dhcp_status_t dhcp_status; tcpip_adapter_dhcpc_get_status(TCPIP_ADAPTER_IF_STA, &dhcp_status); log_to(out, "IP Mode: ", (dhcp_status == TCPIP_ADAPTER_DHCP_STARTED ? "DHCP" : "Static")); - log_to(out, "IP: ", WiFi.localIP().toString()); - log_to(out, "Gateway: ", WiFi.gatewayIP().toString()); - log_to(out, "Mask: ", WiFi.subnetMask().toString()); - log_to(out, "DNS: ", WiFi.dnsIP().toString()); + log_to(out, "IP: ", IP_string(WiFi.localIP())); + log_to(out, "Gateway: ", IP_string(WiFi.gatewayIP())); + log_to(out, "Mask: ", IP_string(WiFi.subnetMask())); + log_to(out, "DNS: ", IP_string(WiFi.dnsIP())); } //this is web command so connection => no command - print_mac(out, "Disabled Mode: AP", WiFi.softAPmacAddress()); + print_mac(out, "Disabled Mode: AP", WiFi.softAPmacAddress().c_str()); break; case WIFI_AP: - print_mac(out, "Current WiFi Mode: AP", WiFi.softAPmacAddress()); + print_mac(out, "Current WiFi Mode: AP", WiFi.softAPmacAddress().c_str()); - wifi_config_t conf; + wifi_config_t conf; wifi_country_t country; esp_wifi_get_config(WIFI_IF_AP, &conf); esp_wifi_get_country(&country); log_to(out, "SSID: ", (const char*)conf.ap.ssid); log_to(out, "Visible: ", (conf.ap.ssid_hidden == 0 ? "Yes" : "No")); - log_to(out, "Radio country set: ", country.cc << " (channels " << country.schan << "-" << (country.schan+country.nchan-1) << ", max power " << country.max_tx_power << "dBm)"); + log_to(out, + "Radio country set: ", + country.cc << " (channels " << country.schan << "-" << (country.schan + country.nchan - 1) << ", max power " + << country.max_tx_power << "dBm)"); const char* mode; switch (conf.ap.authmode) { @@ -303,12 +270,12 @@ namespace WebUI { tcpip_adapter_dhcps_get_status(TCPIP_ADAPTER_IF_AP, &dhcp_status); log_to(out, "DHCP Server: ", (dhcp_status == TCPIP_ADAPTER_DHCP_STARTED ? "Started" : "Stopped")); - log_to(out, "IP: ", WiFi.softAPIP().toString()); + log_to(out, "IP: ", IP_string(WiFi.softAPIP())); tcpip_adapter_ip_info_t ip_AP; tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_AP, &ip_AP); - log_to(out, "Gateway: ", IPAddress(ip_AP.gw.addr).toString()); - log_to(out, "Mask: ", IPAddress(ip_AP.netmask.addr).toString()); + log_to(out, "Gateway: ", IP_string(IPAddress(ip_AP.gw.addr))); + log_to(out, "Mask: ", IP_string(IPAddress(ip_AP.netmask.addr))); wifi_sta_list_t station; tcpip_adapter_sta_list_t tcpip_sta_list; @@ -319,15 +286,15 @@ namespace WebUI { for (int i = 0; i < station.num; i++) { log_to(out, "", - wifi_config.mac2str(tcpip_sta_list.sta[i].mac) << " " << IPAddress(tcpip_sta_list.sta[i].ip.addr).toString()); + wifi_config.mac2str(tcpip_sta_list.sta[i].mac) << " " << IP_string(IPAddress(tcpip_sta_list.sta[i].ip.addr))); } - print_mac(out, "Disabled Mode: STA", WiFi.macAddress()); + print_mac(out, "Disabled Mode: STA", WiFi.macAddress().c_str()); break; case WIFI_AP_STA: //we should not be in this state but just in case .... log_to(out, ""); - print_mac(out, "Mixed: STA", WiFi.macAddress()); - print_mac(out, "Mixed: AP", WiFi.softAPmacAddress()); + print_mac(out, "Mixed: STA", WiFi.macAddress().c_str()); + print_mac(out, "Mixed: AP", WiFi.softAPmacAddress().c_str()); break; default: //we should not be there if no wifi .... @@ -372,8 +339,7 @@ namespace WebUI { (bool (*)(char*))WiFiConfig::isPasswordValid); wifi_ap_ssid = new StringSetting( "AP SSID", WEBSET, WA, "ESP105", "AP/SSID", DEFAULT_AP_SSID, MIN_SSID_LENGTH, MAX_SSID_LENGTH, (bool (*)(char*))WiFiConfig::isSSIDValid); - wifi_ap_country = new EnumSetting( - "AP regulatory domain", WEBSET, WA, NULL, "AP/Country", WiFiCountry01, &wifiContryOptions, NULL); + wifi_ap_country = new EnumSetting("AP regulatory domain", WEBSET, WA, NULL, "AP/Country", WiFiCountry01, &wifiContryOptions, NULL); wifi_sta_netmask = new IPaddrSetting("Station Static Mask", WEBSET, WA, NULL, "Sta/Netmask", DEFAULT_STA_MK, NULL); wifi_sta_gateway = new IPaddrSetting("Station Static Gateway", WEBSET, WA, NULL, "Sta/Gateway", DEFAULT_STA_GW, NULL); wifi_sta_ip = new IPaddrSetting("Station Static IP", WEBSET, WA, NULL, "Sta/IP", DEFAULT_STA_IP, NULL); @@ -421,13 +387,13 @@ namespace WebUI { s += std::to_string(webServer.port() + 1) + ":"; switch (WiFi.getMode()) { case WIFI_MODE_AP: - s += WiFi.softAPIP().toString().c_str(); + s += IP_string(WiFi.softAPIP()); break; case WIFI_MODE_STA: - s += WiFi.localIP().toString().c_str(); + s += IP_string(WiFi.localIP()); break; case WIFI_MODE_APSTA: - s += WiFi.softAPIP().toString().c_str(); + s += IP_string(WiFi.softAPIP()); break; default: s += "0.0.0.0"; @@ -449,11 +415,11 @@ namespace WebUI { result += ":Status="; result += (WiFi.status() == WL_CONNECTED) ? "Connected" : "Not connected"; result += ":IP="; - result += WiFi.localIP().toString().c_str(); + result += IP_string(WiFi.localIP()); result += ":MAC="; - String tmp = WiFi.macAddress(); - tmp.replace(":", "-"); - result += tmp.c_str(); + std::string mac(WiFi.macAddress().c_str()); + std::replace(mac.begin(), mac.end(), ':', '-'); + result += mac; } return result; } @@ -470,11 +436,11 @@ namespace WebUI { esp_wifi_get_config(WIFI_IF_AP, &conf); result += (const char*)conf.ap.ssid; result += ":IP="; - result += WiFi.softAPIP().toString().c_str(); + result += IP_string(WiFi.softAPIP()); result += ":MAC="; - String tmp = WiFi.softAPmacAddress(); - tmp.replace(":", "-"); - result += tmp.c_str(); + std::string mac(WiFi.softAPmacAddress().c_str()); + std::replace(mac.begin(), mac.end(), ':', '-'); + result += mac; } return result; } @@ -619,8 +585,7 @@ namespace WebUI { log_info("Connection failed"); return false; case WL_CONNECTED: - log_info("Connected - IP is " << WiFi.localIP().toString()); - display("IP", WiFi.localIP().toString()); + log_info("Connected - IP is " << IP_string(WiFi.localIP())); return true; default: if ((dot > 3) || (dot == 0)) { @@ -654,8 +619,8 @@ namespace WebUI { } WiFi.enableAP(false); //SSID - String SSID = wifi_sta_ssid->get(); - if (SSID.length() == 0) { + const char* SSID = wifi_sta_ssid->get(); + if (strlen(SSID) == 0) { log_info("STA SSID is not set"); return false; } @@ -663,21 +628,20 @@ namespace WebUI { WiFi.setMinSecurity(static_cast(wifi_sta_min_security->get())); WiFi.setScanMethod(wifi_fast_scan->get() ? WIFI_FAST_SCAN : WIFI_ALL_CHANNEL_SCAN); //Get parameters for STA - String h = wifi_hostname->get(); - WiFi.setHostname(h.c_str()); + WiFi.setHostname(wifi_hostname->get()); //password - String password = wifi_sta_password->get(); - int8_t IP_mode = wifi_sta_mode->get(); - int32_t IP = wifi_sta_ip->get(); - int32_t GW = wifi_sta_gateway->get(); - int32_t MK = wifi_sta_netmask->get(); + const char* password = wifi_sta_password->get(); + int8_t IP_mode = wifi_sta_mode->get(); + int32_t IP = wifi_sta_ip->get(); + int32_t GW = wifi_sta_gateway->get(); + int32_t MK = wifi_sta_netmask->get(); //if not DHCP if (IP_mode != DHCP_MODE) { IPAddress ip(IP), mask(MK), gateway(GW); WiFi.config(ip, gateway, mask); } - if (WiFi.begin(SSID.c_str(), (password.length() > 0) ? password.c_str() : NULL)) { - log_info("Connecting to STA SSID:" << SSID.c_str()); + if (WiFi.begin(SSID, (strlen(password) > 0) ? password : NULL)) { + log_info("Connecting to STA SSID:" << SSID); return ConnectSTA2AP(); } else { log_info("Starting client failed"); @@ -701,7 +665,7 @@ namespace WebUI { WiFi.enableSTA(false); WiFi.mode(WIFI_AP); - const char *country = wifi_ap_country->getStringValue(); + const char* country = wifi_ap_country->getStringValue(); if (ESP_OK != esp_wifi_set_country_code(country, true)) { log_error("failed to set Wifi regulatory domain to " << country); } @@ -716,12 +680,12 @@ namespace WebUI { //Get parameters for AP //SSID - String SSID = wifi_ap_ssid->get(); - if (SSID.length() == 0) { + const char* SSID = wifi_ap_ssid->get(); + if (strlen(SSID) == 0) { SSID = DEFAULT_AP_SSID; } - String password = wifi_ap_password->get(); + const char* password = wifi_ap_password->get(); int8_t channel = int8_t(wifi_ap_channel->get()); if (channel == 0) { @@ -732,15 +696,14 @@ namespace WebUI { IPAddress mask; mask.fromString(DEFAULT_AP_MK); - log_info("AP SSID " << SSID << " IP " << ip.toString() << " mask " << mask.toString() << " channel " << channel); + log_info("AP SSID " << SSID << " IP " << IP_string(ip) << " mask " << IP_string(mask) << " channel " << channel); //Set static IP WiFi.softAPConfig(ip, ip, mask); //Start AP - if (WiFi.softAP(SSID.c_str(), (password.length() > 0) ? password.c_str() : NULL, channel)) { + if (WiFi.softAP(SSID, (strlen(password) > 0) ? password : NULL, channel)) { log_info("AP started"); - display("IP", ip.toString()); return true; } @@ -872,7 +835,7 @@ namespace WebUI { default: for (int i = 0; i < n; ++i) { j.begin_object(); - j.member("SSID", WiFi.SSID(i)); + j.member("SSID", WiFi.SSID(i).c_str()); j.member("SIGNAL", wifi_config.getSignal(WiFi.RSSI(i))); j.member("IS_PROTECTED", WiFi.encryptionType(i) != WIFI_AUTH_OPEN); // j->member("IS_PROTECTED", WiFi.encryptionType(i) == WIFI_AUTH_OPEN ? "0" : "1"); diff --git a/FluidNC/src/WebUI/WifiConfig.h b/FluidNC/src/WebUI/WifiConfig.h index a9f2ad63f..3fe426eea 100644 --- a/FluidNC/src/WebUI/WifiConfig.h +++ b/FluidNC/src/WebUI/WifiConfig.h @@ -10,8 +10,6 @@ #include "../Error.h" // Error #include "Authentication.h" // AuthenticationLevel -#include - #ifndef ENABLE_WIFI namespace WebUI { class WiFiConfig { diff --git a/FluidNC/src/WebUI/WifiServices.cpp b/FluidNC/src/WebUI/WifiServices.cpp index 934dd7617..d01a002c6 100644 --- a/FluidNC/src/WebUI/WifiServices.cpp +++ b/FluidNC/src/WebUI/WifiServices.cpp @@ -42,11 +42,9 @@ namespace WebUI { return false; } - String h = wifi_hostname->get(); - ArduinoOTA .onStart([]() { - String type; + const char* type; if (ArduinoOTA.getCommand() == U_FLASH) { type = "sketch"; } else { @@ -86,7 +84,8 @@ namespace WebUI { //no need in AP mode if (WiFi.getMode() == WIFI_STA) { //start mDns - if (!MDNS.begin(h.c_str())) { + const char* h = wifi_hostname->get(); + if (!MDNS.begin(h)) { log_info("Cannot start mDNS"); no_error = false; } else { diff --git a/build-release.py b/build-release.py index eaf45ae62..97dd45959 100644 --- a/build-release.py +++ b/build-release.py @@ -111,11 +111,9 @@ def copyToZip(zipObj, platform, fileName, destPath, mode=0o100755): pioPath = os.path.join('.pio', 'build') - # Put bootloader binaries in the archive - tools = os.path.join(os.path.expanduser('~'),'.platformio','packages','framework-arduinoespressif32','tools') - bootloader = 'bootloader_dio_80m.bin' - zipObj.write(os.path.join(tools, 'sdk', 'esp32', 'bin', bootloader), os.path.join(zipDirName, 'common', bootloader)) + # Put boot_app binary in the archive bootapp = 'boot_app0.bin'; + tools = os.path.join(os.path.expanduser('~'),'.platformio','packages','framework-arduinoespressif32','tools') zipObj.write(os.path.join(tools, "partitions", bootapp), os.path.join(zipDirName, 'common', bootapp)) for secFuses in ['SecurityFusesOK.bin', 'SecurityFusesOK0.bin']: zipObj.write(os.path.join(sharedPath, 'common', secFuses), os.path.join(zipDirName, 'common', secFuses)) @@ -123,10 +121,14 @@ def copyToZip(zipObj, platform, fileName, destPath, mode=0o100755): # Put FluidNC binaries, partition maps, and installers in the archive for envName in ['wifi','bt']: - # Put spiffs.bin and index.html.gz in the archive - # bt does not need a spiffs.bin because there is no use for index.html.gz + # Put bootloader binaries in the archive + bootloader = 'bootloader.bin' + zipObj.write(os.path.join(pioPath, envName, bootloader), os.path.join(zipDirName, envName, bootloader)) + + # Put littlefs.bin and index.html.gz in the archive + # bt does not need a littlefs.bin because there is no use for index.html.gz if envName == 'wifi': - name = 'spiffs.bin' + name = 'littlefs.bin' zipObj.write(os.path.join(pioPath, envName, name), os.path.join(zipDirName, envName, name)) name = 'index.html.gz' zipObj.write(os.path.join('FluidNC', 'data', name), os.path.join(zipDirName, envName, name)) diff --git a/example_configs/TMC2209_corexy.yaml b/example_configs/TMC2209_corexy.yaml index 5435aa67e..97eb3916c 100644 --- a/example_configs/TMC2209_corexy.yaml +++ b/example_configs/TMC2209_corexy.yaml @@ -1,5 +1,7 @@ board: TMC2209 4X name: TMC2209 XYZA +meta: "update for 3.6.8" + stepping: engine: RMT idle_ms: 255 @@ -9,6 +11,14 @@ stepping: kinematics: corexy: + +uart1: + txd_pin: gpio.22 + rxd_pin: gpio.21 + rts_pin: NO_PIN + cts_pin: NO_PIN + baud: 115200 + mode: 8N1 axes: shared_stepper_disable_pin: gpio.25:high @@ -32,11 +42,7 @@ axes: hard_limits: false pulloff_mm: 2.000 tmc_2209: - uart: - txd_pin: gpio.22 - rxd_pin: gpio.21 - baud: 115200 - mode: 8N1 + uart_num: 1 addr: 0 r_sense_ohms: 0.110 run_amps: 0.500 @@ -76,6 +82,7 @@ axes: hard_limits: false pulloff_mm: 2.00 tmc_2209: + uart_num: 1 addr: 1 r_sense_ohms: 0.110 run_amps: 0.500 @@ -114,6 +121,7 @@ axes: hard_limits: false pulloff_mm: 4.00 tmc_2209: + uart_num: 1 addr: 2 r_sense_ohms: 0.110 run_amps: 0.500 diff --git a/example_configs/TMC2209_pen.yaml b/example_configs/TMC2209_pen.yaml index 9396dc689..6059dbdec 100644 --- a/example_configs/TMC2209_pen.yaml +++ b/example_configs/TMC2209_pen.yaml @@ -1,11 +1,21 @@ board: TMC2209 Pen name: TMC2209 Pen +meta: "update for 3.6.8" + stepping: engine: RMT idle_ms: 250 pulse_us: 2 dir_delay_us: 1 disable_delay_us: 0 + +uart1: + txd_pin: gpio.22 + rxd_pin: gpio.21 + rts_pin: NO_PIN + cts_pin: NO_PIN + baud: 115200 + mode: 8N1 axes: shared_stepper_disable_pin: gpio.13 @@ -32,14 +42,7 @@ axes: hard_limits: false pulloff_mm: 1.000 tmc_2209: - uart: - txd_pin: gpio.22 - rxd_pin: gpio.21 - rts_pin: NO_PIN - cts_pin: NO_PIN - baud: 115200 - mode: 8N1 - + uart_num: 1 addr: 0 r_sense_ohms: 0.110 run_amps: 0.500 @@ -88,6 +91,7 @@ axes: hard_limits: false pulloff_mm: 1.000 tmc_2209: + uart_num: 1 addr: 1 r_sense_ohms: 0.110 run_amps: 0.500 diff --git a/example_configs/TMC2209_pen_SG.yaml b/example_configs/TMC2209_pen_SG.yaml index 92b4bbdf8..66f8641b1 100644 --- a/example_configs/TMC2209_pen_SG.yaml +++ b/example_configs/TMC2209_pen_SG.yaml @@ -1,11 +1,21 @@ board: TMC2209 Pen name: TMC2209 Pen +meta: "update for 3.6.8" + stepping: engine: RMT idle_ms: 250 pulse_us: 2 dir_delay_us: 1 disable_delay_us: 0 + +uart1: + txd_pin: gpio.22 + rxd_pin: gpio.21 + rts_pin: NO_PIN + cts_pin: NO_PIN + baud: 115200 + mode: 8N1 axes: shared_stepper_disable_pin: gpio.13 @@ -32,14 +42,7 @@ axes: hard_limits: false pulloff_mm: 1.000 tmc_2209: - uart: - txd_pin: gpio.22 - rxd_pin: gpio.21 - rts_pin: NO_PIN - cts_pin: NO_PIN - baud: 115200 - mode: 8N1 - + uart_num: 1 addr: 0 r_sense_ohms: 0.110 run_amps: 1.00 @@ -88,6 +91,7 @@ axes: hard_limits: false pulloff_mm: 1.000 tmc_2209: + uart_num: 1 addr: 1 r_sense_ohms: 0.110 run_amps: 0.500 diff --git a/example_configs/tmc2209_10V.yaml b/example_configs/tmc2209_10V.yaml index dba3c65f8..19bf960e0 100644 --- a/example_configs/tmc2209_10V.yaml +++ b/example_configs/tmc2209_10V.yaml @@ -1,5 +1,6 @@ name: "ESP32 Dev Controller V4" board: "ESP32 Dev Controller V4" +meta: "update for 3.6.8" stepping: engine: RMT @@ -7,6 +8,14 @@ stepping: dir_delay_us: 1 pulse_us: 2 disable_delay_us: 0 + +uart1: + txd_pin: gpio.22 + rxd_pin: gpio.21 + rts_pin: NO_PIN + cts_pin: NO_PIN + baud: 115200 + mode: 8N1 axes: shared_stepper_disable_pin: gpio.25:high @@ -29,11 +38,7 @@ axes: hard_limits: false pulloff_mm: 1.000 tmc_2209: - uart: - txd_pin: gpio.22 - rxd_pin: gpio.21 - baud: 115200 - mode: 8N1 + uart_num: 1 addr: 0 r_sense_ohms: 0.110 run_amps: 0.500 @@ -73,11 +78,12 @@ axes: limit_pos_pin: gpio.34 limit_all_pin: NO_PIN hard_limits: false - pulloff_mm:1.00 + pulloff_mm: 1.00 tmc_2209: step_pin: gpio.33 direction_pin: gpio.32 microsteps: 16 + uart_num: 1 addr: 1 motor1: @@ -102,10 +108,11 @@ axes: limit_pos_pin: gpio.39 limit_all_pin: NO_PIN hard_limits: false - pulloff_mm:1.00 + pulloff_mm: 1.00 tmc_2209: step_pin: gpio.2 direction_pin: gpio.14 + uart_num: 1 addr: 2 motor1: @@ -131,10 +138,11 @@ axes: limit_pos_pin: gpio.36 limit_all_pin: NO_PIN hard_limits: false - pulloff_mm:1.00 + pulloff_mm: 1.00 tmc_2209: step_pin: gpio.16 direction_pin: gpio.15 + uart_num: 1 addr: 3 motor1: diff --git a/example_configs/tmc2209_BESC.yaml b/example_configs/tmc2209_BESC.yaml index 1053f3f1e..a638d4246 100644 --- a/example_configs/tmc2209_BESC.yaml +++ b/example_configs/tmc2209_BESC.yaml @@ -1,5 +1,6 @@ name: "ESP32 Dev Controller V4" board: "ESP32 Dev Controller V4" +meta: "update for 3.6.8" stepping: engine: RMT @@ -7,6 +8,14 @@ stepping: dir_delay_us: 1 pulse_us: 2 disable_delay_us: 0 + +uart1: + txd_pin: gpio.22 + rxd_pin: gpio.21 + rts_pin: NO_PIN + cts_pin: NO_PIN + baud: 115200 + mode: 8N1 axes: shared_stepper_disable_pin: gpio.25:high @@ -29,11 +38,7 @@ axes: hard_limits: false pulloff_mm: 1.000 tmc_2209: - uart: - txd_pin: gpio.22 - rxd_pin: gpio.21 - baud: 115200 - mode: 8N1 + uart_num: 1 addr: 0 r_sense_ohms: 0.110 run_amps: 0.500 @@ -73,11 +78,12 @@ axes: limit_pos_pin: gpio.34 limit_all_pin: NO_PIN hard_limits: false - pulloff_mm:1.00 + pulloff_mm: 1.00 tmc_2209: step_pin: gpio.33 direction_pin: gpio.32 microsteps: 16 + uart_num: 1 addr: 1 motor1: @@ -102,10 +108,11 @@ axes: limit_pos_pin: gpio.39 limit_all_pin: NO_PIN hard_limits: false - pulloff_mm:1.00 + pulloff_mm: 1.00 tmc_2209: step_pin: gpio.2 direction_pin: gpio.14 + uart_num: 1 addr: 2 motor1: @@ -131,10 +138,11 @@ axes: limit_pos_pin: gpio.36 limit_all_pin: NO_PIN hard_limits: false - pulloff_mm:1.00 + pulloff_mm: 1.00 tmc_2209: step_pin: gpio.16 direction_pin: gpio.15 + uart_num: 1 addr: 3 motor1: diff --git a/example_configs/tmc2209_huanyang.yml b/example_configs/tmc2209_huanyang.yml index c69d876a6..8dda93dc9 100644 --- a/example_configs/tmc2209_huanyang.yml +++ b/example_configs/tmc2209_huanyang.yml @@ -1,5 +1,6 @@ name: "TMC2209 XYZA PWM Spindle" board: "TMC2209 4x" +meta: "update for 3.6.8" stepping: engine: RMT @@ -10,6 +11,14 @@ stepping: homing_init_lock: true +uart1: + txd_pin: gpio.22 + rxd_pin: gpio.21 + rts_pin: NO_PIN + cts_pin: NO_PIN + baud: 115200 + mode: 8N1 + axes: shared_stepper_disable_pin: gpio.25:high diff --git a/example_configs/tmc2209_laser.yaml b/example_configs/tmc2209_laser.yaml index fc48d4504..f72dc9127 100644 --- a/example_configs/tmc2209_laser.yaml +++ b/example_configs/tmc2209_laser.yaml @@ -1,5 +1,6 @@ name: "TMC2209 XYYZ Laser" board: "TMC2209 4x DK" +meta: "update for 3.6.8" stepping: engine: RMT @@ -7,6 +8,14 @@ stepping: dir_delay_us: 1 pulse_us: 2 disable_delay_us: 0 + +uart1: + txd_pin: gpio.22 + rxd_pin: gpio.21 + rts_pin: NO_PIN + cts_pin: NO_PIN + baud: 115200 + mode: 8N1 axes: shared_stepper_disable_pin: gpio.25:high @@ -34,14 +43,7 @@ axes: hard_limits: false pulloff_mm: 1.000 tmc_2209: - uart: - txd_pin: gpio.22 - rxd_pin: gpio.21 - rts_pin: NO_PIN - cts_pin: NO_PIN - baud: 115200 - mode: 8N1 - + uart_num: 1 addr: 0 r_sense_ohms: 0.110 run_amps: 1.200 @@ -90,6 +92,7 @@ axes: hard_limits: false pulloff_mm: 1.000 tmc_2209: + uart_num: 1 addr: 1 r_sense_ohms: 0.110 run_amps: 1.200 @@ -138,6 +141,7 @@ axes: hard_limits: false pulloff_mm: 1.000 tmc_2209: + uart_num: 1 addr: 2 r_sense_ohms: 0.110 run_amps: 1.200 @@ -186,6 +190,7 @@ axes: hard_limits: false pulloff_mm: 1.000 tmc_2209: + uart_num: 1 addr: 3 r_sense_ohms: 0.110 run_amps: 1.200 diff --git a/example_configs/tmc2209_relay.yaml b/example_configs/tmc2209_relay.yaml index 4fd8489ed..8c1f06f8b 100644 --- a/example_configs/tmc2209_relay.yaml +++ b/example_configs/tmc2209_relay.yaml @@ -1,5 +1,6 @@ name: "TMC2209 XYYZ Laser" board: "TMC2209 4x DK" +meta: "update for 3.6.8" stepping: engine: RMT @@ -7,6 +8,14 @@ stepping: dir_delay_us: 1 pulse_us: 2 disable_delay_us: 0 + + uart1: + txd_pin: gpio.22 + rxd_pin: gpio.21 + rts_pin: NO_PIN + cts_pin: NO_PIN + baud: 115200 + mode: 8N1 axes: shared_stepper_disable_pin: gpio.25:high @@ -29,11 +38,7 @@ axes: hard_limits: false pulloff_mm: 1.000 tmc_2209: - uart: - txd_pin: gpio.22 - rxd_pin: gpio.21 - baud: 115200 - mode: 8N1 + uart_num: 1 addr: 0 r_sense_ohms: 0.110 run_amps: 0.500 @@ -73,11 +78,12 @@ axes: limit_pos_pin: gpio.34 limit_all_pin: NO_PIN hard_limits: false - pulloff_mm:1.00 + pulloff_mm: 1.00 tmc_2209: step_pin: gpio.33 direction_pin: gpio.32 microsteps: 16 + uart_num: 1 addr: 1 motor1: @@ -102,10 +108,11 @@ axes: limit_pos_pin: gpio.39 limit_all_pin: NO_PIN hard_limits: false - pulloff_mm:1.00 + pulloff_mm: 1.00 tmc_2209: step_pin: gpio.2 direction_pin: gpio.14 + uart_num: 1 addr: 2 motor1: @@ -131,7 +138,7 @@ axes: limit_pos_pin: gpio.36 limit_all_pin: NO_PIN hard_limits: false - pulloff_mm:1.00 + pulloff_mm: 1.00 tmc_2209: step_pin: gpio.16 direction_pin: gpio.15 diff --git a/install_scripts/posix/tools.sh b/install_scripts/posix/tools.sh index c4a00d703..3917b5394 100644 --- a/install_scripts/posix/tools.sh +++ b/install_scripts/posix/tools.sh @@ -80,7 +80,7 @@ esptool_erase() { esptool_basic erase_flash } -Bootloader="0x1000 common/bootloader_dio_80m.bin" +Bootloader="0x1000 ${BuildType}/bootloader.bin" Bootapp="0xe000 common/boot_app0.bin" Firmware="0x10000 ${BuildType}/firmware.bin" Partitions="0x8000 ${BuildType}/partitions.bin" diff --git a/install_scripts/win64/install-bt.bat b/install_scripts/win64/install-bt.bat index a8a45c249..1caca2337 100644 --- a/install_scripts/win64/install-bt.bat +++ b/install_scripts/win64/install-bt.bat @@ -11,7 +11,7 @@ set EsptoolPath=win64\esptool.exe set BaseArgs=--chip esp32 --baud 921600 set SetupArgs=--before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size detect -set Bootloader=0x1000 common\bootloader_dio_80m.bin +set Bootloader=0x1000 %BuildType%\bootloader.bin set Bootapp=0xe000 common\boot_app0.bin set Firmware=0x10000 %BuildType%\firmware.bin set Partitions=0x8000 %BuildType%\partitions.bin diff --git a/install_scripts/win64/install-wifi.bat b/install_scripts/win64/install-wifi.bat index 5a02c28c6..6eb1f4db3 100644 --- a/install_scripts/win64/install-wifi.bat +++ b/install_scripts/win64/install-wifi.bat @@ -11,7 +11,7 @@ set EsptoolPath=win64\esptool.exe set BaseArgs=--chip esp32 --baud 921600 set SetupArgs=--before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size detect -set Bootloader=0x1000 common\bootloader_dio_80m.bin +set Bootloader=0x1000 %BuildType%\bootloader.bin set Bootapp=0xe000 common\boot_app0.bin set Firmware=0x10000 %BuildType%\firmware.bin set Partitions=0x8000 %BuildType%\partitions.bin diff --git a/min_littlefs.csv b/min_littlefs.csv new file mode 100644 index 000000000..0b6a9ffd0 --- /dev/null +++ b/min_littlefs.csv @@ -0,0 +1,6 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x5000, +otadata, data, ota, 0xe000, 0x2000, +app0, app, ota_0, 0x10000, 0x1E0000, +app1, app, ota_1, 0x1F0000,0x1E0000, +spiffs, data, spiffs, 0x3D0000,0x30000, diff --git a/platformio.ini b/platformio.ini index b878665e7..f2e0961e5 100644 --- a/platformio.ini +++ b/platformio.ini @@ -33,21 +33,28 @@ lib_deps = bt_deps = BluetoothSerial wifi_deps = - ArduinoOTA - DNSServer - ESPmDNS - Update - WebServer - WiFi - WiFiClientSecure arduinoWebSockets=https://github.com/Links2004/arduinoWebSockets + ; If we include the following explicit dependencies when we get the + ; Arduino framework code from github, platformio picks up different + ; and incompatible versions of these libraries from who knows where, + ; ArduinoOTA + ; DNSServer + ; ESPmDNS + ; Update + ; WebServer + ; WiFi + ; WiFiClientSecure [common_esp32_base] -platform = espressif32@^4.3.0 - +platform = https://github.com/platformio/platform-espressif32.git framework = arduino +platform_packages = + framework-arduinoespressif32@ https://github.com/espressif/arduino-esp32.git#2.0.7 +board_build.arduino.upstream_packages = no + upload_speed = 921600 -board_build.partitions = min_spiffs.csv ; For 4M ESP32 +board_build.partitions = min_littlefs.csv ; For 4M ESP32 +board_build.filesystem = littlefs ; board_build.partitions = FluidNC/ld/esp32/app3M_spiffs1M_8MB.csv ; For 8Meg ESP32 ; board_build.partitions = FluidNC/ld/esp32/app3M_spiffs9M_16MB.csv ; For 16Meg ESP32 monitor_speed = 115200 @@ -72,11 +79,22 @@ extra_scripts = FluidNC/ld/esp32/vtable_in_dram.py extends = common_esp32_base board = esp32dev +[common_esp32_s3] +extends = common_esp32_base +board = esp32-s3-devkitc-1 +lib_deps = ${common.lib_deps} + [common_esp32_s2] extends = common_esp32_base board = esp32-s2-saola-1 lib_deps = ${common.lib_deps} +[common_wifi] +build_flags = -DENABLE_WIFI -DUSE_LITTLEFS + +[common_bt] +build_flags = -DENABLE_BLUETOOTH + [env:noradio_s2] extends = common_esp32_s2 lib_deps = ${common.lib_deps} @@ -84,17 +102,17 @@ lib_deps = ${common.lib_deps} [env:wifi_s2] extends = common_esp32_s2 lib_deps = ${common.lib_deps} ${common.wifi_deps} -build_flags = ${common_esp32.build_flags} -DENABLE_WIFI +build_flags = ${common_esp32.build_flags} ${common_wifi.build_flags} [env:bt_s2] extends = common_esp32_s2 lib_deps = ${common.lib_deps} ${common.bt_deps} -build_flags = ${common_esp32.build_flags} -DENABLE_BLUETOOTH +build_flags = ${common_esp32.build_flags} ${common_bt.build_flags}\ [env:wifibt_s2] extends = common_esp32_s2 lib_deps = ${common.lib_deps} ${common.bt_deps} ${common.wifi_deps} -build_flags = ${common_esp32.build_flags} -DENABLE_BLUETOOTH -DENABLE_WIFI +build_flags = ${common_esp32.build_flags} ${common_wifi.build_flags} ${common_bt.build_flags} [env:debug] extends = common_esp32 @@ -108,23 +126,36 @@ lib_deps = ${common.lib_deps} [env:wifi] extends = common_esp32 lib_deps = ${common.lib_deps} ${common.wifi_deps} -build_flags = ${common_esp32.build_flags} -DENABLE_WIFI - -[env:wifi-littlefs] -board_build.filesystem = littlefs -extends = common_esp32 -lib_deps = ${common.lib_deps} ${common.wifi_deps} -build_flags = ${common_esp32.build_flags} -DENABLE_WIFI -DUSE_LITTLEFS +build_flags = ${common_esp32.build_flags} ${common_wifi.build_flags} [env:bt] extends = common_esp32 lib_deps = ${common.lib_deps} ${common.bt_deps} -build_flags = ${common_esp32.build_flags} -DENABLE_BLUETOOTH +build_flags = ${common_esp32.build_flags} ${common_bt.build_flags} [env:wifibt] extends = common_esp32 lib_deps = ${common.lib_deps} ${common.bt_deps} ${common.wifi_deps} -build_flags = ${common_esp32.build_flags} -DENABLE_BLUETOOTH -DENABLE_WIFI +build_flags = ${common_esp32.build_flags} ${common_wifi.build_flags} ${common_bt.build_flags} + +[env:noradio_s3] +extends = common_esp32_s3 +lib_deps = ${common.lib_deps} + +[env:wifi_s3] +extends = common_esp32_s3 +lib_deps = ${common.lib_deps} ${common.wifi_deps} +build_flags = ${common_esp32.build_flags} ${common_wifi.build_flags} + +[env:bt_s3] +extends = common_esp32_s3 +lib_deps = ${common.lib_deps} ${common.bt_deps} +build_flags = ${common_esp32.build_flags} ${common_bt.build_flags} + +[env:wifibt_s3] +extends = common_esp32_s3 +lib_deps = ${common.lib_deps} ${common.bt_deps} ${common.wifi_deps} +build_flags = ${common_esp32.build_flags} ${common_wifi.build_flags} ${common_bt.build_flags} [env:native] platform = native