diff --git a/Grbl_Esp32/src/Grbl.cpp b/Grbl_Esp32/src/Grbl.cpp index 49372bcb..42f0a110 100644 --- a/Grbl_Esp32/src/Grbl.cpp +++ b/Grbl_Esp32/src/Grbl.cpp @@ -116,7 +116,7 @@ void run_once() { // This can exit on a system abort condition, in which case run_once() // is re-executed by an enclosing loop. protocol_main_loop(); - } catch (AssertionFailed ex) { + } catch (const AssertionFailed& ex) { // This means something is terribly broken: grbl_sendf(CLIENT_ALL, "Critical error: %s", ex.stackTrace.c_str()); } diff --git a/Grbl_Esp32/src/Pin.cpp b/Grbl_Esp32/src/Pin.cpp index 771f9c9d..ea47ccd7 100644 --- a/Grbl_Esp32/src/Pin.cpp +++ b/Grbl_Esp32/src/Pin.cpp @@ -8,6 +8,9 @@ #if defined PIN_DEBUG && defined ESP32 # include "Pins/DebugPinDetail.h" +#endif + +#ifdef ESP32 # include "Grbl.h" // grbl_sendf #endif @@ -125,7 +128,7 @@ Pin Pin::create(const String& str) { } } - } catch (AssertionFailed& ex) { // We shouldn't get here under normal circumstances. + } catch (const AssertionFailed& ex) { // We shouldn't get here under normal circumstances. #ifdef PIN_DEBUG # ifdef ESP32 grbl_sendf(CLIENT_ALL, "Failed. Details: %s\r\n", ex.stackTrace.c_str()); diff --git a/Grbl_Esp32/src/PinUsers/LimitedResource.h b/Grbl_Esp32/src/PinUsers/LimitedResource.h new file mode 100644 index 00000000..5b2ab7f2 --- /dev/null +++ b/Grbl_Esp32/src/PinUsers/LimitedResource.h @@ -0,0 +1,33 @@ +#pragma once + +#include + +namespace PinUsers { + /// + /// Helper class that to manage 'Count' resources. Resources can be claimed and released at + /// any time. This helper class keeps track of a bitmap that holds the resources. + /// + template + class LimitedResource { + static_assert(Count > 0, "Resource count cannot be 0 or negative."); + + // We do +32 instead of +31 because 0 should round to 1. Worse case we loose 1 uint. + uint32_t _claimed[(Count + 31) / 32]; + + public: + inline void forceClaim(int index) { _claimed[index / 32] |= (1 << (index % 32)); } + + inline int tryClaim() { + for (int i = 0; i < Count; ++i) { + auto bit = _claimed[i / 32] & (uint32_t(1) << (i % 32)); + if (bit == 0) { + _claimed[i / 32] |= (uint32_t(1) << (i % 32)); + return bit; + } + } + return -1; + } + + inline void release(int index) { _claimed[index / 32] &= ~(uint32_t(1) << (index % 32)); } + }; +} diff --git a/Grbl_Esp32/src/PinUsers/Uart.cpp b/Grbl_Esp32/src/PinUsers/Uart.cpp new file mode 100644 index 00000000..37c060d0 --- /dev/null +++ b/Grbl_Esp32/src/PinUsers/Uart.cpp @@ -0,0 +1,107 @@ +#include "Uart.h" + +#include "../Pin.h" +#include "../Pins/PinOptionsParser.h" +#include "../Assert.h" +#include "LimitedResource.h" +#include + +namespace PinUsers { + + // Native UART in the ESP32 + class NativeUart : public UartDetail { + static LimitedResource<2>& UartResources() { + // The ESP32 chip has three UART controllers (UART0, UART1, and UART2), but + // UART0 is used for Serial0. In other words, we can only make 1 and 2 available. + + static LimitedResource<2> instances_; + return instances_; + } + + Pin tx_; + Pin rx_; + Pin rts_; + + uart_port_t uartPort_; + + public: + NativeUart(Pin tx, Pin rx, Pin rts, Pins::PinOptionsParser& options, Pins::PinOptionsParser& userOptions) : + tx_(tx), rx_(rx), rts_(rts), uartPort_(UART_NUM_MAX) { + // Validate if claiming the resources will err: + Assert(tx.capabilities().has(Pin::Capabilities::Native | Pin::Capabilities::UART | Pin::Capabilities::Output)); + Assert(rx.capabilities().has(Pin::Capabilities::Native | Pin::Capabilities::UART | Pin::Capabilities::Input)); + Assert(rts.capabilities().has(Pin::Capabilities::Native | Pin::Capabilities::UART | Pin::Capabilities::Output)); + + // Iterate options: + uart_config_t uart_config = { 0 }; + int bufferSize = 128; + for (auto opt : options) { + // if (opt.is("baud")) { baudRate = opt.valueInt(); } + // if (opt.is("mode")) { mode = opt.valueString(); /* 8N1 etc */ } + // if (opt.is("bufsize")) { mode = opt.valueString(); /* 128 etc */ } + // if (opt.is("protocol")) { protocol= opt.valueString(); /* RS485 etc */ } + // etc... + } + for (auto opt : userOptions) { + // if (opt.is("baud")) { baudRate = opt.valueInt(); } + // if (opt.is("mode")) { mode = opt.valueString(); /* 8N1 etc */ } + // etc... + } + + int uartIndex = UartResources().tryClaim(); + Assert(uartIndex >= 0); + uartPort_ = static_cast(uartIndex + 1); + + // We have everything we need. Set it up: + + auto txn = tx_.getNative(Pin::Capabilities::UART | Pin::Capabilities::Output); + auto rxn = rx_.getNative(Pin::Capabilities::UART | Pin::Capabilities::Output); + auto rtsn = rts_.getNative(Pin::Capabilities::UART | Pin::Capabilities::Output); + + Assert(uart_param_config(uartPort_, &uart_config) == ESP_OK, "Uart parameter set failed."); + Assert(uart_set_pin(uartPort_, txn, rxn, rtsn, UART_PIN_NO_CHANGE) == ESP_OK, "Uart parameter set failed."); + Assert(uart_driver_install(uartPort_, bufferSize * 2, 0, 0, NULL, 0) == ESP_OK, "Uart driver install failed."); + + // TODO FIXME: We should set the UART mode somewhere better suited than here: + if (uart_set_mode(uartPort_, UART_MODE_RS485_HALF_DUPLEX) != ESP_OK) { + uart_driver_delete(uartPort_); + uartPort_ = UART_NUM_MAX; + + Assert(false, "UART set mode failed"); + } + } + + int write(const uint8_t* ptr, int bytes) override { + // Flush the UART and write the data: + uart_flush(uartPort_); + return uart_write_bytes(uartPort_, reinterpret_cast(ptr), bytes); + } + + int read(uint8_t* ptr, int bytes, int ticksToWait) override { + // Read the response + return uart_read_bytes(uartPort_, ptr, bytes, ticksToWait); + } + + ~NativeUart() override { + // Tear down the uart, give back all resources. + if (uartPort_ != UART_NUM_MAX) { + uart_driver_delete(uartPort_); + } + } + }; + + Uart::Uart(Pin tx, Pin rx, Pin rts, String config, String userConfig) { + Pins::PinOptionsParser configParser(config.begin(), config.end()); + Pins::PinOptionsParser userConfigParser(userConfig.begin(), userConfig.end()); + + // Decide on the RX pin what to do: + if (rx.capabilities().has(Pin::Capabilities::Native)) + { + this->_detail = new NativeUart(tx, rx, rts, configParser, userConfigParser); + } + else { + Assert(false, "Pin is not supported for UART."); + } + } + +} diff --git a/Grbl_Esp32/src/PinUsers/Uart.h b/Grbl_Esp32/src/PinUsers/Uart.h new file mode 100644 index 00000000..c18251fc --- /dev/null +++ b/Grbl_Esp32/src/PinUsers/Uart.h @@ -0,0 +1,125 @@ +#pragma once + +#include "../Pin.h" +#include +#include "../Assert.h" + +namespace PinUsers { + class UartDetail { + public: + virtual int write(const uint8_t* ptr, int bytes) = 0; + virtual int read(uint8_t* ptr, int bytes, int ticksToWait) = 0; + + virtual ~UartDetail() {} + }; + + class Uart { + Pin _tx; + Pin _rx; + Pin _rts; + + UartDetail* _detail; + + public: + Uart() : _tx(Pin::UNDEFINED), _rx(Pin::UNDEFINED), _rts(Pin::UNDEFINED), _detail(nullptr) {} + Uart(Pin tx, Pin rx, Pin rts, String config, String userConfig); + + // TODO FIXME: If _detail is null is currently asserted. We can also just 'do nothing', which might + // be easier in the application. + + // Writes a buffer to the uart. Returns the number of _bytes_ written + template + int writePartial(const T& buffer, int byteOffset = 0) { + writePartial(&buffer, 1, byteOffset); + } + + // Writes a buffer to the uart. Returns the number of _bytes_ written + template + int writePartial(const T* bufferArray, int numberElements, int byteOffset = 0) { + Assert(_detail != nullptr, "Uart is not initialized; cannot write to it."); + auto byteBuf = reinterpret_cast(bufferArray); + return _detail->write(byteBuf + byteOffset, sizeof(T) * numberElements - byteOffset); + } + + // Writes a buffer to the uart. This continues while it can write. If writing succeeded, true is + // returned, otherwise false. + template + bool write(const T& buffer) { + return write(&buffer, 1); + } + + // Writes a buffer to the uart. This continues while it can write. If writing succeeded, true is + // returned, otherwise false. + template + bool write(const T* buffer, int numberElements) { + auto index = 0; + auto limit = sizeof(T) * numberElements; + while (index < limit) { + int size = writePartial(buffer, numberElements, index); + index += size; + + if (size == 0) { + return false; + } + } + return true; + } + + // Reads a buffer from the uart. If ticks elapsed, stops reading. Returns the number of bytes + // that was read. + template + int readPartial(T* bufferArray, int numberElements, int offset = 0, int ticksToWait = 0) { + Assert(_detail != nullptr, "Uart is not initialized; cannot write to it."); + + auto byteArray = reinterpret_cast(bufferArray) + offset; + auto bytes = sizeof(T) * numberElements - offset; + return _detail->read(byteArray, bytes, ticksToWait); + } + + // Reads a buffer from the uart. If ticks elapsed, stops reading. Returns the number of bytes + // that was read. + template + int readPartial(T& buffer, int offset = 0, int ticksToWait = 0) { + return readPartial(&buffer, 1, offset, ticksToWait); + } + + // Reads a buffer from the uart within the given time. The return value is 'true' if this succeeded + // and 'false' if it failed or gave a partial result. + template + bool readOnce(T* bufferArray, int numberElements, int ticksToWait = 0) { + auto bytesRead = readPartial(bufferArray, numberElements, 0, ticksToWait); + return bytesRead == (numberElements * sizeof(T)); + } + + // Reads a buffer from the uart within the given time. The return value is 'true' if this succeeded + // and 'false' if it failed or gave a partial result. + template + bool readOnce(T& buffer, int ticksToWait = 0) { + return readOnce(&buffer, 1, ticksToWait); + } + + // Reads a buffer from the uart. While data is returned, this keeps on reading until the buffer is + // full. If no data is returned anymore, or if the buffer is full, this returns 'true' if successul, + // or 'false' otherwise + template + bool readBlocking(T* bufferArray, int numberElements, int ticksToWait = 0) { + int offset = 0; + int limit = sizeof(T) * numberElements; + while (offset < limit) { + auto bytesRead = readPartial(bufferArray, numberElements, offset, ticksToWait); + if (bytesRead == 0) { break; } + offset += bytesRead; + } + return offset == limit; + } + + // Reads a buffer from the uart within the given time. The return value is 'true' if this succeeded + // and 'false' if it failed or gave a partial result. + template + bool readBlocking(T& buffer, int offset = 0, int ticksToWait = 0) { + return readBlocking(&buffer, 1, ticksToWait); + } + + inline ~Uart() { delete _detail; } + }; +} diff --git a/Grbl_Esp32/test/TestFactory.cpp b/Grbl_Esp32/test/TestFactory.cpp index de16befb..f11f3a22 100644 --- a/Grbl_Esp32/test/TestFactory.cpp +++ b/Grbl_Esp32/test/TestFactory.cpp @@ -80,7 +80,7 @@ void TestFactory::runAll() { current->run(); printf("Passed.\r\n"); - } catch (AssertionFailed& ex) { + } catch (const AssertionFailed& ex) { setColor(12); printf("FAILED!\r\n"); printf(ex.stackTrace.c_str());