Giter VIP home page Giter VIP logo

arduinostreamutils's Introduction

StreamUtils: Power-ups for Arduino Streams

Continuous Integration Coverage Status Arduino Library Manager PlatformIO Registry

The stream is an essential abstraction in Arduino; we find it in many places:

This library provides some helper classes and functions for dealing with streams.

For example, with this library, you can:

  • speed up your program by buffering the data it reads from a file
  • reduce the number of packets sent over WiFi by buffering the data you send
  • improve the reliability of a serial connection by adding error correction codes
  • debug your program more easily by logging what it sends to a Web service
  • send large data with the Wire library
  • use a String, EEPROM, or PROGMEM with a stream interface
  • decode HTTP chunks

Read on to see how StreamUtils can help you!

How to add buffering to a Stream?

Buffering read operations

Sometimes, you can significantly improve performance by reading many bytes at once. For example, according to SPIFFS's wiki, reading files in chunks of 64 bytes is much faster than reading them one byte at a time.

ReadBufferingStream

To buffer the input, decorate the original Stream with ReadBufferingStream. For example, suppose your program reads a JSON document from SPIFFS like this:

File file = SPIFFS.open("example.json", "r");
deserializeJson(doc, file);

Then you only need to insert one line to greatly improve the reading speed:

File file = SPIFFS.open("example.json", "r");
ReadBufferingStream bufferedFile{file, 64};  // <- HERE
deserializeJson(doc, bufferedFile);

