diff --git a/.github/workflows/compile-sketch.yml b/.github/workflows/compile-sketch.yml index 86fcf45..eee9338 100644 --- a/.github/workflows/compile-sketch.yml +++ b/.github/workflows/compile-sketch.yml @@ -4,7 +4,10 @@ on: push: branches: - main - #- pull_request + pull_request: + branches: + - main + workflow_dispatch: jobs: @@ -101,11 +104,10 @@ jobs: libraries: | - source-path: ./ sketch-paths: | - - tests + - examples/Example_01_TestCompile enable-warnings-report: true enable-deltas-report: true verbose: true # outputs: # report-artifact-name: ${{ steps.report-artifact-name.outputs.report-artifact-name }} - diff --git a/README.md b/README.md index f9bbb5a..439875f 100644 --- a/README.md +++ b/README.md @@ -33,52 +33,15 @@ The SparkFun Toolkit provides a single implementation of common functionality us Implemented using C++, the SparkFun toolkit follows a simple two layered approach in it's design: A core foundational layer, and a platform specific layer. -```mermaid ---- -title: General Architecture Structure ---- -classDiagram - class CoreToolkit["Core Toolkit Interfaces"] - class PlatformOne["Platform Implementation"] - CoreToolkit <|-- PlatformOne +![Core Architecture](docs/images/rm_img_01.png) -``` -And as additional plaforms are added, they also implement/inherit from the SparkFun Toolkit Core. -```mermaid ---- -title: Multi-Platform Structure ---- -classDiagram - class CoreToolkit["Core Toolkit Interfaces"] - class PlatformOne["Platform One"] - class PlatformTwo["Platform Two"] - - CoreToolkit <|-- PlatformOne - CoreToolkit <|-- PlatformTwo -``` +And as additional platforms are added, they also implement/inherit from the SparkFun Toolkit Core. -When using the SparkFun Toolkit, the intent is for the implementation to follow the same pattern: A platform independent layer that works with the SparkFun Toolkit core, and a platform specific layer that utilizes the SparkFun Toolkit platform specific implementation. +![Multi-Platform Structure](docs/images/rm_img_02.png) -```mermaid ---- -title: Application Structure ---- -classDiagram - direction TD - note for ApplicationCore "Application Logic" - class ApplicationCore["Application Core"] - class CoreToolkit["Core Toolkit Interfaces"] - - note for CoreToolkit "SparkFun Toolkit" - class ApplicationPlatform["Application Platform"] - style ApplicationPlatform fill:#909090 - class PlatformOne["Platform Implementation"] - style PlatformOne fill:#909090 - - CoreToolkit <|-- PlatformOne - ApplicationCore <--> Application Platform +When using the SparkFun Toolkit, the intent is for the implementation to follow the same pattern: A platform independent layer that works with the SparkFun Toolkit core, and a platform specific layer that utilizes the SparkFun Toolkit platform specific implementation. -``` +![Application Structure](docs/images/rm_img_03.png) If/when the application is moved to another platform, just the platform specific logic needs implementation. diff --git a/docs/images/rm_img_01.png b/docs/images/rm_img_01.png new file mode 100644 index 0000000..4574766 Binary files /dev/null and b/docs/images/rm_img_01.png differ diff --git a/docs/images/rm_img_02.png b/docs/images/rm_img_02.png new file mode 100644 index 0000000..0a73c98 Binary files /dev/null and b/docs/images/rm_img_02.png differ diff --git a/docs/images/rm_img_03.png b/docs/images/rm_img_03.png new file mode 100644 index 0000000..082da78 Binary files /dev/null and b/docs/images/rm_img_03.png differ diff --git a/tests/test_bus01/test_bus01.ino b/examples/Example_01_TestCompile/Example_01_TestCompile.ino similarity index 76% rename from tests/test_bus01/test_bus01.ino rename to examples/Example_01_TestCompile/Example_01_TestCompile.ino index a25dec6..f214ffc 100644 --- a/tests/test_bus01/test_bus01.ino +++ b/examples/Example_01_TestCompile/Example_01_TestCompile.ino @@ -6,6 +6,8 @@ sfTkArdI2C myI2C; sfTkArdSPI mySPI; +sfTkArdUART myUART; +sfTkArdUARTBus myUARTBus(myUART); void setup() { diff --git a/library.properties b/library.properties index 8584b70..e52c88b 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=SparkFun Toolkit -version=1.0.0 +version=1.1.0 author=SparkFun Electronics maintainer=SparkFun Electronics sentence=A utility library that other SparkFun libraries can take advantage of. diff --git a/src/SparkFun_Toolkit.h b/src/SparkFun_Toolkit.h index b2c293b..4d88ef4 100644 --- a/src/SparkFun_Toolkit.h +++ b/src/SparkFun_Toolkit.h @@ -21,4 +21,5 @@ // Just include the toolkit headers #include "sfTkArdI2C.h" #include "sfTkArdSPI.h" +#include "sfTkArdUART.h" #include "sfTkArduino.h" diff --git a/src/sfTk/sfTkISerial.h b/src/sfTk/sfTkISerial.h new file mode 100644 index 0000000..e84c966 --- /dev/null +++ b/src/sfTk/sfTkISerial.h @@ -0,0 +1,115 @@ +/** + * @file sfTkISerial.h + * @brief Header file for the SparkFun Toolkit Base Serial Interface Definition. + * + * This file contains the interface declaration for basic serial read/write. + * + * @author SparkFun Electronics + * @date 2025 + * @copyright Copyright (c) 2025, SparkFun Electronics Inc. This project is released under the MIT License. + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +// clang-format off +#include "sfTkError.h" +// clang-format on + +/** + * @brief A base value for serial errors. All serial errors are greater than this value, in the 2000 range + * + */ +const sfTkError_t ksfTkErrBaseSerial = 0x2000; + +/** + * @brief Error code for when a serial system is not initalized. + */ +const sfTkError_t ksfTkErrSerialNotInit = ksfTkErrFail * (ksfTkErrBaseSerial + 1); + +/** + * @brief Returned when a serial interface times out. + * + */ +const sfTkError_t ksfTkErrSerialTimeout = ksfTkErrFail * (ksfTkErrBaseSerial + 2); + +/** + * @brief Returned when a serial interface does not respond. + * + */ +const sfTkError_t ksfTkErrSerialNoResponse = ksfTkErrFail * (ksfTkErrBaseSerial + 3); + +/** + * @brief Returned when the data to be sent is too long or received is too short. + * + */ +const sfTkError_t ksfTkErrSerialDataTooLong = ksfTkErrFail * (ksfTkErrBaseSerial + 4); + +/** + * @brief Returned when the serial settings are null, invalid, or on set/initialized. + * + */ +const sfTkError_t ksfTkErrSerialNullSettings = ksfTkErrFail * (ksfTkErrBaseSerial + 5); + +/** + * @brief Returned when the buffer is null or invalid. + * + */ +const sfTkError_t ksfTkErrSerialNullBuffer = ksfTkErrFail * (ksfTkErrBaseSerial + 6); + +/** + * @brief Returned when the bus is under read. Warning. + * + */ +const sfTkError_t ksfTkErrSerialUnderRead = ksfTkErrBaseSerial + 7; + +class sfTkISerial +{ + public: + sfTkISerial() = default; + virtual ~sfTkISerial() = default; + + /** + * @brief Writes an array of bytes to the serial interface. + * + * @param data The data to write + * @param length The length of the data buffer + * @return sfTkError_t Returns ksfTkErrOk on success, or ksfTkErrFail code + */ + virtual sfTkError_t write(const uint8_t *data, size_t length) = 0; + + /** + * @brief Writes a single byte to the serial interface. + * + * @param data The data to write + * @return sfTkError_t Returns ksfTkErrOk on success, or ksfTkErrFail code + */ + virtual sfTkError_t write(const uint8_t data) + { + return write(&data, sizeof(data)); + } + + /** + * @brief Reads an array of bytes from the serial interface + * + * @param data The data buffer to read into + * @param length The length of the data buffer + * @param readBytes[out] The number of bytes read + * @return sfTkError_t Returns ksfTkErrOk on success, or ksfTkErrFail code + */ + virtual sfTkError_t read(uint8_t *data, size_t length, size_t &readBytes) = 0; + + /** + * @brief Read a single byte from the serial interface + * + * @param data Byte to be read + * @return sfTkError_t Returns ksfTkErrOk on success, or ksfTkErrFail code + */ + virtual sfTkError_t read(uint8_t &data) + { + size_t nRead; + + return read(&data, sizeof(data), nRead); + } +}; diff --git a/src/sfTk/sfTkISerialBus.h b/src/sfTk/sfTkISerialBus.h new file mode 100644 index 0000000..0ece81d --- /dev/null +++ b/src/sfTk/sfTkISerialBus.h @@ -0,0 +1,117 @@ +/** + * @file sfTkISerialBus.h + * @brief Header file for the SparkFun Toolkit Base Serial Bus Interface Definition. + * + * This file contains the interface declaration for connecting sfTkISerial to the sfTkIBus. + * + * @author SparkFun Electronics + * @date 2025 + * @copyright Copyright (c) 2025, SparkFun Electronics Inc. This project is released under the MIT License. + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +// clang-format off +#include "sfTkError.h" +#include "sfTkIBus.h" +#include "sfTkISerial.h" +// clang-format on + +const uint8_t ksfTkBusTypeSerialBus = 0x03; + +class sfTkISerialBus : sfTkIBus +{ + public: + /** + * @brief Constructor for the serial bus + * + */ + sfTkISerialBus() + { + } + + virtual ~sfTkISerialBus() = default; + + /** + * @brief Writes an array of bytes to the serial interface. + * + * @param data The data to write + * @param length The length of the data buffer + * @return sfTkError_t Returns ksfTkErrOk on success, or ksfTkErrFail code + */ + virtual sfTkError_t write(const uint8_t *data, size_t length) = 0; + + /** + * @brief Writes an array of bytes to a register on the target address. Supports any address size + * + * @param devReg The device's register's address - can be any size, If nullptr, address is not sent + * @param regLength The length of the register address. If 0, address is not sent + * @param data The data to write + * @param length The length of the data buffer + * @return sfTkError_t Returns ksfTkErrOk on success, or ksfTkErrFail code + */ + virtual sfTkError_t writeRegister(uint8_t *devReg, size_t regLength, const uint8_t *data, size_t length) override + { + + // Do we have a register? If so write it, else skip. + if (devReg != nullptr && regLength > 0) + write(devReg, regLength); + + // Write the data. + return write(data, length); + } + + /** + * @brief Read an array of bytes from the serial interface + * + * @param data The data buffer to read into + * @param length The length of the data buffer + * @param readBytes[out] The number of bytes read + * @return sfTkError_t Returns ksfTkErrOk on success, or ksfTkErrFail code + */ + virtual sfTkError_t read(uint8_t *data, size_t length, size_t &readBytes) = 0; + + /** + * @brief Reads an array of bytes to a register on the target address. Supports any address size + * + * @param devReg The device's register's address - can be any size + * @param regLength The length of the register address + * @param data The data to buffer to read into + * @param numBytes The length of the data buffer + * @param readBytes[out] The number of bytes read + * @return sfTkError_t Returns ksfTkErrOk on success, or ksfTkErrFail code + */ + virtual sfTkError_t readRegister(uint8_t *devReg, size_t regLength, uint8_t *data, size_t numBytes, + size_t &readBytes) override + { + // Buffer valid? + if (!data) + return ksfTkErrBusNullBuffer; + + sfTkError_t retVal = ksfTkErrOk; + + // Do we have a register? If so, write it, else skip. + if (devReg != nullptr && regLength > 0) + retVal = write(devReg, regLength); + + if (retVal != ksfTkErrOk) + return retVal; + + // Read the data. + retVal = read(data, numBytes, readBytes); + + return retVal; + } + + /** + * @brief Get the type of the object + * + * @return uint8_t The type of the object + */ + virtual uint8_t type(void) override + { + return ksfTkBusTypeSerialBus; + } +}; diff --git a/src/sfTk/sfTkIUART.h b/src/sfTk/sfTkIUART.h new file mode 100644 index 0000000..9ce5c15 --- /dev/null +++ b/src/sfTk/sfTkIUART.h @@ -0,0 +1,274 @@ +/** + * @file sfTkIUART.h + * @brief Header file for the SparkFun Toolkit UART Interface Definition. + * + * This file contains the interface declaration for UART configuration. + * + * @author SparkFun Electronics + * @date 2025 + * @copyright Copyright (c) 2025, SparkFun Electronics Inc. This project is released under the MIT License. + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +// clang-format off +#include "sfTkError.h" +#include "sfTkISerial.h" +// clang-format on + +// Enums follow the Arduino Serial API constants. + +typedef enum _sfTkUARTParity +{ + kUARTParityEven = 0x1ul, + kUARTParityOdd = 0x2ul, + kUARTParityNone = 0x3ul, + kUARTParityMark = 0x4ul, + kUARTParitySpace = 0x5ul +} sfTkUARTParity_t; + +inline const char *parityToString(sfTkUARTParity_t parity) +{ + static const char *parityArray[] = {"Even", "Odd", "None", "Mark", "Space"}; + + // if the parity is out of bounds, return "Unknown" + if (parity < kUARTParityEven || parity > kUARTParitySpace) + return "Unknown"; + + // return the parity string + return parityArray[((uint8_t)parity) - 1]; +} + +typedef enum _sfTkUARTStopBits +{ + kUARTStopBitsOne = 0x10ul, + kUARTStopBitsOneAndHalf = 0x20ul, + kUARTStopBitsTwo = 0x30ul +} sfTkUARTStopBits_t; + +inline const char *stopBitsToString(sfTkUARTStopBits_t stopBits) +{ + static const char *stopBitsArray[] = {"One", "OneAndHalf", "Two"}; + + // Return "Unknown" if index is out of bounds (less than 0 or greater than 2) + if (stopBits < kUARTStopBitsOne || stopBits > kUARTStopBitsTwo) + return "Unknown"; + + // Return the stop bits string + return stopBitsArray[(((uint8_t)stopBits) >> 4) - 1]; +} + +typedef enum _sfTkUARTDataBits +{ + kUARTDataBitsFive = 0x100ul, + kUARTDataBitsSix = 0x200ul, + kUARTDataBitsSeven = 0x300ul, + kUARTDataBitsEight = 0x400ul, +} sfTkUARTDataBits_t; + +inline const uint8_t dataBitsToValue(sfTkUARTDataBits_t dataBits) +{ + static const uint8_t dataBitsArray[] = {5, 6, 7, 8}; + + // Check if data bits are within valid range + if (dataBits < kUARTDataBitsFive || dataBits > kUARTDataBitsEight) + return 0; + + // Extract index using bit shift (removing first 8 bits) and subtract 1 + return dataBitsArray[(((uint16_t)dataBits) >> 8) - 1]; +} + +class sfTkIUART : public sfTkISerial +{ + public: + typedef struct _UARTConfig + { + uint32_t baudRate; + sfTkUARTDataBits_t dataBits; + sfTkUARTParity_t parity; + sfTkUARTStopBits_t stopBits; + } UARTConfig_t; + /** + * @brief Default constructor for the UART bus + * + * @param baudRate + */ + sfTkIUART(uint32_t baudRate = kDefaultBaudRate) + : _config{kDefaultBaudRate, kUARTDataBitsEight, kUARTParityNone, kUARTStopBitsOne} + { + } + + /** + * @brief Constructor for the UART bus with a configuration passed in + * + * @param config + */ + sfTkIUART(UARTConfig_t config) : _config(config) + { + } + + /** + * @brief setter for UART baud rate + * + * @param baudRate The baud rate to set + * @return sfTkError_t Returns ksfTkErrOk on success, or ksfTkErrFail code + */ + virtual sfTkError_t setBaudRate(const uint32_t baudRate) + { + if (_config.baudRate != baudRate) + _config.baudRate = baudRate; // set the baud rate + + return ksfTkErrOk; + } + + /** + * @brief getter for the baud rate + * + * @retval uint32_t returns the baud rate + */ + virtual uint32_t baudRate(void) const + { + return _config.baudRate; + } + + /** + * @brief setter for the stop bits + * + * @param stopBits The stop bits to set + * @return sfTkError_t Returns ksfTkErrOk on success, or ksfTkErrFail code + */ + virtual sfTkError_t setStopBits(const sfTkUARTStopBits_t stopBits) + { + if (_config.stopBits != stopBits) + _config.stopBits = stopBits; + + return ksfTkErrOk; + } + + /** + * @brief getter for the stop bits + * + * @retval sfTkUARTStopBits_t returns the stop bits + */ + virtual sfTkUARTStopBits_t stopBits(void) const + { + return _config.stopBits; + } + + /** + * @brief setter for the parity + * + * @param parity The parity to set + * @return sfTkError_t Returns ksfTkErrOk on success, or ksfTkErrFail code + */ + virtual sfTkError_t setParity(const sfTkUARTParity_t parity) + { + if (_config.parity != parity) + _config.parity = parity; + + return ksfTkErrOk; + } + + /** + * @brief getter for the parity + * + * @retval sfTkUARTParity_t returns the parity + */ + virtual sfTkUARTParity_t parity(void) const + { + return _config.parity; + } + + /** + * @brief setter for the data bits + * + * @param dataBits The data bits to set + * @return sfTkError_t Returns ksfTkErrOk on success, or ksfTkErrFail code + */ + virtual sfTkError_t setDataBits(const sfTkUARTDataBits_t dataBits) + { + if (_config.dataBits != dataBits) + _config.dataBits = dataBits; + + return ksfTkErrOk; + } + + /** + * @brief getter for the data bits + * + * @retval uint8_t returns the data bits + */ + virtual sfTkUARTDataBits_t dataBits(void) const + { + return _config.dataBits; + } + + /** + * @brief setter for the internal config object + * + * @param baudRate The baud rate to set + * @param dataBits The data bits to set + * @param parity The parity to set + * @param stopBits The stop bits to set + * @return sfTkError_t Returns ksfTkErrOk on success, or ksfTkErrFail code + */ + virtual sfTkError_t setConfig(const uint32_t baudRate = kDefaultBaudRate, + const sfTkUARTDataBits_t dataBits = kDefaultDataBits, + const sfTkUARTParity_t parity = kDefaultParity, + const sfTkUARTStopBits_t stopBits = kDefaultStopBits) + { + if (_config.baudRate != baudRate) + _config.baudRate = baudRate; + + if (_config.dataBits != dataBits) + _config.dataBits = dataBits; + + if (_config.parity != parity) + _config.parity = parity; + + if (_config.stopBits != stopBits) + _config.stopBits = stopBits; + + return ksfTkErrOk; + } + + /** + * @brief getter for the internal config object + * + * @return sfTkUARTConfig_t + */ + virtual UARTConfig_t config(void) const + { + return _config; + } + + /** + * @brief kDefaultBaudRate - Default baud rate for the UART bus + * + */ + static constexpr uint32_t kDefaultBaudRate = 115200; + + /** + * @brief kDefaultStopBits - Default stop bits for the UART bus + * + */ + static constexpr sfTkUARTStopBits_t kDefaultStopBits = kUARTStopBitsOne; + + /** + * @brief kDefaultParity - Default parity for the UART bus + * + */ + static constexpr sfTkUARTParity_t kDefaultParity = kUARTParityNone; + + /** + * @brief kDefaultDataBits - Default data bits for the UART bus + * + */ + static constexpr sfTkUARTDataBits_t kDefaultDataBits = kUARTDataBitsEight; + + protected: + /** The internal storage of the UART config */ + UARTConfig_t _config; +}; diff --git a/src/sfTkArdUART.cpp b/src/sfTkArdUART.cpp new file mode 100644 index 0000000..7e63721 --- /dev/null +++ b/src/sfTkArdUART.cpp @@ -0,0 +1,627 @@ +/** + * @file sfTkArdUART.cpp + * @brief Implementation file for the SparkFun Toolkit Arduino UART driver. + * + * This file contains the Arduino UART driver for the SparkFun Toolkit library. + * + * @author SparkFun Electronics + * @date 2025 + * @copyright Copyright (c) 2025, SparkFun Electronics Inc. This project is released under the MIT License. + * + * SPDX-License-Identifier: MIT + */ +#include + +#include +#include + +// clang-format off +#include "sfTkArdUART.h" +#include "sfTk/sfTkError.h" +#include "sfTk/sfTkISerial.h" +#include "sfTk/sfTkIUART.h" +// clang-format on + +sfTkError_t sfTkArdUART::init(HardwareSerial &hwSerial, sfTkIUART::UARTConfig_t &config, bool bInit) +{ + _hwSerial = &hwSerial; // set the serial port + + _config = config; // set the config + + if (bInit) + return _start(); // start the port + + return ksfTkErrOk; +} + +sfTkError_t sfTkArdUART::init(HardwareSerial &hwSerial, uint32_t baudRate, bool bInit) +{ + _hwSerial = &hwSerial; // set the serial port + + _config.baudRate = baudRate; // set the baud rate + + if (bInit) + return _start(); // start the port + + return ksfTkErrOk; +} + +sfTkError_t sfTkArdUART::init(uint32_t baudRate, bool bInit) +{ + // if we don't have a port already, use the default Arduino Serial. + if (!_hwSerial) + return init(Serial, baudRate, bInit); + + // We already have a UART setup, so it's already initialized. Change the baud rate. + return setBaudRate(baudRate); // set the baud rate +} + +sfTkError_t sfTkArdUART::init(sfTkIUART::UARTConfig_t config, bool bInit) +{ + // if we don't have a port already, use the default Arduino Serial. + if (!_hwSerial) + return init(Serial, config, bInit); + + if (bInit) + return _start(); // start the port + + // We already have a UART setup, so it's already initialized. + return ksfTkErrOk; +} + +sfTkError_t sfTkArdUART::init() +{ + return init(kDefaultBaudRate, true); +} + +sfTkError_t sfTkArdUART::write(const uint8_t *data, size_t len) +{ + if (!_hwSerial) + return ksfTkErrSerialNotInit; + + return (_hwSerial->write(data, len) == len ? ksfTkErrOk : ksfTkErrSerialUnderRead); +} + +sfTkError_t sfTkArdUART::write(const uint8_t data) +{ + if (!_hwSerial) + return ksfTkErrSerialNotInit; + + return (_hwSerial->write(data) ? ksfTkErrOk : ksfTkErrFail); +} + +sfTkError_t sfTkArdUART::read(uint8_t *data, size_t length, size_t &bytesRead) +{ + if (!_hwSerial) + return ksfTkErrSerialNotInit; + + if (!data) + return ksfTkErrSerialNullBuffer; + + if (length == 0) + return ksfTkErrSerialDataTooLong; // nothing to read + + bytesRead = 0; // zero out value + +// #ifdef ARDUINO_ARCH_AVR + bytesRead = _hwSerial->readBytes(data, length); +// #else +// bytesRead = readBytes(data, length); +// #endif + + if (bytesRead == 0) + return ksfTkErrFail; + + return (bytesRead == length) ? ksfTkErrOk : ksfTkErrSerialUnderRead; // return success if all bytes read +} + +sfTkError_t sfTkArdUART::read(uint8_t &data) +{ + size_t nRead; + return read(&data, 1, nRead); +} + +sfTkArdUART::operator bool() +{ + return (bool)*_hwSerial; +} + +sfTkError_t sfTkArdUART::setBaudRate(const uint32_t baudRate) +{ + if (_config.baudRate != baudRate) + _config.baudRate = baudRate; // set the baud rate + + return _start(); // start the port again +} + +sfTkError_t sfTkArdUART::setStopBits(const sfTkUARTStopBits_t stopBits) +{ + if (_config.stopBits != stopBits) + _config.stopBits = stopBits; // set the stop bits + + return _start(); // start the port again +} + +sfTkError_t sfTkArdUART::setParity(const sfTkUARTParity_t parity) +{ + if (_config.parity != parity) + _config.parity = parity; // set the baud rate + + return _start(); // start the port again +} + +sfTkError_t sfTkArdUART::setDataBits(const sfTkUARTDataBits_t dataBits) +{ + if (_config.dataBits != dataBits) + _config.dataBits = dataBits; // set the baud rate + + return _start(); // start the port again +} + +sfTkError_t sfTkArdUART::setConfig(const uint32_t baudRate, const sfTkUARTDataBits_t dataBits, + const sfTkUARTParity_t parity, const sfTkUARTStopBits_t stopBits) +{ + if (_config.baudRate != baudRate) + _config.baudRate = baudRate; + + if (_config.dataBits != dataBits) + _config.dataBits = dataBits; + + if (_config.parity != parity) + _config.parity = parity; + + if (_config.stopBits != stopBits) + _config.stopBits = stopBits; + + return _start(); // start the port again +} + +sfTkError_t sfTkArdUART::_start(void) +{ + if (!_hwSerial) + return ksfTkErrSerialNotInit; + if (_running) + end(); // close the port if already running + // set the config +#ifdef ARDUINO_ARCH_ESP8266 + // ESP8266 does not support setting stop bits, parity, and data bits in a stanard manner. + _hwSerial->begin(_config.baudRate); +#else + _hwSerial->begin(_config.baudRate, _config.stopBits | _config.parity | _config.dataBits); +#endif + if (!availableForWrite()) + return ksfTkErrSerialNotInit; // check if the port is available + // set the running flag to true + _running = true; + + return ksfTkErrOk; +} + +void sfTkArdUART::end(void) +{ + _running = false; // set the running flag to false + _hwSerial->end(); +} + +int sfTkArdUART::available(void) +{ + if (!_hwSerial) + return 0; + return _hwSerial->available(); +} + +int sfTkArdUART::availableForWrite(void) +{ + if (!_hwSerial) + return 0; + + return _hwSerial->availableForWrite(); +} + +int sfTkArdUART::peek(void) +{ + if (!_hwSerial) + return 0; + + return _hwSerial->peek(); +} + +void sfTkArdUART::flush(void) +{ + if (!_hwSerial) + return; + + _hwSerial->flush(); +} + +void sfTkArdUART::setTimeout(unsigned long timeout) +{ + if (!_hwSerial) + return; + + _hwSerial->setTimeout(timeout); +} + +unsigned long sfTkArdUART::getTimeout() +{ + if (!_hwSerial) + return 0; + + return _hwSerial->getTimeout(); +} + +#if !defined(ARDUINO_ARCH_AVR) && !defined(ARDUINO_ARCH_ESP8266) +bool sfTkArdUART::find(CONSTVAR char *target) +{ + if (!_hwSerial) + return false; + + return _hwSerial->find(target); +} + +bool sfTkArdUART::find(CONSTVAR uint8_t *target) +{ + if (!_hwSerial) + return false; + + return _hwSerial->find(target); +} + +bool sfTkArdUART::find(CONSTVAR char *target, size_t length) +{ + if (!_hwSerial) + return false; + + return _hwSerial->find(target, length); +} + +bool sfTkArdUART::find(CONSTVAR uint8_t *target, size_t length) +{ + if (!_hwSerial) + return false; + + return _hwSerial->find(target, length); +} + +bool sfTkArdUART::find(char target) +{ + if (!_hwSerial) + return false; + + return _hwSerial->find(target); +} + +bool sfTkArdUART::findUntil(CONSTVAR char *target, CONSTVAR char *terminator) +{ + if (!_hwSerial) + return false; + + return _hwSerial->findUntil(target, terminator); +} + +bool sfTkArdUART::findUntil(CONSTVAR uint8_t *target, CONSTVAR char *terminator) +{ + if (!_hwSerial) + return false; + + return _hwSerial->findUntil(target, terminator); +} + +bool sfTkArdUART::findUntil(CONSTVAR char *target, size_t targetLen, CONSTVAR char *terminate, size_t termLen) +{ + if (!_hwSerial) + return false; + + return _hwSerial->findUntil(target, targetLen, terminate, termLen); +} + +bool sfTkArdUART::findUntil(CONSTVAR uint8_t *target, size_t targetLen, CONSTVAR char *terminate, size_t termLen) +{ + if (!_hwSerial) + return false; + + return _hwSerial->findUntil(target, targetLen, terminate, termLen); +} + +#ifdef ARDUINO_ARCH_ESP8266 +long sfTkArdUART::parseInt() +{ + if (!_hwSerial) + return 0; + + return _hwSerial->parseInt(); +} + +float sfTkArdUART::parseFloat() +{ + if (!_hwSerial) + return 0.0f; + + return _hwSerial->parseFloat(); +} +#else +long sfTkArdUART::parseInt(LookaheadMode lookahead, char ignore) +{ + if (!_hwSerial) + return 0; + + return _hwSerial->parseInt(lookahead, ignore); +} + +float sfTkArdUART::parseFloat(LookaheadMode lookahead, char ignore) +{ + if (!_hwSerial) + return 0.0f; + + return _hwSerial->parseFloat(lookahead, ignore); +} +#endif + +size_t sfTkArdUART::readBytes(char *buffer, size_t length) +{ + if (!_hwSerial) + return 0; + + return _hwSerial->readBytes(buffer, length); +} + +size_t sfTkArdUART::readBytes(uint8_t *buffer, size_t length) +{ + if (!_hwSerial) + return 0; + + return _hwSerial->readBytes(buffer, length); +} + +size_t sfTkArdUART::readBytesUntil(char terminator, char *buffer, size_t length) +{ + if (!_hwSerial) + return 0; + + return _hwSerial->readBytesUntil(terminator, buffer, length); +} + +size_t sfTkArdUART::readBytesUntil(char terminator, uint8_t *buffer, size_t length) +{ + if (!_hwSerial) + return 0; + + return _hwSerial->readBytesUntil(terminator, buffer, length); +} + +String sfTkArdUART::readString() +{ + if (!_hwSerial) + return String(""); + + return _hwSerial->readString(); +} + +String sfTkArdUART::readStringUntil(char terminator) +{ + if (!_hwSerial) + return String(""); + + return _hwSerial->readStringUntil(terminator); +} + +/** + * @brief Print mappings + * + */ +size_t sfTkArdUART::print(const __FlashStringHelper *ifsh) +{ + if (!_hwSerial) + return 0; + + return _hwSerial->print(ifsh); +} + +size_t sfTkArdUART::print(const String &s) +{ + if (!_hwSerial) + return 0; + + return _hwSerial->print(s); +} + +size_t sfTkArdUART::print(const char str[]) +{ + if (!_hwSerial) + return 0; + + return _hwSerial->print(str); +} + +size_t sfTkArdUART::print(char c) +{ + if (!_hwSerial) + return 0; + + return _hwSerial->print(c); +} + +size_t sfTkArdUART::print(unsigned char b, int base) +{ + if (!_hwSerial) + return 0; + + return _hwSerial->print(b, base); +} + +size_t sfTkArdUART::print(int n, int base) +{ + if (!_hwSerial) + return 0; + + return _hwSerial->print(n, base); +} + +size_t sfTkArdUART::print(unsigned int n, int base) +{ + if (!_hwSerial) + return 0; + + return _hwSerial->print(n, base); +} + +size_t sfTkArdUART::print(long n, int base) +{ + if (!_hwSerial) + return 0; + + return _hwSerial->print(n, base); +} + +size_t sfTkArdUART::print(unsigned long n, int base) +{ + if (!_hwSerial) + return 0; + + return _hwSerial->print(n, base); +} + +size_t sfTkArdUART::print(long long n, int base) +{ + if (!_hwSerial) + return 0; + + return _hwSerial->print(n, base); +} + +size_t sfTkArdUART::print(unsigned long long n, int base) +{ + if (!_hwSerial) + return 0; + + return _hwSerial->print(n, base); +} + +size_t sfTkArdUART::print(double n, int digits) +{ + if (!_hwSerial) + return 0; + + return _hwSerial->print(n, digits); +} + +size_t sfTkArdUART::print(const Printable &x) +{ + if (!_hwSerial) + return 0; + + return _hwSerial->print(x); +} + +size_t sfTkArdUART::println(const __FlashStringHelper *ifsh) +{ + if (!_hwSerial) + return 0; + + return _hwSerial->println(ifsh); +} + +size_t sfTkArdUART::println(const String &s) +{ + if (!_hwSerial) + return 0; + + return _hwSerial->println(s); +} + +size_t sfTkArdUART::println(const char c[]) +{ + if (!_hwSerial) + return 0; + + return _hwSerial->println(c); +} + +size_t sfTkArdUART::println(char c) +{ + if (!_hwSerial) + return 0; + + return _hwSerial->println(c); +} + +size_t sfTkArdUART::println(unsigned char b, int base) +{ + if (!_hwSerial) + return 0; + + return _hwSerial->println(b, base); +} + +size_t sfTkArdUART::println(int n, int base) +{ + if (!_hwSerial) + return 0; + + return _hwSerial->println(n, base); +} + +size_t sfTkArdUART::println(unsigned int n, int base) +{ + if (!_hwSerial) + return 0; + + return _hwSerial->println(n, base); +} + +size_t sfTkArdUART::println(long n, int base) +{ + if (!_hwSerial) + return 0; + + return _hwSerial->println(n, base); +} + +size_t sfTkArdUART::println(unsigned long n, int base) +{ + if (!_hwSerial) + return 0; + + return _hwSerial->println(n, base); +} + +size_t sfTkArdUART::println(long long n, int base) +{ + if (!_hwSerial) + return 0; + + return _hwSerial->println(n, base); +} + +size_t sfTkArdUART::println(unsigned long long n, int base) +{ + if (!_hwSerial) + return 0; + + return _hwSerial->println(n, base); +} + +size_t sfTkArdUART::println(double n, int digits) +{ + if (!_hwSerial) + return 0; + + return _hwSerial->println(n, digits); +} + +size_t sfTkArdUART::println(const Printable &x) +{ + if (!_hwSerial) + return 0; + + return _hwSerial->println(x); +} + +size_t sfTkArdUART::println(void) +{ + if (!_hwSerial) + return 0; + + return _hwSerial->println(); +} +#endif diff --git a/src/sfTkArdUART.h b/src/sfTkArdUART.h new file mode 100644 index 0000000..833587c --- /dev/null +++ b/src/sfTkArdUART.h @@ -0,0 +1,551 @@ +/** + * @file sfTkArdUART.h + * @brief header file for the SparkFun Toolkit Arduino UART driver. + * + * This file contains the Arduino UART header for the SparkFun Toolkit library. + * + * @author SparkFun Electronics + * @date 2025 + * @copyright Copyright (c) 2025, SparkFun Electronics Inc. This project is released under the MIT License. + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include +#include +#include +#include + +// clang-format off +// Include our platform UART interface definitions. +#include "sfTk/sfTkError.h" +#include "sfTk/sfTkISerial.h" +#include "sfTkArduino.h" +#include +#include +// clang-format on + +#define NO_IGNORE_CHAR '\x01' // a char not found in a valid ASCII numeric field + +#ifdef ARDUINO_ARCH_AVR +#define CONSTVAR +#else +#define CONSTVAR const +#endif + +/** + * @brief The sfTkArdUART implements an sfTkIUART interface, defining the Arduino implementation for UART in the Toolkit + */ +class sfTkArdUART : public sfTkIUART +{ + public: + /** + * @brief Constructor + */ + sfTkArdUART(void) : sfTkIUART(), _hwSerial{nullptr} + { + } + + /** + * @brief Constructor + * + * @param baudRate The baud rate to set + */ + sfTkArdUART(uint32_t baudRate) : sfTkIUART(baudRate), _hwSerial{nullptr} + { + } + + /** + * @brief Constructor + * + * @param config The UART configuration settings. + */ + sfTkArdUART(UARTConfig_t config) : sfTkIUART(config), _hwSerial{nullptr} + { + } + + /** + * @brief Constructor + * + * @param uartPort Port for UART communication. + */ + sfTkArdUART(HardwareSerial &hwSerial) : sfTkIUART(), _hwSerial{&hwSerial} + { + } + + /** + * @brief Copy Constructor + */ + sfTkArdUART(sfTkArdUART const &rhs) : sfTkIUART(rhs._config), _hwSerial{rhs._hwSerial} + { + } + + /** + * @brief Copy assignment + * + * @param rhs right hand side of the assignment + * @return value of the left hand side of the assignment + */ + sfTkArdUART &operator=(const sfTkArdUART &rhs) + { + if (this != &rhs) + { + sfTkIUART::operator=(rhs); + _hwSerial = rhs._hwSerial; + } + return *this; + } + + /** + * @brief Method sets up the required UART settings. + * @note This function provides a default UART Port. + * + * @retval ksfTkErrOk on successful execution. + */ + sfTkError_t init(void); + + /** + * @brief - address version of the init method + * + * @param baudRate The baud rate to set + */ + sfTkError_t init(uint32_t baudRate, bool bInit = false); + + /** + * @brief config version of the init method + * + * @param config The UART configuration settings. + */ + sfTkError_t init(UARTConfig_t config, bool bInit = false); + + /** + * @brief Method sets up the required UART settings. + * + * @param uartPort Port for UART communication. + * @param baudRate The baud rate to set. + * @param bInit This flag tracks whether the bus has been initialized. + * + * @retval ksfTkErrOk on successful execution. + */ + sfTkError_t init(HardwareSerial &hwSerial, uint32_t baudRate, bool bInit = false); + + /** + * @brief Method sets up the required UART settings. + * + * @param uartPort Port for UART communication. + * @param config The UART configuration settings. + * @param bInit This flag tracks whether the bus has been initialized. + * + * @retval ksftkErrOk on successful execution. + */ + sfTkError_t init(HardwareSerial &hwSerial, sfTkIUART::UARTConfig_t &config, bool bInit = false); + + /** + * @brief Write `len` bytes to the UART TX buffer. + * + * @param data Data buffer to write. + * @param len Number of bytes to write. + * @return sfTkError_t - Returns ksfTkErrOk on success, or ksfTkErrFail code. + */ + sfTkError_t write(const uint8_t *data, size_t len) override; + + /** + * @brief Write one byte to the UART TX buffer. + * + * @param data Byte to write. + * @return sfTkError_t - Returns ksfTkErrOk on success, or ksfTkErrFail code. + */ + sfTkError_t write(const uint8_t data) override; + + /** + * @brief Reads an array of bytes from the serial interface + * + * @param data The data buffer to read into + * @param length The length of the data buffer + * @param bytesRead[out] The number of bytes read + * @return sfTkError_t Returns ksfTkErrOk on success, or ksfTkErrFail code + */ + sfTkError_t read(uint8_t *data, size_t length, size_t &bytesRead) override; + + /** + * @brief Reads a single byte from the serial interface + * + * @param data[out] Byte to be read + * @return sfTkError_t Returns ksfTkErrOk on success, or ksfTkErrFail code + */ + sfTkError_t read(uint8_t &data) override; + + /** + * @brief Checks if UART is running. + * + * @return true - UART is running. + * @return false - UART is not running. + */ + operator bool(); + + /** + * @brief setter for UART baud rate + * + * @param baudRate The baud rate to set + * @return sfTkError_t Returns ksfTkErrOk on success, or ksfTkErrFail code + */ + sfTkError_t setBaudRate(const uint32_t baudRate) override; + + /** + * @brief setter for the stop bits + * + * @param stopBits The stop bits to set + * @return sfTkError_t Returns ksfTkErrOk on success, or ksfTkErrFail code + */ + sfTkError_t setStopBits(const sfTkUARTStopBits_t stopBits) override; + + /** + * @brief setter for the parity + * + * @param parity The parity to set + * @return sfTkError_t Returns ksfTkErrOk on success, or ksfTkErrFail code + */ + sfTkError_t setParity(const sfTkUARTParity_t parity) override; + + /** + * @brief setter for the data bits + * + * @param dataBits The data bits to set + * @return sfTkError_t Returns ksfTkErrOk on success, or ksfTkErrFail code + */ + sfTkError_t setDataBits(const sfTkUARTDataBits_t dataBits) override; + + /** + * @brief setter for the internal config object + * + * @param config The config struct to set + * @return sfTkError_t Returns ksfTkErrOk on success, or ksfTkErrFail code + */ + sfTkError_t setConfig(const uint32_t baudRate = kDefaultBaudRate, + const sfTkUARTDataBits_t dataBits = kDefaultDataBits, + const sfTkUARTParity_t parity = kDefaultParity, + const sfTkUARTStopBits_t stopBits = kDefaultStopBits) override; + + /** + * @brief Arduino HardwareSerial functionality mappings. + * + */ + void end(void); + int available(void); + int availableForWrite(void); + int peek(void); + void flush(void); + + /** + * @brief Arduino Stream functionality mappings. + * + */ + void setTimeout(unsigned long timeout); + unsigned long getTimeout(void); + + bool find(CONSTVAR char *target); + bool find(CONSTVAR uint8_t *target); + + bool find(CONSTVAR char *target, size_t length); + bool find(CONSTVAR uint8_t *target, size_t length); + + bool find(char target); + + bool findUntil(CONSTVAR char *target, CONSTVAR char *terminator); + bool findUntil(CONSTVAR uint8_t *target, CONSTVAR char *terminator); + + bool findUntil(CONSTVAR char *target, size_t targetLen, CONSTVAR char *terminate, size_t termLen); + bool findUntil(CONSTVAR uint8_t *target, size_t targetLen, CONSTVAR char *terminate, size_t termLen); + +#ifdef ARDUINO_ARCH_ESP8266 + long parseInt(); + float parseFloat(); +#else + long parseInt(LookaheadMode lookahead = LookaheadMode::SKIP_ALL, char ignore = NO_IGNORE_CHAR); + float parseFloat(LookaheadMode lookahead = LookaheadMode::SKIP_ALL, char ignore = NO_IGNORE_CHAR); +#endif + + size_t readBytes(char *buffer, size_t length); + size_t readBytes(uint8_t *buffer, size_t length); + + size_t readBytesUntil(char terminator, char *buffer, size_t length); + size_t readBytesUntil(char terminator, uint8_t *buffer, size_t length); + + String readString(void); + String readStringUntil(char terminator); + + /** + * @brief Arduino Print functionality mappings. + * + */ + size_t print(const __FlashStringHelper *); + size_t print(const String &); + size_t print(const char[]); + size_t print(char); + size_t print(unsigned char, int = DEC); + size_t print(int, int = DEC); + size_t print(unsigned int, int = DEC); + size_t print(long, int = DEC); + size_t print(unsigned long, int = DEC); + size_t print(long long, int = DEC); + size_t print(unsigned long long, int = DEC); + size_t print(double, int = 2); + size_t print(const Printable &); + + size_t println(const __FlashStringHelper *); + size_t println(const String &s); + size_t println(const char[]); + size_t println(char); + size_t println(unsigned char, int = DEC); + size_t println(int, int = DEC); + size_t println(unsigned int, int = DEC); + size_t println(long, int = DEC); + size_t println(unsigned long, int = DEC); + size_t println(long long, int = DEC); + size_t println(unsigned long long, int = DEC); + size_t println(double, int = 2); + size_t println(const Printable &); + size_t println(void); + + + protected: + /** The actual Arduino hardware port */ + HardwareSerial *_hwSerial; + + private: + bool _running = false; // Flag to track if the bus is running + sfTkError_t _start(void); // Start the connection to the UART port +}; + +/** + * @brief The sfTkArdUARTBus class implements an sfTkIUARTBus interface, connecting Arduino UART to the sfTkIBus + * interface. + * + */ +class sfTkArdUARTBus : public sfTkISerialBus +{ + public: + /** + * @brief Constructor for the UART bus + * + */ + sfTkArdUARTBus(void) : sfTkISerialBus(), _uartPort{nullptr} + { + } + + /** + * @brief Construct a new sfTkArdUARTBus object + * + * @param uartPort UART port to use + */ + sfTkArdUARTBus(sfTkArdUART &uartPort) : sfTkISerialBus(), _uartPort{&uartPort} + { + } + + /** + * @brief Construct a new sfTkArdUARTBus object + * + * @param hwSerial Pass in an underlying hardware serial port + */ + sfTkArdUARTBus(HardwareSerial &hwSerial) : sfTkISerialBus() + { + _uartPort = new sfTkArdUART(hwSerial); + } + + /** + * @brief Copy constructor + * + * @param rhs Bus object to be copied + */ + sfTkArdUARTBus(sfTkArdUARTBus const &rhs) : sfTkISerialBus() + { + _uartPort = rhs._uartPort ? new sfTkArdUART(*rhs._uartPort) : nullptr; + } + + /** + * @brief Destructor + */ + ~sfTkArdUARTBus() + { + delete _uartPort; + } + + /** + * @brief Copy assignment + * + * @param rhs right hand side of the assignment + * @return value of the left hand side of the assignment + */ + sfTkArdUARTBus &operator=(const sfTkArdUARTBus &rhs) + { + if (this != &rhs) + { + delete _uartPort; + _uartPort = rhs._uartPort ? new sfTkArdUART(*rhs._uartPort) : nullptr; + } + return *this; + } + + /** + * @brief Method sets up the required UART settings. + * @note This function provides a default UART Port. + * + * @retval ksfTkErrOk on successful execution. + */ + sfTkError_t init(void) + { + if (!_uartPort) + _uartPort = new sfTkArdUART(); + return _uartPort->init(); + } + + /** + * @brief - address version of the init method + * + * @param baudRate The baud rate to set + */ + sfTkError_t init(uint32_t baudRate, bool bInit = false) + { + if (!_uartPort) + _uartPort = new sfTkArdUART(); + return _uartPort->init(baudRate, bInit); + } + + /** + * @brief - config version of the init method + * + * @param config The configuration to set + */ + sfTkError_t init(sfTkIUART::UARTConfig_t config, bool bInit = false) + { + if (!_uartPort) + _uartPort = new sfTkArdUART(); + return _uartPort->init(config, bInit); + } + + /** + * @brief Method sets up the required UART settings using the provided UART port. + * + * @param uartPort Port for communication + * @param baudRate The baud rate to set + * @param bInit This flag tracks whether the bus has been initialized. + * @return sfTkError_t ksfTkErrOk on successful execution. + */ + sfTkError_t init(sfTkArdUART &uartPort, uint32_t baudRate, bool bInit = false) + { + delete _uartPort; // Delete existing reference to port. + _uartPort = new sfTkArdUART(uartPort); + return _uartPort->init(baudRate, bInit); + } + + /** + * @brief Method sets up the required UART settings using the provided UART port. + * + * @param uartPort Port for communication + * @param config The configuration to set + * @param bInit This flag tracks whether the bus has been initialized. + * @return sfTkError_t ksfTkErrOk on successful execution. + */ + sfTkError_t init(sfTkArdUART &uartPort, sfTkIUART::UARTConfig_t config, bool bInit = false) + { + delete _uartPort; // Delete existing reference to port. + _uartPort = new sfTkArdUART(uartPort); + return _uartPort->init(config, bInit); + } + + /** + * @brief Method sets up the required UART settings using the provided UART port. + * + * @param uartPort Port for communication + * @param bInit This flag tracks whether the bus has been initialized. + * @return sfTkError_t ksfTkErrOk on successful execution. + */ + sfTkError_t init(sfTkArdUART &uartPort, bool bInit = false) + { + delete _uartPort; // Delete existing reference to port. + _uartPort = new sfTkArdUART(uartPort); + return _uartPort->init(sfTkIUART::kDefaultBaudRate, bInit); + } + + /** + * @brief Method sets up the required UART settings using the provided HardwareSerial port. + * + * @param hwSerial The hardware serial port to use + * @param baudRate The baud rate to set + * @param bInit This flag tracks whether the bus has been initialized. + * @return sfTkError_t ksfTkErrOk on successful execution. + */ + sfTkError_t init(HardwareSerial &hwSerial, uint32_t baudRate, bool bInit = false) + { + delete _uartPort; // Delete existing reference to port. + _uartPort = new sfTkArdUART(hwSerial); + return _uartPort->init(baudRate, bInit); + } + + /** + * @brief Method sets up the required UART settings using the provided HardwareSerial port. + * + * @param hwSerial The hardware serial port to use + * @param config The configuration to set + * @param bInit This flag tracks whether the bus has been initialized. + * @return sfTkError_t ksfTkErrOk on successful execution. + */ + sfTkError_t init(HardwareSerial &hwSerial, sfTkIUART::UARTConfig_t config, bool bInit = false) + { + delete _uartPort; // Delete existing reference to port. + _uartPort = new sfTkArdUART(hwSerial); + return _uartPort->init(config, bInit); + } + + /** + * @brief Method sets up the required UART settings using the provided HardwareSerial port. + * + * @param hwSerial The hardware serial port to use + * @param bInit This flag tracks whether the bus has been initialized. + * @return sfTkError_t ksfTkErrOk on successful execution. + */ + sfTkError_t init(HardwareSerial &hwSerial, bool bInit = false) + { + delete _uartPort; // Delete existing reference to port. + _uartPort = new sfTkArdUART(hwSerial); + return _uartPort->init(sfTkIUART::kDefaultBaudRate, bInit); + } + + /** + * @brief Write `len` bytes to the UART TX buffer. + * + * @param data Data buffer to write. + * @param len Number of bytes to write. + * @return sfTkError_t - Returns ksfTkErrOk on success, or ksfTkErrFail code. + */ + sfTkError_t write(const uint8_t *data, size_t length) override + { + if (!_uartPort) + return ksfTkErrBusNotInit; + + sfTkError_t retVal = _uartPort->write(data, length); + + return retVal; + } + + /** + * @brief Reads an array of bytes from the serial interface + * + * @param data The data buffer to read into + * @param length The length of the data buffer + * @param bytesRead[out] The number of bytes read + * @return sfTkError_t Returns ksfTkErrOk on success, or ksfTkErrFail code + */ + sfTkError_t read(uint8_t *data, size_t length, size_t &readBytes) override + { + if (!_uartPort) + return ksfTkErrBusNotInit; + return _uartPort->read(data, length, readBytes); + } + + protected: + /** The actual UART port */ + sfTkArdUART* _uartPort; +}; diff --git a/tests/test_uart/test_uart.ino b/tests/test_uart/test_uart.ino new file mode 100644 index 0000000..e49b502 --- /dev/null +++ b/tests/test_uart/test_uart.ino @@ -0,0 +1,254 @@ +// Slightly more comprehensive test for UART. + +#include "SparkFun_Toolkit.h" + +sfTkArdUART myUART; +sfTkArdUART myExtUART(Serial1); +sfTkArdUARTBus serialBus1(myExtUART); + +// Button variables +bool lastButtonState = LOW; +unsigned long lastDebounceTime = 0; +unsigned long debounceDelay = 100; +bool buttonPressed = false; + +void setup() { + pinMode(LED_BUILTIN, OUTPUT); + pinMode(PIN_WL_LED, OUTPUT); + pinMode(USER_SW, INPUT); + Serial.begin(115200); + while(!Serial) { delay(100);}; + Serial.println("Begin UART test."); + sfTkError_t err = myUART.init(); + if(ksfTkErrOk != err) { + Serial.print("Failed to init UART. Error: "); + Serial.println(err); + while(1){}; + } + test1(); + test2(); + test3(); + test4(); + Serial.println("Test complete. Cue the blinkin' lights."); +} + +void loop() { + digitalWrite(LED_BUILTIN, HIGH); + delay(500); + digitalWrite(LED_BUILTIN, LOW); + delay(500); +} + +void test1() { + myUART.println("Test 1: Use println and write functions."); + myUART.println(F("This statement printed with flash string helper.")); + myUART.println("Printing ASCII alphabet: "); + for (int i = 0; i < 58; i++) + { + if (i == 26) + { + myUART.write('\n'); + i += 6; // Skips "[\]^_`" + } + myUART.write(65+i); + } + myUART.write('\n'); + myUART.println("Test 1 complete. Waiting for button press to continue."); + waitForButtonPress(); +} + +void test2() { + myUART.println("Test 2: Get UART parameters."); + sfTkIUART::UARTConfig_t config = myUART.config(); + myUART.print("Baud rate: "); + myUART.println(config.baudRate); + myUART.print("Data bits: "); + myUART.println(dataBitsToValue(config.dataBits)); + myUART.print("Parity: "); + myUART.println(parityToString(config.parity)); + myUART.print("Stop bits: "); + myUART.println(stopBitsToString(config.stopBits)); + myUART.println("Test 2 complete. Waiting for button press to continue."); + waitForButtonPress(); +} + +void test3() { + myUART.println("Test 3: UART Configuration and Loopback Test"); + + // Initialize Serial1 with default settings first + sfTkError_t err = myExtUART.init(); + if(ksfTkErrOk != err) { + myUART.print("Failed to init external UART. Error: "); + myUART.println(err); + while(1){}; + } + + // Get initial configuration + sfTkIUART::UARTConfig_t initialConfig = myExtUART.config(); + myUART.println("Initial UART configuration:"); + printUARTConfig(initialConfig); + + // Test different configurations + sfTkIUART::UARTConfig_t testConfigs[] = { + // Test config 1: 9600 baud, 7 bits, odd parity, 2 stop bits + {9600, kUARTDataBitsSeven, kUARTParityOdd, kUARTStopBitsTwo}, + // Test config 2: 57600 baud, 8 bits, even parity, 1 stop bit + {57600, kUARTDataBitsEight, kUARTParityEven, kUARTStopBitsOne}, + // Test config 3: 115200 baud, 8 bits, no parity, 1 stop bit + {115200, kUARTDataBitsEight, kUARTParityNone, kUARTStopBitsOne} + }; + + myUART.println("\nShort TX to RX on Serial1 and press the user button to begin tests."); + waitForButtonPress(); + + for (int i = 0; i < 3; i++) { + myUART.print("\nTesting configuration #"); + myUART.println(i + 1); + printUARTConfig(testConfigs[i]); + + // Apply new configuration + err = myExtUART.setConfig(testConfigs[i].baudRate, testConfigs[i].dataBits, testConfigs[i].parity, testConfigs[i].stopBits); + if (err != ksfTkErrOk) { + myUART.print("Failed to set UART config. Error: "); + myUART.println(err); + continue; + } + + // Test data patterns + uint8_t testPatterns[][4] = { + {0x55, 0xAA, 0x55, 0xAA}, // Alternating bits + {0x00, 0xFF, 0x00, 0xFF}, // Full swing + {'T', 'E', 'S', 'T'}, // ASCII text + {0x12, 0x34, 0x56, 0x78} // Incremental bytes + }; + + uint8_t dataBits = dataBitsToValue(testConfigs[i].dataBits); + + for (int p = 0; p < 4; p++) { + myUART.print(" Pattern "); + myUART.print(p + 1); + myUART.print(": "); + + // Send test pattern + myExtUART.write(testPatterns[p], 4); + if (err != ksfTkErrOk) { + myUART.print("Write Error: "); + myUART.println(err); + continue; + } + myExtUART.flush(); + delay(50); // Allow time for transmission + + // Read back the response + uint8_t readBuf[4] = {0}; + size_t bytesRead = 0; + err = myExtUART.read(readBuf, 4, bytesRead); + + // Verify response + bool success = true; + for (int b = 0; b < 4; b++) { + if (readBuf[b] != (testPatterns[p][b] & ~(1 << dataBits))) { + success = false; + break; + } + } + + if (success && bytesRead == 4) { + myUART.println("PASS"); + } else { + myUART.print("FAIL - Sent: "); + printHexArray(testPatterns[p], 4); + myUART.print(" Received: "); + printHexArray(readBuf, bytesRead); + myUART.println(); + } + } + } + + // Restore initial configuration + myExtUART.setConfig(initialConfig.baudRate, initialConfig.dataBits, initialConfig.parity, initialConfig.stopBits); + myUART.println("\nTest 3 complete. Waiting for button press to continue."); + waitForButtonPress(); +} + +void test4() { + myUART.println("Test 4: Send and Receive data via Bus functions."); + myUART.println("Keep Serial1 shorted TX to RX."); + + sfTkError_t err = serialBus1.init(); + if(ksfTkErrOk != err) { + Serial.print("Failed to init external UART Bus. Error: "); + Serial.println(err); + while(1){}; + } + + myUART.println("Ready to send data. Press user button to continue."); + waitForButtonPress(); + + uint8_t testReg[] = {0x21}; + uint8_t testData[] = {0x5A}; + uint8_t readData[1] = {0}; + size_t bytesRead = 0; + + myUART.println("Sending register 0x21 with data 0x5A"); + err = serialBus1.writeRegister(testReg, 1, testData, 1); + if(ksfTkErrOk != err) { + myUART.print("Write failed with error: "); + myUART.println(err); + return; + } + + myUART.println("Reading back data..."); + err = serialBus1.readRegister(testReg, 1, readData, 2, bytesRead); + if(ksfTkErrOk != err) { + myUART.print("Read failed with error: "); + myUART.println(err); + return; + } + + myUART.print("Bytes read: "); + myUART.println(bytesRead); + myUART.print("Data read: 0x"); + myUART.println(readData[1], HEX); + myUART.println("Test 4 complete. All done!"); +} + +void waitForButtonPress() +{ + buttonPressed = false; + while(!buttonPressed) { + int reading = digitalRead(USER_SW); + if (reading != lastButtonState) { + lastDebounceTime = millis(); + } + + if ((millis() - lastDebounceTime) > debounceDelay) { + if (reading == LOW) { + buttonPressed = true; + delay(100); + } + } + lastButtonState = reading; + } +} + +// Helper function to print UART configuration +void printUARTConfig(const sfTkIUART::UARTConfig_t& config) { + myUART.print(" Baud Rate: "); + myUART.println(config.baudRate); + myUART.print(" Data Bits: "); + myUART.println(dataBitsToValue(config.dataBits)); + myUART.print(" Parity: "); + myUART.println(parityToString(config.parity)); + myUART.print(" Stop Bits: "); + myUART.println(stopBitsToString(config.stopBits)); +} + +// Helper function to print byte array in hex +void printHexArray(const uint8_t* data, size_t len) { + for (size_t i = 0; i < len; i++) { + if (data[i] < 0x10) myUART.print('0'); + myUART.print(data[i], HEX); + if (i < len - 1) myUART.print(' '); + } +}