From 230487b82a1635b9b5c5c5dfcc6c22b75c5e8b69 Mon Sep 17 00:00:00 2001 From: Kinsey Moore Date: Mon, 26 Feb 2018 23:12:19 -0600 Subject: [PATCH] Use dynamic buffer instead of static buffer This removes the requirement to modify the aJson library to get a larger buffer. The default method allocates a buffer for rendering JSON dynamically based on output size. A new method has also been added for applications with less available SRAM (setBufferlessResponses). This requires no large fixed or dynamic buffer, but increases response rendering time by close to two orders of magnitude due to inefficiencies and overhead in the WiFiClient and aJson. With the following PR in place, this is reduced to an order of magnitude difference (50ms vs 400ms for /api): https://github.com/interactive-matter/aJson/pull/91 --- ESP8266HueEmulator/ESP8266HueEmulator.ino | 1 + ESP8266HueEmulator/LightService.cpp | 101 +++++++++++++++++++--- ESP8266HueEmulator/LightService.h | 1 + README.md | 2 - 4 files changed, 91 insertions(+), 14 deletions(-) diff --git a/ESP8266HueEmulator/ESP8266HueEmulator.ino b/ESP8266HueEmulator/ESP8266HueEmulator.ino index e474717..bc13aec 100644 --- a/ESP8266HueEmulator/ESP8266HueEmulator.ino +++ b/ESP8266HueEmulator/ESP8266HueEmulator.ino @@ -171,6 +171,7 @@ void setup() { digitalWrite(LED_BUILTIN, HIGH); // Turn the LED off by making the voltage HIGH LightService.begin(); + //LightService.setBufferlessResponses(true); // setup pixels as lights for (int i = 0; i < MAX_LIGHT_HANDLERS && i < pixelCount; i++) { diff --git a/ESP8266HueEmulator/LightService.cpp b/ESP8266HueEmulator/LightService.cpp index 0feb4a2..6fcc64e 100644 --- a/ESP8266HueEmulator/LightService.cpp +++ b/ESP8266HueEmulator/LightService.cpp @@ -9,10 +9,6 @@ #include #include -#if PRINT_BUFFER_LEN < 4096 -# error aJson print buffer length PRINT_BUFFER_LEN must be increased to at least 4096 -#endif - String macString; String bridgeIDString; String ipString; @@ -966,15 +962,96 @@ void LightServiceClass::update() { HTTP->handleClient(); } -void sendJson(aJsonObject *root) { - // Take aJsonObject and print it to Serial and to WiFi - // From https://github.com/pubnub/msp430f5529/blob/master/msp430f5529.ino - char *msgStr = aJson.print(root); - aJson.deleteItem(root); +bool bufferlessResponses = false; +void LightServiceClass::setBufferlessResponses(bool enabled) { + bufferlessResponses = enabled; +} + +#include "Stream.h" +class WebServerStream : public Stream { + public: + WebServerStream() {} + ~WebServerStream() {} + size_t write(uint8_t ch) override { + HTTP->client().write(&ch, 1); + return 1; + } + size_t write(const uint8_t *str, size_t size) override { + HTTP->client().write(str, size); + return 1; + } + int available(void) override { return 1; } + int peek(void) override { return 1; } + int read(void) override { return 1; } + int availableForWrite(void) { return 1; } + void flush(void) override {} +}; + +class StringStream : public Stream { + public: + StringStream(String& buf): buf(buf) {} + ~StringStream() {} + size_t write(uint8_t ch) override { + buf += (char)ch; + return 1; + } + int available(void) override { return 1; } + int peek(void) override { return 1; } + int read(void) override { return 1; } + int availableForWrite(void) { return 1; } + void flush(void) override {} + private: + String &buf; +}; + +class CountStream : public Stream { + public: + CountStream() {} + ~CountStream() {} + size_t write(uint8_t ch) override { + count++; + return 1; + } + int available(void) override { return 1; } + int peek(void) override { return 1; } + int read(void) override { return 1; } + int availableForWrite(void) { return 1; } + void flush(void) override {} + long count = 0; +}; + +WebServerStream *WebStream = new WebServerStream(); +CountStream *CStream = new CountStream(); +aJsonStream serial_stream(&Serial); +aJsonStream web_stream(WebStream); +aJsonStream count_stream(CStream); +void sendJson(aJsonObject *root) +{ Serial.println(millis()); - Serial.println(msgStr); - HTTP->send(200, "application/json", msgStr); - free(msgStr); + + if (bufferlessResponses) { + CStream->count = 0; + aJson.print(root, &count_stream); + HTTP->setContentLength(CStream->count+1); + HTTP->send(200, "application/json", ""); + aJson.print(root, &web_stream); + HTTP->sendContent("\n"); + } else { + CStream->count = 0; + aJson.print(root, &count_stream); + String outstr; + outstr.reserve(CStream->count+1); + StringStream *SStream = new StringStream(outstr); + aJsonStream string_stream(SStream); + aJson.print(root, &string_stream); + outstr += '\n'; + HTTP->send(200, "application/json", outstr); + } + + Serial.println(millis()); + aJson.print(root, &serial_stream); + Serial.println(); + aJson.deleteItem(root); } // ============================================================================================================== diff --git a/ESP8266HueEmulator/LightService.h b/ESP8266HueEmulator/LightService.h index 3eb19a6..3bb2bbb 100644 --- a/ESP8266HueEmulator/LightService.h +++ b/ESP8266HueEmulator/LightService.h @@ -59,6 +59,7 @@ class LightServiceClass { bool setLightsAvailable(int numLights); int getLightsAvailable(); bool setLightHandler(int index, LightHandler *handler); + void setBufferlessResponses(bool enabled); void begin(); void begin(ESP8266WebServer *svr); void update(); diff --git a/README.md b/README.md index e907b1d..41ffd16 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,6 @@ Please note that currently only the bare minimum to advertise the emulated Hue b * I used [Arduino-1.6.11.hourly201608161225.esp497d19d-x86_64.AppImage](https://bintray.com/probono/AppImages/Arduino#files) which conveniently comes with both the Arduino IDE and esp8266/Arduino ready-to-use; otherwise use a recent version of the Arduino IDE and [esp8266/Arduino](https://github.com/esp8266/Arduino) * In the Arduino IDE, Open the Library Manager and search for "NeoPixelBus by Makuna" and install * Download https://github.com/interactive-matter/aJson/archive/master.zip and install the library via the Arduino IDE -* Important: Change `#define PRINT_BUFFER_LEN 256` to `#define PRINT_BUFFER_LEN 4096` in `aJson/aJSON.h` * Edit the sketch to contain your WLAN credentials * Load the sketch onto your ESP-01 or other ESP8266 device * Optionally connect the DATA line of your WS2812b NeoPixels to pin GPIO2 (you do not really need this in order to test communication between the sketch and Hue client apps) @@ -36,7 +35,6 @@ git clone --branch 2.1.4 https://github.com/Makuna/NeoPixelBus.git git clone https://github.com/interactive-matter/aJson.git git clone https://github.com/PaulStoffregen/Time.git git clone https://github.com/gmag11/NtpClient.git -sed -i -e 's|#define PRINT_BUFFER_LEN 256|#define PRINT_BUFFER_LEN 4096|g' aJson/aJSON.h cd - git clone https://github.com/probonopd/ESP8266HueEmulator.git sed -i -e 's|#include "/secrets.h"|//#include "/secrets.h"|g' ESP8266HueEmulator/ESP8266HueEmulator/ESP8266HueEmulator.ino