Unfortunately, this optimization is only possible if:

  1. Stream.readBytes() is declared virtual in your Arduino Code (as it's the case for ESP8266), and
  2. the derived class has an optimized implementation of readBytes() (as it's the case for SPIFFS' File).

When possible, prefer ReadBufferingClient to ReadBufferingStream because Client defines a read() method similar to readBytes(), except this one is virtual on all platforms.

If memory allocation fails, ReadBufferingStream behaves as if no buffer was used: it forwards all calls to the upstream Stream.

Adding a buffer only makes sense for unbuffered streams. For example, there is no benefit to adding a buffer to serial ports because they already include an internal buffer.

Buffering write operations

Similarly, you can improve performance significantly by writing many bytes at once. For example, writing to WiFiClient one byte at a time is very slow; it's much faster if you send large chunks.

WriteBufferingStream

To add a buffer, decorate the original Stream with WriteBufferingStream. For example, if your program sends a JSON document via WiFiClient like this:

serializeJson(doc, wifiClient);

Rewrite it like this:

WriteBufferingStream bufferedWifiClient{wifiClient, 64};
serializeJson(doc, bufferedWifiClient);
bufferedWifiClient.flush();

flush() sends the remaining data; if you forget to call it, the end of the message will be missing. The destructor of WriteBufferingStream calls flush(), so you can remove this line if you destroy the decorator immediately.

If memory allocation fails, WriteBufferingStream behaves as if no buffer was used: it forwards all calls to the upstream Stream.

Adding a buffer only makes sense for unbuffered streams. For example, there is no benefit to adding a buffer to serial ports because they already include an internal buffer.

How to add logging to a stream?

Logging write operations

When debugging a program that makes HTTP requests, you first want to check whether the request is correct. With this library, you can decorate the EthernetClient or the WiFiClient to log everything to the serial.

WriteLoggingStream

For example, if your program is:

client.println("GET / HTTP/1.1");
client.println("User-Agent: Arduino");
// ...

Then, create the decorator and update the calls to println():

WriteLoggingStream loggingClient(client, Serial);
loggingClient.println("GET / HTTP/1.1");
loggingClient.println("User-Agent: Arduino");
// ...

Everything you write to loggingClient is written to client and logged to Serial.

Logging read operations

Similarly, you often want to see what the HTTP server sent back. With this library, you can decorate the EthernetClient or the WiFiClient to log everything to the serial.

ReadLoggingStream

For example, if your program is:

char response[256];
client.readBytes(response, 256);

Then, create the decorator and update the calls to readBytes():

ReadLoggingStream loggingClient(client, Serial);
char response[256];
loggingClient.readBytes(response, 256);
// ...

loggingClient forwards all operations to client and logs read operations to Serial.

WARNING
If your program receives data from one serial port and logs to another, ensure the latter runs at a much higher baud rate. Logging must be at least ten times faster, or it will slow down the receiving port, which may drop incoming bytes.

Logging read and write operations

Of course, you could log read and write operations by combining ReadLoggingStream and WriteLoggingStream, but there is a simpler solution: LoggingStream.

LoggingStream

As usual, if your program is:

client.println("GET / HTTP/1.1");
client.println("User-Agent: Arduino");

char response[256];
client.readBytes(response, 256);

Then, decorate client and replace the calls:

LoggingStream loggingClient(client, Serial);

loggingClient.println("GET / HTTP/1.1");
loggingClient.println("User-Agent: Arduino");

char response[256];
loggingClient.readBytes(response, 256);

How to use error-correction codes (ECC)?

StreamUtils supports the Hamming(7, 4) error-correction code, which encodes 4 bits of data into 7 bits by adding three parity bits. These extra bits increase the amount of traffic but allow correcting any one-bit error within the 7 bits.

If you use this encoding on an 8-bit channel, it effectively doubles the amount of traffic. However, if you use an HardwareSerial instance (like Serial, Serial1...), you can slightly reduce the overhead by configuring the ports as a 7-bit channel, like so:

// Initialize serial port with 9600 bauds, 7 bits of data, no parity, and one stop bit
Serial1.begin(9600, SERIAL_7N1);

Adding parity bits

The class HammingEncodingStream<7, 4> decorates an existing Stream to include parity bits in every write operation.

HammingEncodingStream

You can use this class like so:

HammingEncodingStream<7, 4> eccSerial(Serial1);

eccSerial.println("Hello world!");

Like every Stream decorator in this library, HammingEncodingStream<7, 4> supports all Stream methods (like print(), println(), read(), readBytes(), and available()).

Correcting errors

The class HammingDecodingStream<7, 4> decorates an existing Stream to decode parity bits in every read operation.

HammingDecodingStream

You can use this class like so:

HammingDecodingStream<7, 4> eccSerial(Serial1);

char buffer[256];
size_t n = eccSerial.readBytes(buffer, n);

Like every Stream decorator in this library, HammingDecodingStream<7, 4> supports all Stream methods (like print(), println(), read(), readBytes(), and available()).

Encoding and decoding in both directions

The class HammingStream<7, 4> combines the features of HammingEncodingStream<7, 4> and HammingDecodingStream<7, 4>, which is very useful when you do two-way communication.

HammingStream

You can use this class like so:

HammingStream<7, 4> eccSerial(Serial1);

eccSerial.println("Hello world!");

char buffer[256];
size_t n = eccSerial.readBytes(buffer, n);

Like every Stream decorator in this library, HammingStream<7, 4> supports all Stream methods (like print(), println(), read(), readBytes(), and available()).

How to retry write operations?

Sometimes, a stream is limited to the capacity of its internal buffer. In that case, you must wait before sending more data. To solve this problem, StreamUtils provides the WriteWaitingStream decorator:

WriteWaitingStream

This function repeatedly waits and retries until it times out. You can customize the wait() function; by default, it's yield().

For example, if you want to send more than 32 bytes with the Wire library, you can do the following:

WriteWaitingStream wireStream(Wire, [](){
  Wire.endTransmission(false); // <- don't forget this argument
  Wire.beginTransmission(address);
});

Wire.beginTransmission(address); 
wireStream.print("This is a very very long message that I'm sending!");
Wire.endTransmission();

As you can see, we use the wait() function as a hook to flush the Wire transmission buffer. Notice that we pass false to endTransmission() so that it sends the data but doesn't actually stop the transmission.

How to use a String as a stream?

Writing to a String

Sometimes, you use a piece of code that expects a Print instance (like ReadLoggingStream), but you want the output in a String instead of a regular Stream. In that case, use the StringPrint class. It wraps a String within a Print implementation.

StringPrint

Here is how you can use it:

StringPrint stream;

stream.print("Temperature = ");
stream.print(22.3);
stream.print(" °C");

String result = stream.str();

At the end of this snippet, the string result contains:

Temperature = 22.30 °C

Reading from a String

Similarly, there are cases where you have a String, but you need to pass a Stream to some other piece of code. In that case, use StringStream; it's similar to StrintPrint, except you can also read from it.

StringStream

How to use EEPROM as a stream?

SteamUtils also allows using EEPROM as a stream. Create an instance of EepromStream and specify the start address and the size of the region you want to expose.

EepromStream

For example, it allows you to save a JSON document in EEPROM:

EepromStream eepromStream(0, 128);
serializeJson(doc, eepromStream);
eepromStream.flush();  // <- calls EEPROM.commit() on ESP (optional)

In the same way, you can read a JSON document from EEPROM:

EepromStream eepromStream(0, 128);
deserializeJson(doc, eepromStream);

How to use PROGMEM as a stream?

SteamUtils also allows reading PROGMEM buffers with a Stream interface.

ProgmemStream

Create an instance of ProgmemStream and pass the pointer to the PROGMEM buffer.

const char buffer[] PROGMEM = "This string is in program memory"
ProgmemStream stream{buffer};
Serial.println(stream.readString());

ProgmemStream's constructor also supports const __FlashStringHelper* (the type returned by the F() macro) and an optional second argument to specify the size of the buffer.

How to decode HTTP chunks?

HTTP servers can send their response in multiple parts using Chunked Transfer Encoding. Clients using HTTP 1.1 must support this encoding as it's not optional and is dictated by the server.

ChunkDecodingStream and ChunkDecodingClient are decorators that decode the chunks and make the response available as a regular stream.

ChunkDecodingStream

Here is an example using HTTPClient:

// Initialize HTTPClient
HTTPClient http;
http.begin(client, url);

// Tell HTTPClient to collect the Transfer-Encoding header
// (by default HTTPClient discards the response headers)
const char *keys[] = {"Transfer-Encoding"};
http.collectHeaders(keys, 1);

// Send the request
int status = http.GET();
if (status != 200) return;

// Create the raw and decoded stream
Stream& rawStream = http.getStream();
ChunkDecodingStream decodedStream(http.getStream());

// Choose the stream based on the Transfer-Encoding header
Stream& response = http.header("Transfer-Encoding") == "chunked" ? decodedStream : rawStream;

// Read the response
JsonDocument doc;
deserializeJson(doc, response);

// Close the connection
http.end();

Note that HTTPClient already performs chunk decoding if you use getString(), but you might want to use getStream() to avoid buffering the entire response in memory.

Also, you can avoid chunked transfer encoding by downgrading the HTTP version to 1.0. HTTPClient allows you to do that by calling useHTTP10(true) before sending the request.

Summary

Some of the decorators are also available for the Print and Client classes. See the equivalence table below.

Purpose Client Stream Print
Log write operations WriteLoggingClient WriteLoggingStream LoggingPrint
Log read operations ReadLoggingClient ReadLoggingStream
Log read and write op. LoggingClient LoggingStream
Buffer write operations WriteBufferingClient WriteBufferingStream BufferingPrint
Buffer read operations ReadBufferingClient ReadBufferingStream
Repeat write operations WriteWaitingClient WriteWaitingStream WaitingPrint
Use String as a stream StringStream StringPrint
Use EEPROM as a stream EepromStream
Use PROGMEM as a stream ProgmemStream
Error correction (decode only) HammingDecodingClient HammingDecodingStream
Error correction (encode only) HammingEncodingClient HammingEncodingStream HammingPrint
Error correction (encode & decode) HammingClient HammingStream
Decode HTTP chunks ChunkDecodingClient ChunkDecodingStream

Prefer XxxClient to XxxStream because, unlike Stream::readBytes(), Client::read() is virtual on all cores and therefore allows optimized implementations.

Portability

This library relies on Client, Print, and Stream definitions, which unfortunately differ from one core to another.

It has been tested on the following cores:

If your core is not supported, please open an issue. Thank you for your understanding.

arduinostreamutils's People

Contributors

bblanchon avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

arduinostreamutils's Issues

Optimal buffer size for hardware serial

Thanks for this great library(s). I've been using ArdionJson v5 for several years and am upgrading the project and moving to v6. One of the issues I've had is garbled JSON because of sending large messages (up to 512 bytes) at 9600 baud with minimal time in-between. In addition to StreamUtils I will also be using message pack, hence one of the reasons for upgrading ArduinoJson.

Without (then) knowing that the parser processes input byte-by-byte it seemed to me that I wasn't consuming the hardware buffer fast enough before the next message came in and an overrun occurred. Inserting a delay between the messages solved the issue. I also increased the hardware read/write buffers to 128 though I'm not sure if the delay is more important.

Given this, is there a relationship between the hardware buffer size, baudrate, and software buffer size that can be defined? If not numerically at least in a relative sense?

I can see this is actually a flow control problem. I wonder if its possible to implement software flow control into StreamUtils. But then it might not work with message pack.

Oh yeah and what is ReadThrottlingStream.hpp and does it have any significance to my issue. Thanks.

EEPROM.h: No such file or directory with ArduinoCore-mbed

Hello Beniot,

First, I want to thank you for the quick fix of issue reference to '__FlashStringHelper' is ambiguous with arduino-pico.
The fix seems to have introduced an error with the other core ArduinoCore-mbed for the raspberry pi pico. When I compile with StreamUtils version 1.7.2 and core ArduinoCore-mbed, I get the error message "EEPROM.h: No such file or directory".
The header EEPROM.h is not present in the core ArduinoCore-mbed. I assume ArduinoCore-mbed does not support EEPROM or EEPROM emulation (see EEPROM issue).
With StreamUtils version 1.7.1 and the core ArduinoCore-mbed it compiles just fine.
Reproduction of the error should be possible with an empty sketch, just include <StreamUtils.h> (version 1.7.2) and compile with core ArduinoCore-mbed.
Could you have a look?

Best regards,
Samuel

PS: I use both cores for different projects with the pico, therefore I notice corresponding differences. (;

complete compiler output:

In file included from c:\Users\samue\OneDrive\Dokumente\Arduino\libraries\StreamUtils\src/StreamUtils.hpp:17:0,
                 from c:\Users\samue\OneDrive\Dokumente\Arduino\libraries\StreamUtils\src/StreamUtils.h:5,
                 from C:\Users\samue\AppData\Local\Temp\.arduinoIDE-unsaved202328-15616-1up951o.5wvm\sketch_mar8a\sketch_mar8a.ino:1:
c:\Users\samue\OneDrive\Dokumente\Arduino\libraries\StreamUtils\src/StreamUtils/Streams/EepromStream.hpp:11:10: fatal error: EEPROM.h: No such file or directory
 #include <EEPROM.h>
          ^~~~~~~~~~
compilation terminated.

exit status 1

Compilation error: exit status 1

BufferingPrint needs to take Print (not Stream)

Can't currently use BufferingPrint with PubSubClient which inherits from Print.

Need to change the BasicBufferingPrint to use Print (not Stream).

--- BufferingPrint.hpp  2019-07-17 20:51:17.329167200 +0100
+++ BufferingPrint.hpp  2019-07-17 20:51:27.962692900 +0100
@@ -12,7 +12,7 @@

 template <typename TAllocator>
 struct BasicBufferingPrint : PrintProxy<WriteBufferingPolicy<TAllocator>> {
-  explicit BasicBufferingPrint(Stream &upstream, size_t capacity,
+  explicit BasicBufferingPrint(Print &upstream, size_t capacity,
                                TAllocator allocator = TAllocator())
       : PrintProxy<WriteBufferingPolicy<TAllocator>>(upstream,
                                                      {capacity, allocator}) {}

Add DXCORE AVR128DB64 to Library

Hi,

I have an AVR128DB4 and already using it to store a JSON in EEPROM. I added "|| defined(ID_AVR128DB)" to "Confirguration.hpp" in Line 39 and its working :)

Could you add it?

Thank you!

Error trying ot log while deserializing

When trying to follow this example to log a string while deserializing it I get either of the following errors (when using either a String or char * input)

error: no matching function for call to 'StreamUtils::ReadLoggingStream::ReadLoggingStream(String&, HardwareSerial&)
error: no matching function for call to 'StreamUtils::ReadLoggingStream::ReadLoggingStream(char*&, HardwareSerial&)

Sample code

  String text = extractContent(*filename);
  // char *textchar = extractContentChar(*filename);

  ReadLoggingStream loggingStream(text, Serial);
  // ReadLoggingStream loggingStream(textChar, Serial);
  DeserializationError error = deserializeJson(ds, loggingStream);

Cannot use a StringStream in ArduinoJson's deserializeJson

I hope I'm not doing anything wrong here, but if I try to use a StringStream as the source for ArduinoJson's deserializeJson method, it always results in an EmptyInput error.

DynamicJsonDocument doc(512);
String testString= "{\"testField\":\"testValue\"}";
StringStream testStringStream(testString);
auto jsonErr = deserializeJson(doc, testStringStream);

if (jsonErr)
{
    Serial.printf("I always get this EmpyInput error: %s\n", jsonErr.c_str());
    return;
}

Running on ESP32 Arduino framework if that makes any difference. I haven't tested other platforms.

multicore environment: locking of Stream methods

Hi Benoit,

not really your issue or an issue of this wonderful library, but rather a general Arduino problem with multicore environments like ESP32 - really just putting it up for discussion

I am prepared to roll my own solution, so no fear of feature requests ;) but this is going to bite others as well

I have the following setup:

  • console dual output to a Serial and Websockets if connected
  • both Serial and Websockets (which are behind Webserial) are buffered with a WriteBufferingStream
  • both WriteBufferingStreams are connected to a T-piece which is driven by the Console stream class.

reasoning: I want console output go to Serial, and to any WebSerial clients as-is. The single character I/O of console output is killing the Websockets (really ESPAsyncWebserver) and its not her fault, so I introduced buffering. To get stuff out eventually, I added a flushTicker to flush the whole stack of streams periodically.
This code is not in the online version but looks like so:

  // during setup run flushBuffers(); via a ticker
  // at the end of setup detach the ticker as loop() will take over
  flushTicker.attach_ms(STARTUP_FLUSH_INTERVAL, []() { flushBuffers(); });

That buffering works fine... mostly. Every now and then output becomes intermingled and garbled.

I tracked down the issue to the ticker library running its own high-priority soft timer task pinned to esp32 core 0 which actually calls the user callbacks when due - in the context of core 0. Application code like setup() and loop() are pinned to core 1.

The garbling seems to happen when write() calls are percolating down this stack of streams, and ticker kicks in and calls flushBuffers() which calls the stream's flush() method simultaneously. I am pretty sure that is the cause, because if I flushBuffers() repeatedly at some points in setup() the problem goes away.

What this boils down to is: Arduino Stream, Print etc are not multicore-safe as-is, and likely not even single-core multithreading with a preemptive OS underneath.

The solution of course is: use a real operating system ;)

The second-best solution is to introduce locking on Stream methods. Now doing this across the board would be a tad heavy-handed, so I am looking at a wrapper class which can be used as poor man's multicore safety feature selectively.

That suggests a wrapper class which has a mutex, and calls to write(), flush() etc need to acquire the mutex on enter, and release on return. Waiting delay, or no-wait immediate failure would be parameters; for instance, if the ticker-driven flush() routine happens to hit a locked mutex no harm is done by immediately returning - flushing eventually is good enough.

For FreeRTOS, I have the nicely wrapped mutex primitives at hand.

Have you ever seen something similar? What would you suggest?

best regards
Michael

WriteBufferingStream does not buffer in some instances

Arduino Mega, using IDE 2.0 beta.
Ethernet library.

I have a function that parses incoming commands and calls other functions to send responses, passing the EthernetClient as a parameter. For instance (I have edited the names for clarity):

            } else if (strcmp(url, "/Sensors") == 0) {
              answerStatus(client, Temp_Status | Press_Status);

Then answerStatus does this at the very beginning (c is the EthernetClient):

  sendHeaders(c, 200);
  WriteBufferingStream buffer(c, 256);
  StaticJsonDocument<256> doc;

and then a json document is created, buffered and sent out.

Before that, as it can be seen we call a function that sends out the appropriate response headers, which can't be simpler (names edited for clarity):

void sendHeaders(EthernetClient c, int code) {
  WriteBufferingStream buffer{c, 192};
  switch (code) {
    case 200:
      buffer.println("HTTP/1.1 200 OK");
      break;
    case 400:
      buffer.println("HTTP/1.1 400 Bad Request");
      break;
    default:
      buffer.println("HTTP/1.1 200 OK I guess");
      break;
  }
  buffer.println("Content-Type: application/json");
  buffer.println("cache-control: private, max-age=0, no-cache");
  buffer.println("Access-Control-Allow-Origin: *");
  buffer.println("Connection: close");
  buffer.println();
  buffer.flush();
  return;
}

The issue is that, while headers (the function above) are always buffered and I can see them getting out in a single nice packet, the rest of the answer is sometimes not buffered and is seen on the wire with just one byte per packet, instead of a single packet. The key here is sometimes: all the functions that send out answers are structured like above (copypasting straight from the documentation), but some of them get their answers in one nice packet and others get a stream of packets with just one byte.

I'd like to know what could possibly make WriteBufferingStream not buffer its output, or perhaps the W5100 in the Mega deciding to send one byte per packet despite getting a nice big buffer to send out... Can't think of a way to debug this, and I can't see any reason why some functions get their output buffered and tucket in one packet while others don't, using the same technique.

Longest answer is below 256 bytes.

Any clue, pointer, idea or direction appreciated.

JSON deserialisation of Hammingstream, can't get it to work

Hi, I'm using a mega as port externder for a ESP32 via UART. The idea is to send JSON files with IO-status and requests back and forth. Because the size of the JSON doc is larger than UART capacity, it needs to be sent in pieces. I've tested it with the readBufferingstream class and this works great (thank you by the way)! I wanted to add some errorchecking by trying the hammingstream-class. In my test setup I make some dummy JSON about 800bytes. I think the sending works ok (I see it on scope) using the "serializeJson(doc, eccSerial1);", but on the receiver side the "DeserializationError err = deserializeJson(doc, eccSerial1);" returns error.

sender (arduino mega)

#include <ArduinoJson.h>
#include <StreamUtils.h>
// Create a new Stream that buffers all writes to Serial
//ReadBufferingStream bufferedSerial{Serial1, 64};
HammingStream<7, 4> eccSerial1(Serial1);

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  Serial1.begin(115200);

}

