Skip to content

Commit

Permalink
Merge pull request #46 from bxparks/develop
Browse files Browse the repository at this point in the history
merge v1.2.0 into master
  • Loading branch information
bxparks authored Dec 30, 2021
2 parents 9057371 + 3e22027 commit 2606815
Show file tree
Hide file tree
Showing 12 changed files with 257 additions and 53 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
# Changelog

* Unreleased
* 1.2.0 (2021-12-29)
* Simplify `StdioSerial` class, see
[Issue#43](https://github.com/bxparks/EpoxyDuino/issues/43).
* Replace input ring buffer with a buffer of one character.
* Wire `StdioSerial::write(uint8_t)` directly to Posix `write()`,
by-passing the `<stdio.h>` buffer. `flush()` is no longer necessary.
* Thanks to @felias-fogg.
* **Revert Breaking Change Made in v1.1.0** Revert 432e304, so that
`Print::writeln()` writes `\r\n` again by default.
* Fixes [Issue#45](https://github.com/bxparks/EpoxyDuino/issues/45).
* Add `Print::setLineModeNormal()` and `Print::setLineModeUnix()`
methods to control the line termination behavior.
* See [README.md#UnixLineMode](README.md#UnixLineMode) for usage info.
* 1.1.0 (2021-12-09)
* Add optional `DEPS` variable containing header files that the `*.ino`
depends on.
Expand Down
77 changes: 76 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ The disadvantages are:
environments (e.g. 16-bit `int` versus 32-bit `int`, or 32-bit `long` versus
64-bit `long`).

**Version**: 1.1.0 (2021-12-09)
**Version**: 1.2.0 (2021-12-09)

**Changelog**: See [CHANGELOG.md](CHANGELOG.md)

Expand All @@ -94,6 +94,7 @@ The disadvantages are:
* [Supported Arduino Features](#SupportedArduinoFeatures)
* [Arduino Functions](#ArduinoFunctions)
* [Serial Port Emulation](#SerialPortEmulation)
* [Unix Line Mode](#UnixLineMode)
* [Libraries and Mocks](#LibrariesAndMocks)
* [Inherently Compatible Libraries](#InherentlyCompatibleLibraries)
* [Emulation Libraries](#EmulationLibraries)
Expand Down Expand Up @@ -729,6 +730,80 @@ the program. But this convenience means that the Arduino program running under
`Serial.read()` function. The advantages of having normal Unix signals seemed
worth the trade-off.

<a name="UnixLineMode"></a>
### Unix Line Mode

(Added in v1.2.0)

The `Print` class in the Arduino API implements the `Print::println()` function
by printing the DOS line terminator characters `\r\n`. This decision make sense
when the serial port of the microcontroller is connected to a serial terminal,
which requires a `\r\n` at the end of each line to render the text properly.

But when the Arduino application is executed on Linux machine, and the output is
redirected into a file, the `\r\n` is not consistent with the Unix convention
of using only a single `\n` to terminate each line. This causes the file to be
interpreted as a DOS-formatted file. Usually the DOS formatted file can be
processed without problems by other Linux programs and scripts, but sometimes
the extra `\r\n` causes problems, especially when mixed with a `Serial.printf()`
function using a single `\n`.

EpoxyDuino provides a mechanism to configure the line termination convention for
a given application by providing 2 additional methods to its `Print` class:

```C++
class Print {
public:
// Use DOS line termination. This is the default.
void setLineModeNormal();

// Use Unix line termination.
void setLineModeUnix();

...
};
```
When an Arduino application is executed on a Linux machine using EpoxyDuino,
you can configure the `Serial` object in the `*.ino` file to use the Unix
convention like this:
```C++
void setup() {
#if ! defined(EPOXY_DUINO)
delay(1000); // wait to prevent garbage on Serial
#endif
Serial.begin(115200);
while (!Serial); // Leonardo/Micro
#if defined(EPOXY_DUINO)
Serial.setLineModeUnix();
#endif
}
```

Why isn't `setLineModeUnix()` simply made to be the default on EpoxyDuino?
Because people write [AUnit](https://github.com/bxparks/AUnit) unit tests which
they expect will pass on both the microcontroller and on EpoxyDuino:

```C++
#include <Arduino.h>
#include <AUnit.h>
#include <AceCommon.h> // PrintStr<N>
...

static void sayHello(Print& printer) {
printer.println("hello");
}

test(myTest) {
PrintStr<200> observed;
sayHello(observed);
assertEqual(observed.cstr(), "hello\r\n");
}
```
<a name="LibrariesAndMocks"></a>
## Libraries and Mocks
Expand Down
7 changes: 1 addition & 6 deletions cores/epoxy/Arduino.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
*/

#include <inttypes.h>
#include <unistd.h> // read(), STDIN_FILENO, usleep()
#include <unistd.h> // usleep()
#include <time.h> // clock_gettime()
#include "Arduino.h"

Expand All @@ -22,11 +22,6 @@
// -----------------------------------------------------------------------

void yield() {
char c = '\0';
if (read(STDIN_FILENO, &c, 1) == 1) {
Serial.insertChar(c);
}

usleep(1000); // prevents program from consuming 100% CPU
}

Expand Down
4 changes: 2 additions & 2 deletions cores/epoxy/Arduino.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
#define EPOXY_DUINO_EPOXY_ARDUINO_H

// xx.yy.zz => xxyyzz (without leading 0)
#define EPOXY_DUINO_VERSION 10100
#define EPOXY_DUINO_VERSION_STRING "1.1.0"
#define EPOXY_DUINO_VERSION 10200
#define EPOXY_DUINO_VERSION_STRING "1.2.0"

#include <algorithm> // min(), max()
#include <cmath> // abs()
Expand Down
6 changes: 5 additions & 1 deletion cores/epoxy/Print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,11 @@ size_t Print::print(const Printable& x)

size_t Print::println(void)
{
return write('\n');
if (isLineModeUnix) {
return write('\n');
} else {
return write("\r\n");
}
}

size_t Print::println(const String &s)
Expand Down
16 changes: 16 additions & 0 deletions cores/epoxy/Print.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,27 @@ class Print
{
private:
int write_error;
bool isLineModeUnix = false;

size_t printNumber(unsigned long, uint8_t);
size_t printFloat(double, uint8_t);

protected:
void setWriteError(int err = 1) { write_error = err; }

public:
/**
* Set the line termination mode to Normal (i.e. \\r\\n). This is the
* default. This function is available only on EpoxyDuino.
*/
void setLineModeNormal() { isLineModeUnix = false; }

/**
* Set the line termination mode to Unix (i.e. \\n). This function is
* available only on EpoxyDuino.
*/
void setLineModeUnix() { isLineModeUnix = true; }

Print() : write_error(0) {}

int getWriteError() { return write_error; }
Expand Down
26 changes: 22 additions & 4 deletions cores/epoxy/StdioSerial.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,33 @@
*/

#include <stdio.h>
#include <unistd.h>
#include "StdioSerial.h"

size_t StdioSerial::write(uint8_t c) {
int result = putchar(c);
return (result == EOF) ? 0 : 1;
ssize_t status = ::write(STDOUT_FILENO, &c, 1);
return (status <= 0) ? 0 : 1;
}

void StdioSerial::flush() {
fflush(stdout);
int StdioSerial::read() {
int ch = peek();
bufch = -1;
return ch;
}

int StdioSerial::peek() {
if (bufch == -1) {
// 'c' must be unsigned to avoid ambiguity with -1 in-band error condition
unsigned char c;
ssize_t status = ::read(STDIN_FILENO, &c, 1);
bufch = (status <= 0) ? -1 : c;
}
return bufch;
}

int StdioSerial::available() {
int ch = peek();
return (int) (ch != -1);
}

StdioSerial Serial;
44 changes: 7 additions & 37 deletions cores/epoxy/StdioSerial.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,50 +15,20 @@
*/
class StdioSerial: public Stream {
public:
void begin(unsigned long /*baud*/) { }
void begin(unsigned long /*baud*/) { bufch = -1; }

size_t write(uint8_t c) override;

void flush() override;

operator bool() { return true; }

int available() override { return mHead != mTail; }

int read() override {
if (mHead == mTail) {
return -1;
} else {
char c = mBuffer[mHead];
mHead = (mHead + 1) % kBufSize;
return c;
}
}

int peek() override {
return (mHead != mTail) ? mBuffer[mHead] : -1;
}

/** Insert a character into the ring buffer. */
void insertChar(char c) {
int newTail = (mTail + 1) % kBufSize;
if (newTail == mHead) {
// Buffer full, drop the character. (Strictly speaking, there's one
// remaining slot in the buffer, but we can't use it because we need to
// distinguish between buffer-empty and buffer-full).
return;
}
mBuffer[mTail] = c;
mTail = newTail;
}
int available() override;

int read() override;

int peek() override;

private:
// Ring buffer size (should be a power of 2 for efficiency).
static const int kBufSize = 128;

char mBuffer[kBufSize];
int mHead = 0;
int mTail = 0;
int bufch;
};

extern StdioSerial Serial;
Expand Down
2 changes: 1 addition & 1 deletion cores/epoxy/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ static void enableRawMode() {
raw.c_cflag |= (CS8);

// Enable ISIG to allow Ctrl-C to kill the program.
raw.c_lflag &= ~(/*ECHO | ISIG |*/ ICANON | IEXTEN);
raw.c_lflag &= ~(ECHO | /*ISIG |*/ ICANON | IEXTEN);
raw.c_cc[VMIN] = 0;
raw.c_cc[VTIME] = 0;
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1) {
Expand Down
6 changes: 6 additions & 0 deletions examples/echo/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# See https://github.com/bxparks/EpoxyDuino for documentation about this
# Makefile to compile and run Arduino programs natively on Linux or MacOS.

APP_NAME := echo
ARDUINO_LIBS :=
include ../../../EpoxyDuino/EpoxyDuino.mk
Loading

0 comments on commit 2606815

Please sign in to comment.