Skip to content

Commit

Permalink
Multi-threading and multi-segment support (#10)
Browse files Browse the repository at this point in the history
  • Loading branch information
awawa-dev authored Dec 12, 2022
1 parent ca6997c commit 570993a
Show file tree
Hide file tree
Showing 16 changed files with 2,574 additions and 597 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/push-master.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: HyperSerialEsp8266 CI Build
name: HyperSerialESP32 CI Build

on: [push]

Expand Down
5 changes: 5 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"files.associations": {
"algorithm": "cpp"
}
}
63 changes: 54 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,37 +24,82 @@ Why the data integrity check was introduced which causes incompatibility with ot

Recommend to use [esphome-flasher](https://github.com/esphome/esphome-flasher/releases)

For **RGBW LED strip** like RGBW SK6812 NEUTRAL white choose: *firmware_SK6812_RGBW_NEUTRAL.bin*
ESP32-S2 lolin mini requires special firmware version (also provided)

Generic ESP32:

For **RGBW LED strip** like RGBW SK6812 NEUTRAL white choose: *firmware_esp32_SK6812_RGBW_NEUTRAL.bin*

For **RGBW LED strip** like RGBW SK6812 COLD white choose: *firmware_SK6812_RGBW_COLD.bin*
For **RGBW LED strip** like RGBW SK6812 COLD white choose: *firmware_esp32_SK6812_RGBW_COLD.bin*

For **RGB LED strip** like WS8212b or RGB SK6812 variant choose: *firmware_WS281x_RGB.bin*
For **RGB LED strip** like WS8212b or RGB SK6812 variant choose: *firmware_esp32_WS281x_RGB.bin*

For **SPI driven RGB LED strip** APA102: *firmware_SPI_APA102_SK9822_HD107.bin*, WS8201: *firmware_SPI_WS2801.bin*
For **SPI driven RGB LED strip** APA102: *firmware_esp32_SPI_APA102_SK9822_HD107.bin*, WS8201: *firmware_esp32_SPI_WS2801.bin*

If you want to disable your first LED because it's used as a sacrificial level shifter, please use [HyperHDR v19](https://github.com/awawa-dev/HyperHDR/pull/379)

For the RGBW firmware the white channel is automatically calculated and R,G,B channels are corrected.

# Usage in HyperHDR

Make sure you set "Refresh time" to zero, "Baudrate" to 2000000 and enabled HyperHDR's AWA protocol.
Enabling "White channel calibration" is optional, if you want to fine tune the white channel balance of your sk6812 RGBW LED strip.
Make sure you are using HyperHDR v19beta2 or above.
Set `Refresh time` to zero, `Baudrate` to 2000000 and you enabled `HyperHDR's AWA protocol`.
Enabling `White channel calibration` is optional, if you want to fine tune the white channel balance of your sk6812 RGBW LED strip.
`ESP8266/ESP32 handshake` could help you to properly initialize the ESP device and enables statistics available in the logs (you must stop the LED device first to get them).

![obraz](https://user-images.githubusercontent.com/69086569/207109594-0493fe58-3530-46bb-a0a3-31a110475ed6.png)

![obraz](https://user-images.githubusercontent.com/69086569/192893595-324cfcf8-e247-438c-88ce-e52a29463121.png)

# Compiling

Currently we use PlatformIO to compile the project. Install [Visual Studio Code](https://code.visualstudio.com/) and add [PlatformIO plugin](https://platformio.org/).
This environment will take care of everything and compile the firmware for you.

But there is also an alternative and an easier way. Just fork the project and enable its Github Action. Use the online editor to make changes to the ```platformio.ini``` file, for example change default pin-outs, and save it. Github Action will compile new firmware automatically in the Artifacts archive. It has never been so easy!
But there is also an alternative and an easier way. Just fork the project and enable its Github Action. Use the online editor to make changes to the ```platformio.ini``` file, for example change default pin-outs/speed or enable multi-segments support, and save it. Github Action will compile new firmware automatically in the Artifacts archive. It has never been so easy!

Tutorial: https://github.com/awawa-dev/HyperSerialESP32/wiki

# Pinout

**ESP32:**
**LED output (non-SPI):** GPIO 2
**LED output (SPI):** GPIO 0 for Clock, GPIO 2 for Data
**LED output (SPI):** GPIO 4 for Clock, GPIO 2 for Data

# Some benchmark results

ESP32 MH-ET LIVE mini is capable of 4Mb serial port speed and ESP32-S2 lolin mini is capable of 5Mb. But to give equal chances all models were tested using the default speed of 2Mb.

## Multi-segments can double your large sk6812/ws2812b setup refresh rate for free. All you need is to properly project & construct the LED strip and use HyperSerialESP32 v8.

| LED strip / Device | ESP32<br>MH-ET LIVE mini |
|----------------------------------------------------------------------------------|--------------------------|
| 300LEDs<br>Refresh rate/continues output=100Hz<br>SECOND_SEGMENT_START_INDEX=150 | 93-97 |
| 600LEDs<br>Refresh rate/continues output=100Hz<br>SECOND_SEGMENT_START_INDEX=300 | 78-79 |
| 900LEDs<br>Refresh rate/continues output=100Hz<br>SECOND_SEGMENT_START_INDEX=450 | 55-56 |

## Comparing v6.1 and v8 version (single segment) refresh rate using MH-ET LIVE mini

| LED strip / Device | ESP32<br>MH-ET LIVE mini<br>HyperSerialESP32 v6.1 | ESP32<br>MH-ET LIVE mini<br>HyperSerialESP32 v8 |
|------------------------------------------------|---------------------------------------------------|-------------------------------------------------|
| 300LEDs<br>Refresh rate/continues output=100Hz | 81-83 | 80-83 |
| 600LEDs<br>Refresh rate/continues output=60Hz | 39-40 | 41-42 |
| 900LEDs<br>Refresh rate/continues output=40Hz | 21-26 | 26-28 |

## Comparing v6.1 and v8 version (single segment) refresh rate using generic ESP32 with CH340C

| LED strip / Device | ESP32 (CH340C)<br>HyperSerialESP32 v6.1 | ESP32 (CH340C)<br>HyperSerialESP32 v8 |
|------------------------------------------------|-----------------------------------------|---------------------------------------|
| 300LEDs<br>Refresh rate/continues output=100Hz | 72-78 | 81-83 |
| 600LEDs<br>Refresh rate/continues output=60Hz | 33-38 | 39-42 |
| 900LEDs<br>Refresh rate/continues output=40Hz | 21-25 | 26-28 |

## ESP32-S2 lolin mini performance

| LED strip / Device | ESP32-S2 lolin mini<br>HyperSerialESP32 v8 |
|------------------------------------------------|--------------------------------------------|
| 300LEDs<br>Refresh rate/continues output=100Hz | 80-84 |
| 600LEDs<br>Refresh rate/continues output=60Hz | 42 |
| 900LEDs<br>Refresh rate/continues output=40Hz | 27-28 |

# Disclaimer

Expand Down
39 changes: 0 additions & 39 deletions include/README

This file was deleted.

174 changes: 174 additions & 0 deletions include/base.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
/* base.h
*
* MIT License
*
* Copyright (c) 2022 awawa-dev
*
* https://github.com/awawa-dev/HyperSerialESP32
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

#ifndef BASE_H
#define BASE_H

#if defined(SECOND_SEGMENT_START_INDEX)
#if !defined(SECOND_SEGMENT_DATA_PIN)
#error "Please define SECOND_SEGMENT_DATA_PIN for second segment"
#elif !defined(SECOND_SEGMENT_CLOCK_PIN) && !defined(NEOPIXEL_RGBW) && !defined(NEOPIXEL_RGB)
#error "Please define SECOND_SEGMENT_CLOCK_PIN and SECOND_SEGMENT_DATA_PIN for second segment"
#endif
#endif

class Base
{
// LED strip number
int ledsNumber = 0;
// NeoPixelBusLibrary primary object
LED_DRIVER* ledStrip1 = nullptr;
// NeoPixelBusLibrary second object
LED_DRIVER2* ledStrip2 = nullptr;
// frame is set and ready to render
bool readyToRender = false;

public:
// static data buffer for the loop
uint8_t buffer[MAX_BUFFER];
// handle to tasks
TaskHandle_t processTaskHandle;
// current queue position
uint16_t queueCurrent = 0;
// queue end position
volatile uint16_t queueEnd = 0;

inline int getLedsNumber()
{
return ledsNumber;
}

inline LED_DRIVER* getLedStrip1()
{
return ledStrip1;
}

inline LED_DRIVER2* getLedStrip2()
{
return ledStrip2;
}

void initLedStrip(int count)
{
if (ledStrip1 != nullptr)
{
delete ledStrip1;
ledStrip1 = nullptr;
}

if (ledStrip2 != nullptr)
{
delete ledStrip2;
ledStrip2 = nullptr;
}

ledsNumber = count;

#if defined(SECOND_SEGMENT_START_INDEX)
if (ledsNumber > SECOND_SEGMENT_START_INDEX)
{
#if defined(NEOPIXEL_RGBW) || defined(NEOPIXEL_RGB)
ledStrip1 = new LED_DRIVER(SECOND_SEGMENT_START_INDEX, DATA_PIN);
ledStrip1->Begin();
ledStrip2 = new LED_DRIVER2(ledsNumber - SECOND_SEGMENT_START_INDEX, SECOND_SEGMENT_DATA_PIN);
ledStrip2->Begin();
#else
ledStrip1 = new LED_DRIVER(SECOND_SEGMENT_START_INDEX);
ledStrip1->Begin(CLOCK_PIN, 12, DATA_PIN, 15);
ledStrip2 = new LED_DRIVER2(ledsNumber - SECOND_SEGMENT_START_INDEX);
ledStrip2->Begin(SECOND_SEGMENT_CLOCK_PIN, 12, SECOND_SEGMENT_DATA_PIN, 15);
#endif
}
#endif

if (ledStrip1 == nullptr)
{
#if defined(NEOPIXEL_RGBW) || defined(NEOPIXEL_RGB)
ledStrip1 = new LED_DRIVER(ledsNumber, DATA_PIN);
ledStrip1->Begin();
#else
ledStrip1 = new LED_DRIVER(ledsNumber);
ledStrip1->Begin(CLOCK_PIN, 12, DATA_PIN, 15);
#endif
}
}

/**
* @brief Check if there is already prepared frame to display
*
* @return true
* @return false
*/
inline bool hasLateFrameToRender()
{
return readyToRender;
}

inline void renderLeds(bool newFrame)
{
if (newFrame)
readyToRender = true;

if (readyToRender &&
(ledStrip1 != nullptr && ledStrip1->CanShow()) &&
!(ledStrip2 != nullptr && !ledStrip2->CanShow()))
{
statistics.increaseShow();
readyToRender = false;

// display segments
ledStrip1->Show(false);
if (ledStrip2 != nullptr)
ledStrip2->Show(false);
}
}

inline bool setStripPixel(uint16_t pix, ColorDefinition &inputColor)
{
if (pix < ledsNumber)
{
#if defined(SECOND_SEGMENT_START_INDEX)
if (pix < SECOND_SEGMENT_START_INDEX)
ledStrip1->SetPixelColor(pix, inputColor);
else
{
#if defined(SECOND_SEGMENT_REVERSED)
ledStrip2->SetPixelColor(ledsNumber - pix - 1, inputColor);
#else
ledStrip2->SetPixelColor(pix - SECOND_SEGMENT_START_INDEX, inputColor);
#endif
}
#else
ledStrip1->SetPixelColor(pix, inputColor);
#endif
}

return (pix + 1 < ledsNumber);
}
} base;

#endif
Loading

0 comments on commit 570993a

Please sign in to comment.