void loop() {
  // put your main code here, to run repeatedly:
  long timestamp = millis();
  int value = analogRead(1);
  DynamicJsonDocument doc(2000);
  doc["timestamp"] = timestamp;
  doc["value"] = value;
  JsonArray bulk=doc.createNestedArray("bulk");
  for(int i=0;i<50;i++){
    bulk.add(micros());
  }

  // Send the JSON document over the "link" serial port
  //serializeJson(doc, bufferedSerial);
  serializeJson(doc, eccSerial1);
  
  // Even if it looks like the bytes are extracted one by one, they are actually
  // read by chunks of 64 bytes and placed in a buffer.
  while (eccSerial1.available()) {
    Serial1.write(eccSerial1.read());
    //Serial1.write(bufferedSerial.read());
  }
  
  delay(2000);
}

receiver(ESP32):

#include <HardwareSerial.h>
#include <ArduinoJson.h>
#include <StreamUtils.h>
HardwareSerial serial1(1);

HammingStream<7, 4> eccSerial1(serial1);


void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  serial1.begin(115200,SERIAL_8N1, 15, 2);
  //pinMode(15,OUTPUT);
  //pinMode(2,OUTPUT);
}

void loop() {
 // Check if the other Arduino is transmitting
  char response[2000]; 
  if(eccSerial1.available())
  {
    // Allocate the JSON document
    // This one must be bigger than for the sender because it must store the strings
    DynamicJsonDocument doc(2000);

    // Read the JSON document from the "link" serial port
    DeserializationError err = deserializeJson(doc, eccSerial1);
    //DeserializationError err = deserializeJson(doc,bufferedSerial );

    if (err == DeserializationError::Ok) 
    {
      // Print the values
      // (we must use as<T>() to resolve the ambiguity)
      Serial.print("timestamp = ");
      Serial.println(doc["timestamp"].as<long>());
      Serial.print("value = ");
      Serial.println(doc["value"].as<int>());
      serializeJsonPretty(doc, Serial);
    } 
    else 
    {
      // Print error to the "debug" serial port
      Serial.print("deserializeJson() returned ");
      Serial.println(err.c_str());
  
      // Flush all bytes in the "link" serial port buffer
      //while (bufferedSerial.available() > 0)
        //bufferedSerial.read();
      while (eccSerial1.available() > 0)
        eccSerial1.read();
    }
  }
  //delay(1000);
}

json, sd write performance

I am about to see if get better sd card write performance with recommend approach json write to sd card

This is what I read from original post

File file = SD.open(filename, FILE_WRITE);
WriteBufferingStream bufferedFile(client, 64);
doc["hello"] = "world";
serializeJson(doc, loggedFile);
bufferedFile.flush();
file.close();

Should not example have describe like this rather that above one

File file = SD.open(filename, FILE_WRITE);
WriteBufferingStream bufferedFile(file, 64);
doc["hello"] = "world";
serializeJson(doc, bufferedFile);
bufferedFile.flush();
file.close();

Is there any recommend buffer size (above example 64) ? Does it depends on size of sd card ie. cluster size or json size or...I am using #include <SdFat.h> library for SD card writing.

