Skip to content

Commit 12711fb

Browse files
committed
Support Serial output to STDERR (replaces #92)
1 parent 862ae8a commit 12711fb

File tree

6 files changed

+109
-3
lines changed

6 files changed

+109
-3
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# Changelog
22

33
* Unreleased
4+
* Support sending output of `Serial` port to `STDERR`
5+
(replaces [PR#92](https://github.com/bxparks/EpoxyDuino/pull/92).
46
* 1.6.0 (2024-07-25)
57
* Add `strncat_P()` to `pgmspace.h`.
68
* Add `ESP.restart()` and `ESP.getChipId()`. See

README.md

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ The disadvantages are:
106106
* [Serial Port Emulation](#SerialPortEmulation)
107107
* [Unix Line Mode](#UnixLineMode)
108108
* [Enable Terminal Echo](#EnableTerminalEcho)
109+
* [Output to STDERR](#OutputToStderr)
110+
* [Multiple Serial Ports](#MultipleSerialPorts)
109111
* [Libraries and Mocks](#LibrariesAndMocks)
110112
* [Inherently Compatible Libraries](#InherentlyCompatibleLibraries)
111113
* [Emulation Libraries](#EmulationLibraries)
@@ -1062,7 +1064,7 @@ test(myTest) {
10621064
```
10631065
10641066
<a name="EnableTerminalEcho"></a>
1065-
#### Enable Terminal Echno
1067+
#### Enable Terminal Echo
10661068
10671069
By default, the `stdin` of the terminal is set to `NOECHO` mode for consistency
10681070
with the actual serial port of an Arduino microcontroller. However when running
@@ -1084,6 +1086,45 @@ void setup() {
10841086
}
10851087
```
10861088

1089+
<a name="OutputToStderr"></a>
1090+
#### Output to STDERR
1091+
1092+
By default, the `Serial` instance sends its output to the STDOUT (file
1093+
descriptor `STDOUT_FILENO`, i.e. 1). We can override that to send the output to
1094+
STDERR (file descriptor `STDERR_FILENO`) using the
1095+
`StdioSerial::setOutputFileDescriptor(int fd)` method:
1096+
1097+
```C++
1098+
Serial.println("This goes to STDOUT");
1099+
Serial.setOutputFileDescriptor(STDERR_FILENO);
1100+
Serial.println("This goes to STDERR");
1101+
```
1102+
1103+
<a name="MultipleSerialPorts"></a>
1104+
#### Multiple Serial Ports
1105+
1106+
By default, a default instance of `StdioSerial` class is created named `Serial`,
1107+
which matches the behavior of the Arduino programming framework on actual
1108+
microcontrollers which a single serial port is provided by default.
1109+
1110+
Some microcontrollers provide multiple serial ports, often called `Serial1` or
1111+
`Serial2`. We can emulate that in EpoxyDuino by creating additional instances of
1112+
`StdioSerial`. The following creates a `Serial1` instances bound to STDOUT, and
1113+
another `Serial2` bound to STDERR:
1114+
1115+
```C++
1116+
#ifdef EPOXY_DUINO
1117+
StdioSerial Serial1(STDOUT_FILENO);
1118+
StdioSerial Serial2(STDERR_FILENO);
1119+
...
1120+
void someFunction() {
1121+
Serial1.println("Print to STDOUT");
1122+
Serial2.println("Print to STDERR");
1123+
...
1124+
}
1125+
#endif
1126+
```
1127+
10871128
<a name="LibrariesAndMocks"></a>
10881129
## Libraries and Mocks
10891130

cores/epoxy/StdioSerial.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
*/
55

66
#include <stdio.h>
7-
#include <unistd.h>
7+
#include <unistd.h> // STDIN_FILENO
88
#include "StdioSerial.h"
99

1010
size_t StdioSerial::write(uint8_t c) {
11-
ssize_t status = ::write(STDOUT_FILENO, &c, 1);
11+
ssize_t status = ::write(outputFd, &c, 1);
1212
return (status <= 0) ? 0 : 1;
1313
}
1414

cores/epoxy/StdioSerial.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#ifndef EPOXY_DUINO_STDIO_SERIAL_H
77
#define EPOXY_DUINO_STDIO_SERIAL_H
88

9+
#include <unistd.h> // STDOUT_FILENO
910
#include "Print.h"
1011
#include "Stream.h"
1112

@@ -15,6 +16,20 @@
1516
*/
1617
class StdioSerial: public Stream {
1718
public:
19+
/**
20+
* Construct an instance. The default output file descriptor is
21+
* STDOUT_FILENO, but STDERR_FILENO is another option. POSIX.1-2017 defines
22+
* these values in <unistd.h> (see
23+
* https://unix.stackexchange.com/questions/437602).
24+
*/
25+
StdioSerial(int fd = STDOUT_FILENO) : outputFd(fd) { }
26+
27+
/**
28+
* Override the output file descriptor. Two common values are expected to be
29+
* STDOUT_FILENO and STDERR_FILENO.
30+
*/
31+
void setOutputFileDescriptor(int fd) { outputFd = fd; }
32+
1833
void begin(unsigned long /*baud*/) { bufch = -1; }
1934

2035
size_t write(uint8_t c) override;
@@ -37,6 +52,7 @@ class StdioSerial: public Stream {
3752
int peek() override;
3853

3954
private:
55+
int outputFd;
4056
int bufch;
4157
};
4258

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# See https://github.com/bxparks/EpoxyDuino for documentation about this
2+
# Makefile to compile and run Arduino programs natively on Linux or MacOS.
3+
4+
APP_NAME := StdioSerialMultiple
5+
ARDUINO_LIBS :=
6+
include ../../../EpoxyDuino/EpoxyDuino.mk
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Test emulation of multiple Serial ports.
3+
*
4+
* $ ./StdioSerialMultiple.out
5+
* Printing to STDOUT
6+
* Printing to STDERR
7+
*
8+
* $ ./StdioSerialMultiple.out > /dev/null
9+
* Printing to STDERR
10+
*/
11+
12+
#include <Arduino.h>
13+
14+
//-----------------------------------------------------------------------------
15+
16+
#if defined(EPOXY_DUINO)
17+
StdioSerial Serial1(STDOUT_FILENO);
18+
StdioSerial Serial2(STDERR_FILENO);
19+
#endif
20+
21+
22+
void setup(void) {
23+
#if ! defined(EPOXY_DUINO)
24+
delay(1000);
25+
#endif
26+
27+
Serial1.begin(115200);
28+
Serial2.begin(115200);
29+
while (!Serial1);
30+
while (!Serial2);
31+
32+
#if defined(EPOXY_DUINO)
33+
Serial1.setLineModeUnix();
34+
Serial2.setLineModeUnix();
35+
#endif
36+
37+
Serial1.println(F("Printing to STDOUT"));
38+
Serial2.println(F("Printing to STDERR"));
39+
}
40+
41+
void loop(void) {}

0 commit comments

Comments
 (0)