From 5110146a1f503b3fb7f677259128d75a8e05b65e Mon Sep 17 00:00:00 2001 From: Sebastian Romero Date: Wed, 5 Nov 2025 17:04:12 +0100 Subject: [PATCH 1/2] Add RS485 file handle for mbed --- examples/RS485CrashLog/RS485CrashLog.ino | 21 ++++++ src/RS485FileHandle.cpp | 78 +++++++++++++++++++++ src/RS485FileHandle.h | 87 ++++++++++++++++++++++++ 3 files changed, 186 insertions(+) create mode 100644 examples/RS485CrashLog/RS485CrashLog.ino create mode 100644 src/RS485FileHandle.cpp create mode 100644 src/RS485FileHandle.h diff --git a/examples/RS485CrashLog/RS485CrashLog.ino b/examples/RS485CrashLog/RS485CrashLog.ino new file mode 100644 index 0000000..4044bff --- /dev/null +++ b/examples/RS485CrashLog/RS485CrashLog.ino @@ -0,0 +1,21 @@ +/** + * This example demonstrates how to redirect the Arduino Mbed crash log output to the RS485 bus. + * This is useful for boards that support RS485 but lack easy access to other serial output options (like the Arduino Opta) + * + * This example forces a crash in the setup() function to showcase the functionality. + * Initial author: Sebastian Romero @sebromero + */ + +#include "RS485FileHandle.h" + +REDIRECT_STDOUT_TO(&RS485Console) // Redirect mbed crash log output to RS485 + +void setup() { + // Force a crash to demonstrate the crash log over RS485 + volatile int* p = nullptr; + *p = 42; // Dereference null pointer to cause a crash +} + +void loop() { + // Nothing to do here +} \ No newline at end of file diff --git a/src/RS485FileHandle.cpp b/src/RS485FileHandle.cpp new file mode 100644 index 0000000..e31c0b3 --- /dev/null +++ b/src/RS485FileHandle.cpp @@ -0,0 +1,78 @@ +#include "RS485FileHandle.h" +#include +#include + +RS485FileHandle::RS485FileHandle() {} + +void RS485FileHandle::begin() { + RS485FileHandle::begin(115200); // Default baud rate +} + +void RS485FileHandle::begin(int baudRate) { + // Ensure idempotent initialization + if (!_isInitialized) { + const auto bitduration { 1.f / baudRate }; + const auto wordlen { 9.6f }; // OR 10.0f depending on the channel configuration + const auto preDelayBR { bitduration * wordlen * 3.5f * 1e6 }; + const auto postDelayBR { bitduration * wordlen * 3.5f * 1e6 }; + + RS485.begin(baudRate); + RS485.setDelays(preDelayBR, postDelayBR); + RS485.noReceive(); + _isInitialized = true; + } +} + +ssize_t RS485FileHandle::write(const void* buffer, size_t size) { + begin(); // Ensure RS485 is initialized + + // Avoid repeatedly starting transmission if already in progress + // as mbed calls write multiple times for a single output operation. + // This otherwise results in mangled output on RS485. + + if (!_inTransmission) { + RS485.beginTransmission(); + _inTransmission = true; + } + + size_t writtenBytes = RS485.write(static_cast(buffer), size); + return writtenBytes; // Return the number of bytes written +} + +ssize_t RS485FileHandle::read(void* buffer, size_t size) { + // Not implemented for RS485 output redirection + return -ENOSYS; +} + +off_t RS485FileHandle::seek(off_t offset, int whence) { + return -ESPIPE; // Not seekable +} + +int RS485FileHandle::close() { + if (_inTransmission) { + RS485.endTransmission(); + _inTransmission = false; + } + RS485.end(); + _isInitialized = false; + return 0; +} + +short RS485FileHandle::poll(short events) const { + return POLLOUT; // Ready to write +} + +int RS485FileHandle::sync() { + if (_inTransmission) { + RS485.endTransmission(); + _inTransmission = false; + } + return 0; +} + +int RS485FileHandle::isatty() const { + return true; +} + +// Global instance for stdout redirection +RS485FileHandle RS485Console; \ No newline at end of file diff --git a/src/RS485FileHandle.h b/src/RS485FileHandle.h new file mode 100644 index 0000000..871767e --- /dev/null +++ b/src/RS485FileHandle.h @@ -0,0 +1,87 @@ +#ifndef RS485_FILE_HANDLE_H +#define RS485_FILE_HANDLE_H + +#include "mbed.h" + +/** + * @file RS485FileHandle.h + * @brief Declares an mbed FileHandle that redirects stdio to the RS485 bus. + * This is used to capture crash logs on Mbed-based boards that support RS485 but don't have other serial output options. + */ + +/** + * @brief FileHandle implementation that forwards standard I/O to ArduinoRS485. + * The baud rate is fixed at 115200 bps. The internal logic is tuned to ensure correct RS485 transmission behavior in + * the context of crash log output. + */ +class RS485FileHandle : public mbed::FileHandle { +public: + /** + * @brief Constructs a FileHandle that targets the global RS485 interface. + */ + RS485FileHandle(); + + /** + * @brief Writes a buffer to the RS485 bus. + * When the first write occurs, the RS485 interface is initialised automatically. + * + * @param buffer Data to send. + * @param size Number of bytes to write. + * @return Bytes written on success, or a negative error code. + */ + virtual ssize_t write(const void* buffer, size_t size) override; + /** + * @brief Reads data from the RS485 bus. + * + * @param buffer Destination buffer. + * @param size Maximum number of bytes to read. + * @return Bytes read on success, or a negative error code. + */ + virtual ssize_t read(void* buffer, size_t size) override; + /** + * @brief Changes the current position within the stream. + * + * @param offset Byte offset relative to whence. + * @param whence Reference position, defaults to SEEK_SET. + * @return New position on success, or -1 on error. + */ + virtual off_t seek(off_t offset, int whence = SEEK_SET) override; + /** + * @brief Closes the FileHandle and ends any active transmission. + * + * @return 0 on success, or a negative error code. + */ + virtual int close() override; + /** + * @brief Reports readiness for the requested events. + * + * @param events Bitmask of poll events (POLLIN, POLLOUT, ...). + * @return Bitmask indicating the ready events. + */ + virtual short poll(short events) const override; + /** + * @brief Flushes pending data to the RS485 bus. + * + * @return 0 on success, or a negative error code. + */ + virtual int sync() override; + /** + * @brief Indicates whether the handle represents a TTY-like device. + * + * @return Non-zero when treated as a TTY. + */ + virtual int isatty() const; + +private: + bool _isInitialized = false; + bool _inTransmission = false; + void begin(); + void begin(int baudRate); +}; + +/** + * @brief Global RS485 FileHandle instance used to redirect stdio. + */ +extern RS485FileHandle RS485Console; + +#endif // RS485_FILE_HANDLE_H \ No newline at end of file From 5ecab3ff27c440bce4b5cd0718570979a05890c3 Mon Sep 17 00:00:00 2001 From: Sebastian Romero Date: Wed, 5 Nov 2025 17:09:10 +0100 Subject: [PATCH 2/2] Allow compilation on non-mbed targets --- examples/RS485CrashLog/RS485CrashLog.ino | 3 ++- src/RS485FileHandle.cpp | 7 ++++++- src/RS485FileHandle.h | 4 ++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/examples/RS485CrashLog/RS485CrashLog.ino b/examples/RS485CrashLog/RS485CrashLog.ino index 4044bff..9f32bb9 100644 --- a/examples/RS485CrashLog/RS485CrashLog.ino +++ b/examples/RS485CrashLog/RS485CrashLog.ino @@ -6,9 +6,10 @@ * Initial author: Sebastian Romero @sebromero */ +#ifdef ARDUINO_ARCH_MBED #include "RS485FileHandle.h" - REDIRECT_STDOUT_TO(&RS485Console) // Redirect mbed crash log output to RS485 +#endif void setup() { // Force a crash to demonstrate the crash log over RS485 diff --git a/src/RS485FileHandle.cpp b/src/RS485FileHandle.cpp index e31c0b3..547d65c 100644 --- a/src/RS485FileHandle.cpp +++ b/src/RS485FileHandle.cpp @@ -1,4 +1,7 @@ #include "RS485FileHandle.h" + +#ifdef ARDUINO_ARCH_MBED + #include #include @@ -75,4 +78,6 @@ int RS485FileHandle::isatty() const { } // Global instance for stdout redirection -RS485FileHandle RS485Console; \ No newline at end of file +RS485FileHandle RS485Console; + +#endif // ARDUINO_ARCH_MBED \ No newline at end of file diff --git a/src/RS485FileHandle.h b/src/RS485FileHandle.h index 871767e..67832d5 100644 --- a/src/RS485FileHandle.h +++ b/src/RS485FileHandle.h @@ -1,6 +1,8 @@ #ifndef RS485_FILE_HANDLE_H #define RS485_FILE_HANDLE_H +#ifdef ARDUINO_ARCH_MBED + #include "mbed.h" /** @@ -84,4 +86,6 @@ class RS485FileHandle : public mbed::FileHandle { */ extern RS485FileHandle RS485Console; +#endif // ARDUINO_ARCH_MBED + #endif // RS485_FILE_HANDLE_H \ No newline at end of file