HammingStream not working

Hi,

does the ArduinoStreamUtils-lib work with the ARM® Cortex®-M4 XMC4700 ?

Thank you

King regards

Stefan

Debug logging

I'm working on an ESP32 project now where the devices must log offline data locally (to SPIFFS and/or SD) as well as debugging information. My solution is not elegant, but it does work. I have a debug function that you can pass a string to and it will send the results to both the serial port and the SD card. It works, but it's clumsy and not robust. I also have uploads working, so debug files get posted as a multipart form to a web server automatically daily or whenever wifi becomes available. My methods aren't elegant but they seem to be reliable.

I've always wanted something that can take a printf statement and send the data to serial, SD, SPIFFS and/or a webserver (remote logging). Even better would be the ability to pass it a "debug level" info, warning, error, etc., and control the level of logging by passing it a flag from the server in a json config file.

I don't know if you're interested in helping with something like this, but I'll happily offer any and all of my working code to use as a starting point if you can. Here's what I would like to see:

typedef  struct {
    bool bSerial = true;
    bool bSD = true;
    bool bSPIFFS = false;
    bool bWebServer = true;
    bool bSSL = false;
    char *username = "username";
    char *password = "password";
    char *fingerprint = ""; //for ESP8266 or other constrained devices that can't do full SSL
    bool bRTC = true;  
    char *fileTemplate = "/debug/{d}.log";  // {date - yyyymmdd}  I use a realtime clock, but timeLib works too, or SNTP.  Anything with a now() function
    char *url = "debug.mydomain.com/logme.php";
    char *formVariable = "data";
    byte *port = 80;
    byte debugLevel = 0;  //0 = none, 1 = error, 2 = error + warning, 3 = error + warning + info, 4 = lvl 3 + debugging info
    long version = 0;  //this could be populated as a config file download using arduinoJson  If the server version is different than the local version , download new config, save to SPIFFS reboot and reparse
} debugLog

debug.printf(3, "LightLevel  = %d, humidity = %02f\n", lightsOn, humidity); //first param is debugging level 3 - info
debug.printf(2, "Wifi disconnected\n"); //first param is debugging level 2 - warning
debug.printf(1, "Some ugly error %d, %d, %02f\n", param1, param2, param3); //first param is debugging level 1 - error  

Using Arduino But on other Platforms

Hi Beniot,
Many thanks for your hard work on Arduino Json and this stream utils.
I am using your software in a ESP32 IDF only application.
It works fine and Arduino Json at 200K small for my C++ needs.
However, I do not have Serial.print and Wire as its not in my platform.
Q: Is there any pure Arduino dependancies that will affect my use or is your codeor is it fully platform independant?

Many thanks again

Main header doesn't include MemoryStream.hpp

The main header file doesn't include MemoryStream.hpp.

Fix:

--- StreamUtils.h.orig  2019-06-05 08:30:33.000000000 +0100
+++ StreamUtils.h       2019-07-17 21:20:23.643851000 +0100
@@ -10,6 +10,7 @@
 #include "StreamUtils/Prints/BufferingPrint.hpp"
 #include "StreamUtils/Prints/LoggingPrint.hpp"
 #include "StreamUtils/Streams/LoggingStream.hpp"
+#include "StreamUtils/Streams/MemoryStream.hpp"
 #include "StreamUtils/Streams/ReadBufferingStream.hpp"
 #include "StreamUtils/Streams/ReadLoggingStream.hpp"
 #include "StreamUtils/Streams/ReadThrottlingStream.hpp"

Hammingstream using ESP32 only receiving question marks.

Hi Benoit,

I'm using the provided "HammingSerial1" Sketch with an ESP32 looping the TX and RX pins for testing, but only receiving "????" as outputs on the serial monitor.
So far, I've tried remapping the UART to other Pins and connecting the ESP32 to an Arduino Nano Every (with level shifter), which resulted in further question marks on the ESP side, despite the Nano working flawlessly.
ESP32 Board: https://www.az-delivery.de/en/products/esp32-developmentboard

Best regards
Felix

Errors on latest version when using esp8266

Hello,

I recently learned about this library after using ArduinoJSON and when I use this library and compile, I get this error

