Skip to content

Commit 5110146

Browse files
committed
Add RS485 file handle for mbed
1 parent df09f30 commit 5110146

File tree

3 files changed

+186
-0
lines changed

3 files changed

+186
-0
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/**
2+
* This example demonstrates how to redirect the Arduino Mbed crash log output to the RS485 bus.
3+
* This is useful for boards that support RS485 but lack easy access to other serial output options (like the Arduino Opta)
4+
*
5+
* This example forces a crash in the setup() function to showcase the functionality.
6+
* Initial author: Sebastian Romero @sebromero
7+
*/
8+
9+
#include "RS485FileHandle.h"
10+
11+
REDIRECT_STDOUT_TO(&RS485Console) // Redirect mbed crash log output to RS485
12+
13+
void setup() {
14+
// Force a crash to demonstrate the crash log over RS485
15+
volatile int* p = nullptr;
16+
*p = 42; // Dereference null pointer to cause a crash
17+
}
18+
19+
void loop() {
20+
// Nothing to do here
21+
}

src/RS485FileHandle.cpp

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#include "RS485FileHandle.h"
2+
#include <ArduinoRS485.h>
3+
#include <errno.h>
4+
5+
RS485FileHandle::RS485FileHandle() {}
6+
7+
void RS485FileHandle::begin() {
8+
RS485FileHandle::begin(115200); // Default baud rate
9+
}
10+
11+
void RS485FileHandle::begin(int baudRate) {
12+
// Ensure idempotent initialization
13+
if (!_isInitialized) {
14+
const auto bitduration { 1.f / baudRate };
15+
const auto wordlen { 9.6f }; // OR 10.0f depending on the channel configuration
16+
const auto preDelayBR { bitduration * wordlen * 3.5f * 1e6 };
17+
const auto postDelayBR { bitduration * wordlen * 3.5f * 1e6 };
18+
19+
RS485.begin(baudRate);
20+
RS485.setDelays(preDelayBR, postDelayBR);
21+
RS485.noReceive();
22+
_isInitialized = true;
23+
}
24+
}
25+
26+
ssize_t RS485FileHandle::write(const void* buffer, size_t size) {
27+
begin(); // Ensure RS485 is initialized
28+
29+
// Avoid repeatedly starting transmission if already in progress
30+
// as mbed calls write multiple times for a single output operation.
31+
// This otherwise results in mangled output on RS485.
32+
33+
if (!_inTransmission) {
34+
RS485.beginTransmission();
35+
_inTransmission = true;
36+
}
37+
38+
size_t writtenBytes = RS485.write(static_cast<const uint8_t*>(buffer), size);
39+
return writtenBytes; // Return the number of bytes written
40+
}
41+
42+
ssize_t RS485FileHandle::read(void* buffer, size_t size) {
43+
// Not implemented for RS485 output redirection
44+
return -ENOSYS;
45+
}
46+
47+
off_t RS485FileHandle::seek(off_t offset, int whence) {
48+
return -ESPIPE; // Not seekable
49+
}
50+
51+
int RS485FileHandle::close() {
52+
if (_inTransmission) {
53+
RS485.endTransmission();
54+
_inTransmission = false;
55+
}
56+
RS485.end();
57+
_isInitialized = false;
58+
return 0;
59+
}
60+
61+
short RS485FileHandle::poll(short events) const {
62+
return POLLOUT; // Ready to write
63+
}
64+
65+
int RS485FileHandle::sync() {
66+
if (_inTransmission) {
67+
RS485.endTransmission();
68+
_inTransmission = false;
69+
}
70+
return 0;
71+
}
72+
73+
int RS485FileHandle::isatty() const {
74+
return true;
75+
}
76+
77+
// Global instance for stdout redirection
78+
RS485FileHandle RS485Console;

src/RS485FileHandle.h

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#ifndef RS485_FILE_HANDLE_H
2+
#define RS485_FILE_HANDLE_H
3+
4+
#include "mbed.h"
5+
6+
/**
7+
* @file RS485FileHandle.h
8+
* @brief Declares an mbed FileHandle that redirects stdio to the RS485 bus.
9+
* This is used to capture crash logs on Mbed-based boards that support RS485 but don't have other serial output options.
10+
*/
11+
12+
/**
13+
* @brief FileHandle implementation that forwards standard I/O to ArduinoRS485.
14+
* The baud rate is fixed at 115200 bps. The internal logic is tuned to ensure correct RS485 transmission behavior in
15+
* the context of crash log output.
16+
*/
17+
class RS485FileHandle : public mbed::FileHandle {
18+
public:
19+
/**
20+
* @brief Constructs a FileHandle that targets the global RS485 interface.
21+
*/
22+
RS485FileHandle();
23+
24+
/**
25+
* @brief Writes a buffer to the RS485 bus.
26+
* When the first write occurs, the RS485 interface is initialised automatically.
27+
*
28+
* @param buffer Data to send.
29+
* @param size Number of bytes to write.
30+
* @return Bytes written on success, or a negative error code.
31+
*/
32+
virtual ssize_t write(const void* buffer, size_t size) override;
33+
/**
34+
* @brief Reads data from the RS485 bus.
35+
*
36+
* @param buffer Destination buffer.
37+
* @param size Maximum number of bytes to read.
38+
* @return Bytes read on success, or a negative error code.
39+
*/
40+
virtual ssize_t read(void* buffer, size_t size) override;
41+
/**
42+
* @brief Changes the current position within the stream.
43+
*
44+
* @param offset Byte offset relative to whence.
45+
* @param whence Reference position, defaults to SEEK_SET.
46+
* @return New position on success, or -1 on error.
47+
*/
48+
virtual off_t seek(off_t offset, int whence = SEEK_SET) override;
49+
/**
50+
* @brief Closes the FileHandle and ends any active transmission.
51+
*
52+
* @return 0 on success, or a negative error code.
53+
*/
54+
virtual int close() override;
55+
/**
56+
* @brief Reports readiness for the requested events.
57+
*
58+
* @param events Bitmask of poll events (POLLIN, POLLOUT, ...).
59+
* @return Bitmask indicating the ready events.
60+
*/
61+
virtual short poll(short events) const override;
62+
/**
63+
* @brief Flushes pending data to the RS485 bus.
64+
*
65+
* @return 0 on success, or a negative error code.
66+
*/
67+
virtual int sync() override;
68+
/**
69+
* @brief Indicates whether the handle represents a TTY-like device.
70+
*
71+
* @return Non-zero when treated as a TTY.
72+
*/
73+
virtual int isatty() const;
74+
75+
private:
76+
bool _isInitialized = false;
77+
bool _inTransmission = false;
78+
void begin();
79+
void begin(int baudRate);
80+
};
81+
82+
/**
83+
* @brief Global RS485 FileHandle instance used to redirect stdio.
84+
*/
85+
extern RS485FileHandle RS485Console;
86+
87+
#endif // RS485_FILE_HANDLE_H

0 commit comments

Comments
 (0)