In file included from /Users/erikb/.platformio/lib/StreamUtils_ID6347/src/StreamUtils/Clients/LoggingClient.hpp:7:0,
from /Users/erikb/.platformio/lib/StreamUtils_ID6347/src/StreamUtils.h:5,
from src/main.cpp:6:
/Users/erikb/.platformio/lib/StreamUtils_ID6347/src/StreamUtils/Clients/../Policies/ConnectForwardingPolicy.hpp: In member function 'bool StreamUtils::ConnectForwardingPolicy::stop(Client&, unsigned int)':
/Users/erikb/.platformio/lib/StreamUtils_ID6347/src/StreamUtils/Clients/../Policies/ConnectForwardingPolicy.hpp:28:31: error: no matching function for call to 'Client::stop(unsigned int&)'
return target.stop(timeout);
^
/Users/erikb/.platformio/lib/StreamUtils_ID6347/src/StreamUtils/Clients/../Policies/ConnectForwardingPolicy.hpp:28:31: note: candidate is:
In file included from /Users/erikb/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266WiFi/src/WiFiClient.h:27:0,
from /Users/erikb/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266WiFi/src/ESP8266WiFi.h:39,
from src/main.cpp:4:
/Users/erikb/.platformio/packages/framework-arduinoespressif8266/cores/esp8266/Client.h:38:22: note: virtual void Client::stop()
virtual void stop() = 0;
^
/Users/erikb/.platformio/packages/framework-arduinoespressif8266/cores/esp8266/Client.h:38:22: note: candidate expects 0 arguments, 1 provided
In file included from /Users/erikb/.platformio/lib/StreamUtils_ID6347/src/StreamUtils/Clients/LoggingClient.hpp:9:0,
from /Users/erikb/.platformio/lib/StreamUtils_ID6347/src/StreamUtils.h:5,
from src/main.cpp:6:
/Users/erikb/.platformio/lib/StreamUtils_ID6347/src/StreamUtils/Clients/../Policies/WriteLoggingPolicy.hpp: In member function 'bool StreamUtils::WriteLoggingPolicy::flush(Client&, unsigned int)':
/Users/erikb/.platformio/lib/StreamUtils_ID6347/src/StreamUtils/Clients/../Policies/WriteLoggingPolicy.hpp:34:32: error: no matching function for call to 'Client::flush(unsigned int&)'
return target.flush(timeout);
^
/Users/erikb/.platformio/lib/StreamUtils_ID6347/src/StreamUtils/Clients/../Policies/WriteLoggingPolicy.hpp:34:32: note: candidate is:
In file included from /Users/erikb/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266WiFi/src/WiFiClient.h:27:0,
from /Users/erikb/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266WiFi/src/ESP8266WiFi.h:39,
from src/main.cpp:4:
/Users/erikb/.platformio/packages/framework-arduinoespressif8266/cores/esp8266/Client.h:37:22: note: virtual void Client::flush()
virtual void flush() = 0;
^
/Users/erikb/.platformio/packages/framework-arduinoespressif8266/cores/esp8266/Client.h:37:22: note: candidate expects 0 arguments, 1 provided
In file included from /Users/erikb/.platformio/lib/StreamUtils_ID6347/src/StreamUtils/Clients/LoggingClient.hpp:10:0,
from /Users/erikb/.platformio/lib/StreamUtils_ID6347/src/StreamUtils.h:5,
from src/main.cpp:6:
/Users/erikb/.platformio/lib/StreamUtils_ID6347/src/StreamUtils/Clients/ClientProxy.hpp: In instantiation of 'class StreamUtils::ClientProxy<StreamUtils::ReadLoggingPolicy, StreamUtils::WriteLoggingPolicy, StreamUtils::ConnectForwardingPolicy>':
/Users/erikb/.platformio/lib/StreamUtils_ID6347/src/StreamUtils/Clients/LoggingClient.hpp:14:24: required from here
/Users/erikb/.platformio/lib/StreamUtils_ID6347/src/StreamUtils/Clients/ClientProxy.hpp:69:7: error: 'int StreamUtils::ClientProxy<ReadPolicy, WritePolicy, ConnectPolicy>::connect(const IPAddress&, uint16_t) [with ReadPolicy = StreamUtils::ReadLoggingPolicy; WritePolicy = StreamUtils::WriteLoggingPolicy; ConnectPolicy = StreamUtils::ConnectForwardingPolicy; uint16_t = short unsigned int]' marked override, but does not override
int connect(const IPAddress &ip, uint16_t port) override {
^
/Users/erikb/.platformio/lib/StreamUtils_ID6347/src/StreamUtils/Clients/ClientProxy.hpp:85:8: error: 'bool StreamUtils::ClientProxy<ReadPolicy, WritePolicy, ConnectPolicy>::stop(unsigned int) [with ReadPolicy = StreamUtils::ReadLoggingPolicy; WritePolicy = StreamUtils::WriteLoggingPolicy; ConnectPolicy = StreamUtils::ConnectForwardingPolicy]' marked override, but does not override
bool stop(unsigned timeout = 0) override {
^
/Users/erikb/.platformio/lib/StreamUtils_ID6347/src/StreamUtils/Clients/ClientProxy.hpp:105:8: error: 'bool StreamUtils::ClientProxy<ReadPolicy, WritePolicy, ConnectPolicy>::flush(unsigned int) [with ReadPolicy = StreamUtils::ReadLoggingPolicy; WritePolicy = StreamUtils::WriteLoggingPolicy; ConnectPolicy = StreamUtils::ConnectForwardingPolicy]' marked override, but does not override
bool flush(unsigned timeout = 0) override {
^
In file included from /Users/erikb/.platformio/lib/StreamUtils_ID6347/src/StreamUtils/Clients/ReadBufferingClient.hpp:9:0,
from /Users/erikb/.platformio/lib/StreamUtils_ID6347/src/StreamUtils.h:6,
from src/main.cpp:6:
/Users/erikb/.platformio/lib/StreamUtils_ID6347/src/StreamUtils/Clients/../Policies/WriteForwardingPolicy.hpp: In member function 'bool StreamUtils::WriteForwardingPolicy::flush(Client&, unsigned int)':
/Users/erikb/.platformio/lib/StreamUtils_ID6347/src/StreamUtils/Clients/../Policies/WriteForwardingPolicy.hpp:26:32: error: no matching function for call to 'Client::flush(unsigned int&)'
return client.flush(timeout);
^
/Users/erikb/.platformio/lib/StreamUtils_ID6347/src/StreamUtils/Clients/../Policies/WriteForwardingPolicy.hpp:26:32: note: candidate is:
In file included from /Users/erikb/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266WiFi/src/WiFiClient.h:27:0,
from /Users/erikb/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266WiFi/src/ESP8266WiFi.h:39,
from src/main.cpp:4:
/Users/erikb/.platformio/packages/framework-arduinoespressif8266/cores/esp8266/Client.h:37:22: note: virtual void Client::flush()
virtual void flush() = 0;
^
/Users/erikb/.platformio/packages/framework-arduinoespressif8266/cores/esp8266/Client.h:37:22: note: candidate expects 0 arguments, 1 provided
In file included from /Users/erikb/.platformio/lib/StreamUtils_ID6347/src/StreamUtils/Clients/LoggingClient.hpp:10:0,
from /Users/erikb/.platformio/lib/StreamUtils_ID6347/src/StreamUtils.h:5,
from src/main.cpp:6:
/Users/erikb/.platformio/lib/StreamUtils_ID6347/src/StreamUtils/Clients/ClientProxy.hpp: In instantiation of 'class StreamUtils::ClientProxy<StreamUtils::ReadLoggingPolicy, StreamUtils::WriteForwardingPolicy, StreamUtils::ConnectForwardingPolicy>':
/Users/erikb/.platformio/lib/StreamUtils_ID6347/src/StreamUtils/Clients/ReadLoggingClient.hpp:14:28: required from here
/Users/erikb/.platformio/lib/StreamUtils_ID6347/src/StreamUtils/Clients/ClientProxy.hpp:69:7: error: 'int StreamUtils::ClientProxy<ReadPolicy, WritePolicy, ConnectPolicy>::connect(const IPAddress&, uint16_t) [with ReadPolicy = StreamUtils::ReadLoggingPolicy; WritePolicy = StreamUtils::WriteForwardingPolicy; ConnectPolicy = StreamUtils::ConnectForwardingPolicy; uint16_t = short unsigned int]' marked override, but does not override
int connect(const IPAddress &ip, uint16_t port) override {
^
/Users/erikb/.platformio/lib/StreamUtils_ID6347/src/StreamUtils/Clients/ClientProxy.hpp:85:8: error: 'bool StreamUtils::ClientProxy<ReadPolicy, WritePolicy, ConnectPolicy>::stop(unsigned int) [with ReadPolicy = StreamUtils::ReadLoggingPolicy; WritePolicy = StreamUtils::WriteForwardingPolicy; ConnectPolicy = StreamUtils::ConnectForwardingPolicy]' marked override, but does not override
bool stop(unsigned timeout = 0) override {
^
/Users/erikb/.platformio/lib/StreamUtils_ID6347/src/StreamUtils/Clients/ClientProxy.hpp:105:8: error: 'bool StreamUtils::ClientProxy<ReadPolicy, WritePolicy, ConnectPolicy>::flush(unsigned int) [with ReadPolicy = StreamUtils::ReadLoggingPolicy; WritePolicy = StreamUtils::WriteForwardingPolicy; ConnectPolicy = StreamUtils::ConnectForwardingPolicy]' marked override, but does not override
bool flush(unsigned timeout = 0) override {
^
In file included from /Users/erikb/.platformio/lib/StreamUtils_ID6347/src/StreamUtils/Clients/WriteBufferingClient.hpp:9:0,
from /Users/erikb/.platformio/lib/StreamUtils_ID6347/src/StreamUtils.h:8,
from src/main.cpp:6:
/Users/erikb/.platformio/lib/StreamUtils_ID6347/src/StreamUtils/Clients/../Policies/WriteBufferingPolicy.hpp: In member function 'bool StreamUtils::WriteBufferingPolicy::flush(Client&, unsigned int)':
/Users/erikb/.platformio/lib/StreamUtils_ID6347/src/StreamUtils/Clients/../Policies/WriteBufferingPolicy.hpp:73:32: error: no matching function for call to 'Client::flush(unsigned int&)'
return target.flush(timeout);
^
/Users/erikb/.platformio/lib/StreamUtils_ID6347/src/StreamUtils/Clients/../Policies/WriteBufferingPolicy.hpp:73:32: note: candidate is:
In file included from /Users/erikb/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266WiFi/src/WiFiClient.h:27:0,
from /Users/erikb/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266WiFi/src/ESP8266WiFi.h:39,
from src/main.cpp:4:
/Users/erikb/.platformio/packages/framework-arduinoespressif8266/cores/esp8266/Client.h:37:22: note: virtual void Client::flush()
virtual void flush() = 0;
^
/Users/erikb/.platformio/packages/framework-arduinoespressif8266/cores/esp8266/Client.h:37:22: note: candidate expects 0 arguments, 1 provided
In file included from /Users/erikb/.platformio/lib/StreamUtils_ID6347/src/StreamUtils/Clients/LoggingClient.hpp:10:0,
from /Users/erikb/.platformio/lib/StreamUtils_ID6347/src/StreamUtils.h:5,
from src/main.cpp:6:
/Users/erikb/.platformio/lib/StreamUtils_ID6347/src/StreamUtils/Clients/ClientProxy.hpp: In instantiation of 'class StreamUtils::ClientProxy<StreamUtils::ReadForwardingPolicy, StreamUtils::WriteLoggingPolicy, StreamUtils::ConnectForwardingPolicy>':
/Users/erikb/.platformio/lib/StreamUtils_ID6347/src/StreamUtils/Clients/WriteLoggingClient.hpp:15:7: required from here
/Users/erikb/.platformio/lib/StreamUtils_ID6347/src/StreamUtils/Clients/ClientProxy.hpp:69:7: error: 'int StreamUtils::ClientProxy<ReadPolicy, WritePolicy, ConnectPolicy>::connect(const IPAddress&, uint16_t) [with ReadPolicy = StreamUtils::ReadForwardingPolicy; WritePolicy = StreamUtils::WriteLoggingPolicy; ConnectPolicy = StreamUtils::ConnectForwardingPolicy; uint16_t = short unsigned int]' marked override, but does not override
int connect(const IPAddress &ip, uint16_t port) override {
^
/Users/erikb/.platformio/lib/StreamUtils_ID6347/src/StreamUtils/Clients/ClientProxy.hpp:85:8: error: 'bool StreamUtils::ClientProxy<ReadPolicy, WritePolicy, ConnectPolicy>::stop(unsigned int) [with ReadPolicy = StreamUtils::ReadForwardingPolicy; WritePolicy = StreamUtils::WriteLoggingPolicy; ConnectPolicy = StreamUtils::ConnectForwardingPolicy]' marked override, but does not override
bool stop(unsigned timeout = 0) override {
^
/Users/erikb/.platformio/lib/StreamUtils_ID6347/src/StreamUtils/Clients/ClientProxy.hpp:105:8: error: 'bool StreamUtils::ClientProxy<ReadPolicy, WritePolicy, ConnectPolicy>::flush(unsigned int) [with ReadPolicy = StreamUtils::ReadForwardingPolicy; WritePolicy = StreamUtils::WriteLoggingPolicy; ConnectPolicy = StreamUtils::ConnectForwardingPolicy]' marked override, but does not override
bool flush(unsigned timeout = 0) override {
^
*** [.pioenvs/d1_mini/src/main.cpp.o] Error 1

I am using a Wemos D1 R2 and the ide I am using is Visual Studio with PlatformIO but this error persists in Arduino IDE and CLion. Is there any way to fix this? Thank you.

Code:
`
#include <ESP8266WiFi.h>
#include <ArduinoJson.h>
#include <StreamUtils.h>

const char *ssid = "-----------";
const char *password = "-----------";
const uint8_t port = 80;

WiFiServer wifiServer(port);

const size_t capacity = JSON_OBJECT_SIZE(9) + 110;
DynamicJsonDocument doc(capacity);

void waves(DynamicJsonDocument doc);
void music(DynamicJsonDocument doc);
void solid(DynamicJsonDocument doc);

String t;

uint8_t brightness;

void setup() {

Serial.begin(115200);

delay(1000);

WiFi.begin(ssid, password);

while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting..");
}

Serial.print("Connected to WiFi. IP:");
Serial.println(WiFi.localIP());
Serial.print("Port:");
Serial.println(port);

wifiServer.begin();

}

void loop() {

WiFiClient client = wifiServer.available();
if (client) {

    while (client.connected() > 0) {
       // char l = client.read();
       // Serial.print(l);
        ReadBufferingStream bufferingClient(client, capacity);
        deserializeJson(doc, bufferingClient);
        
        t = doc["mode"].as<String>();
        if(t.equals("DeviceMusic")) {
            music(doc);
        } else if(t.equals("DeviceSolid")){
            solid(doc);
        } else if(t.equals("DeviceWaves")){
            waves(doc);
        }
    }
    Serial.println(" ");
    client.flush();
    client.stop();
    delay(10);
}

}

void waves(DynamicJsonDocument document) {

}

void solid(DynamicJsonDocument doc){
brightness = doc["brightness"].as<int8_t>();

}

void music(DynamicJsonDocument doc){

}
`

STM32f103c8 maple mini

Hi, StreamUtils doesn't work in stm32f103c8t6
In file included from C:\Users\Pablo\Documents\Arduino\libraries\StreamUtils\src/StreamUtils/Prints/BufferingPrint.hpp:9:0,
from C:\Users\Pablo\Documents\Arduino\libraries\StreamUtils\src/StreamUtils.hpp:13,
from C:\Users\Pablo\Documents\Arduino\libraries\StreamUtils\src/StreamUtils.h:5,
from C:\Users\Pablo\Documents\Arduino\libraries\StreamUtils\examples\WriteBuffer\WriteBuffer.ino:14:
C:\Users\Pablo\Documents\Arduino\libraries\StreamUtils\src/StreamUtils/Prints/PrintProxy.hpp: In instantiation of 'class StreamUtils::PrintProxyStreamUtils::WriteLoggingPolicy':
C:\Users\Pablo\Documents\Arduino\libraries\StreamUtils\src/StreamUtils/Prints/LoggingPrint.hpp:12:23: required from here
C:\Users\Pablo\Documents\Arduino\libraries\StreamUtils\src/StreamUtils/Prints/PrintProxy.hpp:27:10: error: 'size_t StreamUtils::PrintProxy::write(const uint8_t*, size_t) [with WritePolicy = StreamUtils::WriteLoggingPolicy; size_t = unsigned int; uint8_t = unsigned char]' marked override, but does not override
size_t write(const uint8_t *buffer, size_t size) override {
and more errors

Lots of TCP Window Full and TCP Zero Window while receiving a file via HTTP

Benoît,

I'm using your JSON library and Stream Utils in a network µController project.
Awesome work on the JSON library, by the way.

Environment:

I'm using both ReadBufferingClient and WriteBufferingClient with 512KB buffers.

WriteBufferingClient is working great! The M4 can send a largish chunk of data to the server via an HTTP Put with only one TCP data packet (plus the SYNs, ACKs, etc.) Prior to using WriteBufferingClient the data was being broken up into numerous data packets.

Receiving data, though isn't working so well. I'm using a ReadBufferingClient with a 512-byte buffer. Even so, the incoming data is broken into lots of packets and Wireshark reports lots of TCP WIndow Full and TCP Zero Window events.

The incoming data is about 47KB and is retrieved with an HTTP GET using the Arduino EthernetClient.

There is actually an additional custom HttpClient class between the EthernetClient and ReadBufferingClient. All of its methods (read, available, etc.), simply call the underlying EthernetClient method and return the results.

The PoE FeatherWing has a Wiznet W5500 on it which is supposed to have 2KB buffers by default. Seems like this should work reasonably well.

Have you run into this?
Any suggestions?

TIA,
Dave

One-to-Many and Many-to-One Stream gateway

Hi Benoit,

In one of my projects I let my µCs provide a command-line interface (based on the Shell library). At some point it turned out to be very useful to provide this cli via different channels, i.e. the USB Serial, maybe a different Serial1 and even via MQTT. The best idea I could come up with to facilitate that was to implement a one-to-many and many-to-one Stream gateway or valve - I called it MultiStream for lack of a better term. This Stream subclass basically write()s and read()s everything to and from multiple other registered Streams. In my case it makes it possible to have a command-line interface via both MQTT and USB Serial simulataneously.

I think something like this would make a wonderful addition to your StreamUtils library.

Here is my (probably awful) implementation:

#pragma once

/**
  @file MultiStream.hpp
  @brief Multiple streams in one
*/

#include <Arduino.h>

/**
 *  @brief Interface multiple streams from a single one
 */
template<size_t nSlots>
class MultiStream : public Stream
{
public:
  MultiStream()
    : _slots{ nullptr } {};

  //! Add a stream
  bool add(Stream& stream)
  {
    uint8_t index;
    const bool anyFree = nextFreeSlot(index);
    if (anyFree)
      this->_slots[index] = &stream;
    return anyFree;
  }

  //! Return the first available byte of any stream in order
  int read()
  {
    int ret = -1;
    for (size_t i = 0; i < nSlots; i++) {
      Stream* stream = this->_slots[i];
      if (not stream)
        continue;
#if ESP8266
      // don't know why (yet) but only this works properly together with our
      // StreamGateway
      ret = stream->read();
      if (ret >= 0)
        return ret;
#else  // #if ESP8266
      char c;
      // don't use stream->read() as it doesn't honor timeout (needed for
      // EEPROM write from cli)
      const size_t n = stream->readBytes(&c, 1);
      if (n > 0)
        return c;
#endif // #if ESP8266
    }
    return ret;
  }

  //! fill given buffer from all streams in order until full or timeout
  size_t readBytes(char* buffer, size_t length)
  {
    size_t count = 0;
    for (size_t i = 0; i < nSlots; i++) {
      char* buf = buffer + count;
      const size_t len = length - count;
      if (not len)
        return count;
      Stream* stream = this->_slots[i];
      if (not stream)
        continue;
      count += stream->readBytes(buf, len);
    }
    return count;
  }

  //! Peek on first stream available in order
  int peek()
  {
    int ret = -1;
    for (size_t i = 0; i < nSlots; i++) {
      Stream* stream = this->_slots[i];
      if (not stream)
        continue;
      ret = stream->peek();
      if (ret >= 0)
        return ret;
    }
    return ret;
  }

  //! Total available bytes on all streams
  int available()
  {
    int total = 0;
    for (size_t i = 0; i < nSlots; i++) {
      Stream* stream = this->_slots[i];
      if (not stream)
        continue;
      const int av = stream->available();
      if (av > 0)
        total += av;
    }
    return total;
  }

  //! Write a byte to all streams
  size_t write(uint8_t b)
  {
    int total = 0;
    for (size_t i = 0; i < nSlots; i++) {
      Stream* stream = this->_slots[i];
      if (not stream)
        continue;
      const int written = stream->write(b);
      if (written > total)
        total = written;
    }
    return total;
  }

  template<typename TYPE>
  size_t write(TYPE b)
  {
    int total = 0;
    for (size_t i = 0; i < nSlots; i++) {
      Stream* stream = this->_slots[i];
      if (not stream)
        continue;
      const int written = stream->write(b);
      if (written > total)
        total = written;
    }
    return total;
  }

#if !defined(ESP8266)
  // For some reason, the ArduinoEPS8266 framework's Stream class doesn't
  // implement getTimeout()...
  //! Determine the maximum timeout of all available streams
  unsigned long getTimeout()
  {
    unsigned long timeout = 0;
    for (size_t i = 0; i < nSlots; i++) {
      Stream* stream = this->_slots[i];
      if (not stream)
        continue;
      const unsigned long streamTimeout = stream->getTimeout();
      if (streamTimeout > timeout)
        timeout = streamTimeout;
    }
    return timeout;
  }
#endif // #if ! defined(ESP8266)

  //! Set timeout of all available streams
  void setTimeout(const unsigned long timeout)
  {
    for (size_t i = 0; i < nSlots; i++) {
      Stream* stream = this->_slots[i];
      if (not stream)
        continue;
      stream->setTimeout(timeout);
    }
  }

  void flush()
  {
    for (size_t i = 0; i < nSlots; i++) {
      Stream* stream = this->_slots[i];
      if (not stream)
        continue;
      stream->flush();
    }
  }

protected:
  Stream* _slots[nSlots] = { nullptr };

  /**
   * @brief determine the next free slot index
   * @param index the next free index
   * @return whether a free slot was available
   */
  bool nextFreeSlot(uint8_t& index)
  {
    for (size_t i = 0; i < nSlots; i++) {
      if (not this->_slots[i]) {
        index = i;
        return true;
      }
    }
    return false;
  };
};

CRC and other hash algorithms

@supergohan, in #18, you requested a CRC feature in StreamUtils.

That's something I wanted to add for a long time, but I waited for an actual application to make sure the interface was designed correctly.

Can you share a (pseudo-)code sample that shows how you plan to use this feature?
I'm assuming that you're using this hash to control the integrity of a message; make sure that you include this part in the sample.

Kudos

Thanks for a great and useful library!

Always flush after...

You do mention this as optional vis-a-vis the destructor calls flush. If the buffer is global and you send less than the buffer size and you want to see the ouptut right away then you have to flush. Is this correct?

reference to '__FlashStringHelper' is ambiguous with arduino-pico

Hello Beniot,

When I include <StreamUtils.h> and compile with the core arduino-pico I get the error message "reference to '__FlashStringHelper' is ambiguous". If I compile withe core ArduinoCore-mbed I get no error messages.
Reproduction of the error should be possible with an empty sketch, just include <StreamUtils.h> and compile with arduino-pico.
Could you have a look?
Thank you very much.

Best regards,
Samuel

PS: I really like your libraries (ArduinoJSON ans StreamUtils) and bought your ArduinoJSON book some days ago. (:

complete compiler output:
`In file included from c:\Users\samue\OneDrive\Dokumente\Arduino\libraries\StreamUtils\src/StreamUtils.hpp:22,
from c:\Users\samue\OneDrive\Dokumente\Arduino\libraries\StreamUtils\src/StreamUtils.h:5,
from C:\Users\samue\AppData\Local\Temp.arduinoIDE-unsaved202324-14884-conkzs.vg2bl\sketch_mar4a\sketch_mar4a.ino:1:
c:\Users\samue\OneDrive\Dokumente\Arduino\libraries\StreamUtils\src/StreamUtils/Streams/ProgmemStream.hpp:22:23: error: reference to '__FlashStringHelper' is ambiguous
22 | ProgmemStream(const __FlashStringHelper* ptr)
| ^~~~~~~~~~~~~~~~~~~
In file included from C:\Users\samue\AppData\Local\Arduino15\packages\rp2040\hardware\rp2040\3.0.0\cores\rp2040/api/String.h:2,
from c:\users\samue\appdata\local\arduino15\packages\rp2040\hardware\rp2040\3.0.0\arduinocore-api\api\IPAddress.h:23,
from c:\users\samue\appdata\local\arduino15\packages\rp2040\hardware\rp2040\3.0.0\arduinocore-api\api\arduinoapi.h:30,
from C:\Users\samue\AppData\Local\Arduino15\packages\rp2040\hardware\rp2040\3.0.0\cores\rp2040/api/ArduinoAPI.h:2,
from C:\Users\samue\AppData\Local\Arduino15\packages\rp2040\hardware\rp2040\3.0.0\cores\rp2040/Arduino.h:28,
from C:\Users\samue\AppData\Local\Temp\arduino\sketches\C583811E80838008550356A24ED744EB\sketch\sketch_mar4a.ino.cpp:1:
c:\users\samue\appdata\local\arduino15\packages\rp2040\hardware\rp2040\3.0.0\arduinocore-api\api\string.h:44:7: note: candidates are: 'class arduino::__FlashStringHelper'
44 | class __FlashStringHelper;
| ^~~~~~~~~~~~~~~~~~~
In file included from c:\Users\samue\OneDrive\Dokumente\Arduino\libraries\StreamUtils\src/StreamUtils.hpp:22,
from c:\Users\samue\OneDrive\Dokumente\Arduino\libraries\StreamUtils\src/StreamUtils.h:5,
from C:\Users\samue\AppData\Local\Temp.arduinoIDE-unsaved202324-14884-conkzs.vg2bl\sketch_mar4a\sketch_mar4a.ino:1:
c:\Users\samue\OneDrive\Dokumente\Arduino\libraries\StreamUtils\src/StreamUtils/Streams/ProgmemStream.hpp:11:7: note: 'class __FlashStringHelper'
11 | class __FlashStringHelper;
| ^~~~~~~~~~~~~~~~~~~

exit status 1

Compilation error: exit status 1`

Feature request: Calculate statistics on a (wrapped) stream

Could be interesting to be able to grab some stats of the stream usage:

  • Total TX/RX bytes count
  • Total TX/RX packets count (writes)
  • Min/Max/Average read/write size

The only difficulty I see here: Some functions (i.e. readBytes) could be implemented via read() on some streams, but it can also be implemented directly by other implementations. Not sure if there's a clean way to handle it.

size_t readBytes() marked 'override', but does not override on ESP32

I'm trying to get this to work to debug another part of my code but it won't compile.

I'm using Platformio and these dependencies:
ArduinoJson@~6.17.1
Adafruit GFX Library@~1.6.1
Timezone@~1.2.2
AsyncMqttClient@~0.8.2
ESP Async WebServer@~1.2.0
NTPClient@~3.1.0
PathVariableHandlers@~2.0.0
RichHttpServer@~2.0.0
DNSServer@~1.1.0
Bleeper@~1.0.4
ArduinoMSGraph@^0.2.0
StreamUtils@^1.4.1
AsyncTCP@~1.1.1
BatteryLevel=https://github.com/dobrishinov/18650CL.git
Time=https://github.com/xoseperez/Time#ecb2bb1
WiFiManager=https://github.com/sidoh/WiFiManager#async_support
GxEPD2=https://github.com/ZinggJM/GxEPD2#1.3.8

During compilation I get the following error:
In file included from .pio/libdeps/esp32/StreamUtils/src/StreamUtils/Clients/HammingClient.hpp:11:0, from .pio/libdeps/esp32/StreamUtils/src/StreamUtils.hpp:5, from .pio/libdeps/esp32/StreamUtils/src/StreamUtils.h:5, from .pio/libdeps/esp32/ArduinoMSGraph/src/ArduinoMSGraph.cpp:14: .pio/libdeps/esp32/StreamUtils/src/StreamUtils/Clients/ClientProxy.hpp: In instantiation of 'class StreamUtils::ClientProxy<StreamUtils::ReadLoggingPolicy, StreamUtils::WriteLoggingPolicy, StreamUtils::ConnectForwardingPolicy>': .pio/libdeps/esp32/StreamUtils/src/StreamUtils/Clients/LoggingClient.hpp:14:24: required from here .pio/libdeps/esp32/StreamUtils/src/StreamUtils/Clients/ClientProxy.hpp:69:10: error: 'size_t StreamUtils::ClientProxy<ReadPolicy, WritePolicy, ConnectPolicy>::readBytes(char*, size_t) [with ReadPolicy = StreamUtils::ReadLoggingPolicy; WritePolicy = StreamUtils::WriteLoggingPolicy; ConnectPolicy = StreamUtils::ConnectForwardingPolicy; size_t = unsigned int]' marked 'override', but does not override size_t readBytes(char *buffer, size_t size) override { ^ .pio/libdeps/esp32/StreamUtils/src/StreamUtils/Clients/ClientProxy.hpp: In instantiation of 'class StreamUtils::ClientProxy<StreamUtils::ReadLoggingPolicy, StreamUtils::WriteForwardingPolicy, StreamUtils::ConnectForwardingPolicy>': .pio/libdeps/esp32/StreamUtils/src/StreamUtils/Clients/ReadLoggingClient.hpp:14:28: required from here .pio/libdeps/esp32/StreamUtils/src/StreamUtils/Clients/ClientProxy.hpp:69:10: error: 'size_t StreamUtils::ClientProxy<ReadPolicy, WritePolicy, ConnectPolicy>::readBytes(char*, size_t) [with ReadPolicy = StreamUtils::ReadLoggingPolicy; WritePolicy = StreamUtils::WriteForwardingPolicy; ConnectPolicy = StreamUtils::ConnectForwardingPolicy; size_t = unsigned int]' marked 'override', but does not override .pio/libdeps/esp32/StreamUtils/src/StreamUtils/Clients/ClientProxy.hpp: In instantiation of 'class StreamUtils::ClientProxy<StreamUtils::ReadForwardingPolicy, StreamUtils::WriteLoggingPolicy, StreamUtils::ConnectForwardingPolicy>': .pio/libdeps/esp32/StreamUtils/src/StreamUtils/Clients/WriteLoggingClient.hpp:15:7: required from here .pio/libdeps/esp32/StreamUtils/src/StreamUtils/Clients/ClientProxy.hpp:69:10: error: 'size_t StreamUtils::ClientProxy<ReadPolicy, WritePolicy, ConnectPolicy>::readBytes(char*, size_t) [with ReadPolicy = StreamUtils::ReadForwardingPolicy; WritePolicy = StreamUtils::WriteLoggingPolicy; ConnectPolicy = StreamUtils::ConnectForwardingPolicy; size_t = unsigned int]' marked 'override', but does not override In file included from .pio/libdeps/esp32/StreamUtils/src/StreamUtils/Clients/ClientProxy.hpp:10:0, from .pio/libdeps/esp32/StreamUtils/src/StreamUtils/Clients/HammingClient.hpp:11, from .pio/libdeps/esp32/StreamUtils/src/StreamUtils.hpp:5, from .pio/libdeps/esp32/StreamUtils/src/StreamUtils.h:5, from .pio/libdeps/esp32/ArduinoMSGraph/src/ArduinoMSGraph.cpp:14: .pio/libdeps/esp32/StreamUtils/src/StreamUtils/Clients/../Streams/StreamProxy.hpp: In instantiation of 'class StreamUtils::StreamProxy<StreamUtils::ReadLoggingPolicy, StreamUtils::WriteLoggingPolicy>': .pio/libdeps/esp32/StreamUtils/src/StreamUtils/Streams/LoggingStream.hpp:13:24: required from here .pio/libdeps/esp32/StreamUtils/src/StreamUtils/Clients/../Streams/StreamProxy.hpp:62:10: error: 'size_t StreamUtils::StreamProxy<ReadPolicy, WritePolicy>::readBytes(char*, size_t) [with ReadPolicy = StreamUtils::ReadLoggingPolicy; WritePolicy = StreamUtils::WriteLoggingPolicy; size_t = unsigned int]' marked 'override', but does not override size_t readBytes(char *buffer, size_t size) override { ^ In file included from .pio/libdeps/esp32/StreamUtils/src/StreamUtils.hpp:22:0, from .pio/libdeps/esp32/StreamUtils/src/StreamUtils.h:5, from .pio/libdeps/esp32/ArduinoMSGraph/src/ArduinoMSGraph.cpp:14: .pio/libdeps/esp32/StreamUtils/src/StreamUtils/Streams/ProgmemStream.hpp:35:10: error: 'size_t StreamUtils::ProgmemStream::readBytes(char*, size_t)' marked 'override', but does not override size_t readBytes(char* buffer, size_t size) override { ^ In file included from .pio/libdeps/esp32/StreamUtils/src/StreamUtils/Clients/ClientProxy.hpp:10:0, from .pio/libdeps/esp32/StreamUtils/src/StreamUtils/Clients/HammingClient.hpp:11, from .pio/libdeps/esp32/StreamUtils/src/StreamUtils.hpp:5, from .pio/libdeps/esp32/StreamUtils/src/StreamUtils.h:5, from .pio/libdeps/esp32/ArduinoMSGraph/src/ArduinoMSGraph.cpp:14: .pio/libdeps/esp32/StreamUtils/src/StreamUtils/Clients/../Streams/StreamProxy.hpp: In instantiation of 'class StreamUtils::StreamProxy<StreamUtils::ReadLoggingPolicy, StreamUtils::WriteForwardingPolicy>': .pio/libdeps/esp32/StreamUtils/src/StreamUtils/Streams/ReadLoggingStream.hpp:14:7: required from here .pio/libdeps/esp32/StreamUtils/src/StreamUtils/Clients/../Streams/StreamProxy.hpp:62:10: error: 'size_t StreamUtils::StreamProxy<ReadPolicy, WritePolicy>::readBytes(char*, size_t) [with ReadPolicy = StreamUtils::ReadLoggingPolicy; WritePolicy = StreamUtils::WriteForwardingPolicy; size_t = unsigned int]' marked 'override', but does not override size_t readBytes(char *buffer, size_t size) override { ^ In file included from .pio/libdeps/esp32/StreamUtils/src/StreamUtils.hpp:26:0, from .pio/libdeps/esp32/StreamUtils/src/StreamUtils.h:5, Compiling .pio\build\esp32\lib334\GxEPD2\epd3c\GxEPD2_213c.cpp.o from .pio/libdeps/esp32/ArduinoMSGraph/src/ArduinoMSGraph.cpp:14: .pio/libdeps/esp32/StreamUtils/src/StreamUtils/Streams/StringStream.hpp:64:10: error: 'size_t StreamUtils::StringStream::readBytes(char*, size_t)' marked 'override', but does not override size_t readBytes(char* buffer, size_t length) override { ^ In file included from .pio/libdeps/esp32/StreamUtils/src/StreamUtils/Clients/ClientProxy.hpp:10:0, from .pio/libdeps/esp32/StreamUtils/src/StreamUtils/Clients/HammingClient.hpp:11, from .pio/libdeps/esp32/StreamUtils/src/StreamUtils.hpp:5, from .pio/libdeps/esp32/StreamUtils/src/StreamUtils.h:5, from .pio/libdeps/esp32/ArduinoMSGraph/src/ArduinoMSGraph.cpp:14: .pio/libdeps/esp32/StreamUtils/src/StreamUtils/Clients/../Streams/StreamProxy.hpp: In instantiation of 'class StreamUtils::StreamProxy<StreamUtils::ReadForwardingPolicy, StreamUtils::WriteLoggingPolicy>': .pio/libdeps/esp32/StreamUtils/src/StreamUtils/Streams/WriteLoggingStream.hpp:14:7: required from here .pio/libdeps/esp32/StreamUtils/src/StreamUtils/Clients/../Streams/StreamProxy.hpp:62:10: error: 'size_t StreamUtils::StreamProxy<ReadPolicy, WritePolicy>::readBytes(char*, size_t) [with ReadPolicy = StreamUtils::ReadForwardingPolicy; WritePolicy = StreamUtils::WriteLoggingPolicy; size_t = unsigned int]' marked 'override', but does not override size_t readBytes(char *buffer, size_t size) override { ^ .pio/libdeps/esp32/StreamUtils/src/StreamUtils/Clients/../Streams/StreamProxy.hpp: In instantiation of 'class StreamUtils::StreamProxy<StreamUtils::ReadForwardingPolicy, StreamUtils::WriteWaitingPolicy>': .pio/libdeps/esp32/StreamUtils/src/StreamUtils/Streams/WriteWaitingStream.hpp:14:7: required from here .pio/libdeps/esp32/StreamUtils/src/StreamUtils/Clients/../Streams/StreamProxy.hpp:62:10: error: 'size_t StreamUtils::StreamProxy<ReadPolicy, WritePolicy>::readBytes(char*, size_t) [with ReadPolicy = StreamUtils::ReadForwardingPolicy; WritePolicy = StreamUtils::WriteWaitingPolicy; size_t = unsigned int]' marked 'override', but does not override *** [.pio\build\esp32\libe7b\ArduinoMSGraph\ArduinoMSGraph.cpp.o] Error 1

I'm only using it here.
ReadLoggingStream loggingStream(https.getStream(), Serial); DeserializationError error = deserializeJson(responseDoc, loggingStream);

If you need any more information let me know.

Compatibility nrf52840

Hi Guys

someone has already tested compatibility with the nrf52840, or the nrf52x family

Thanks in advance for any replies

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.