├── .github
├── FUNDING.yml
└── workflows
│ └── push-master.yml
├── .gitignore
├── .vscode
└── extensions.json
├── LICENSE
├── MakunaNeoPixelBusLicence.txt
├── README.md
├── extra_script.py
├── include
├── base.h
├── calibration.h
├── framestate.h
├── main.h
└── statistics.h
├── lib
└── README
├── platformio.ini
├── src
└── main.cpp
└── test
└── test_SingleSegment
└── main.cpp
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: awawa-dev
2 |
--------------------------------------------------------------------------------
/.github/workflows/push-master.yml:
--------------------------------------------------------------------------------
1 | name: HyperSerialEsp8266 CI Build
2 |
3 | on: [push]
4 |
5 | jobs:
6 |
7 | ######################
8 | #### PlatformIO ######
9 | ######################
10 |
11 | PlatformIO:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v3
15 | - name: Cache pip
16 | uses: actions/cache@v3
17 | with:
18 | path: ~/.cache/pip
19 | key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
20 | restore-keys: |
21 | ${{ runner.os }}-pip-
22 | - name: Cache PlatformIO
23 | uses: actions/cache@v3
24 | with:
25 | path: ~/.platformio
26 | key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}
27 | - name: Set up Python
28 | uses: actions/setup-python@v4
29 | with:
30 | python-version: 3.x
31 | - name: Install PlatformIO
32 | run: |
33 | python -m pip install --upgrade pip
34 | pip install --upgrade platformio
35 | - name: Run PlatformIO
36 | run: pio run
37 | - uses: actions/upload-artifact@v3
38 | name: Upload artifacts (commit)
39 | if: (startsWith(github.event.ref, 'refs/tags') != true)
40 | with:
41 | path: |
42 | .pio/build/*.bin
43 |
44 | - uses: actions/upload-artifact@v3
45 | name: Upload artifacts (release)
46 | if: startsWith(github.ref, 'refs/tags/')
47 | with:
48 | name: firmware-release
49 | path: |
50 | .pio/build/*.bin
51 |
52 | ################################
53 | ###### Publish Releases ########
54 | ################################
55 |
56 | publish:
57 | name: Publish Releases
58 | if: startsWith(github.event.ref, 'refs/tags')
59 | needs: [PlatformIO]
60 | runs-on: ubuntu-latest
61 | steps:
62 | # generate environment variables
63 | - name: Generate environment variables from version and tag
64 | run: |
65 | echo "TAG=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV
66 | echo "preRelease=false" >> $GITHUB_ENV
67 |
68 | # If version contains alpha or beta, mark draft release as pre-release
69 | - name: Mark release as pre-release
70 | if: contains(env.VERSION, 'alpha') || contains(env.VERSION, 'beta')
71 | run: echo "preRelease=true" >> $GITHUB_ENV
72 |
73 | - uses: actions/download-artifact@v3
74 | with:
75 | name: firmware-release
76 |
77 | # create draft release and upload artifacts
78 | - name: Create draft release
79 | uses: softprops/action-gh-release@v1
80 | with:
81 | name: HyperSerialEsp8266 ${{ env.VERSION }}
82 | tag_name: ${{ env.TAG }}
83 | files: |
84 | *.bin
85 | draft: true
86 | prerelease: ${{ env.preRelease }}
87 | env:
88 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .pio
2 | .vscode/.browse.c_cpp.db*
3 | .vscode/c_cpp_properties.json
4 | .vscode/launch.json
5 | .vscode/ipch
6 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | // See http://go.microsoft.com/fwlink/?LinkId=827846
3 | // for the documentation about the extensions.json format
4 | "recommendations": [
5 | "platformio.platformio-ide"
6 | ],
7 | "unwantedRecommendations": [
8 | "ms-vscode.cpptools-extension-pack"
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020-2025 awawa-dev
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/MakunaNeoPixelBusLicence.txt:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # HyperSerialEsp8266
2 |
3 | Exposes USB high speed serial port at 2Mb baud for driving led strip using NeoPixelBus library. It's intended to replace slow Arduino solutions (level shifter 3.3V to 5v may be required). **Data integrity check (Fletcher's checksum) included in new 'Awa' protocol for HyperHDR. So no more random flashing caused by serial transmission errors.** That option must be checked in HyperHDR to make system works as on the screen below.
4 |
5 | Make sure that your serial chip on the ESP8266 can handle 2Mb speed: for example CP2102 can't as its max speed is 1Mb and you can compile a version for that speed but I think that's not the point, cheap CH340G can do it without any problems. CH9102x also should work for you, even at 4Mb speed.
6 |
7 | There is also my new fork named HyperSerialWLED available with the support for the AWA serial protocol at @2Mb speed for both ESP8266 and ESP32: https://github.com/awawa-dev/HyperSerialWLED Can't guarantee it will work as stable as HyperSerialEsp8266 because WLED has a lot of other things to do in the backgrounds (ex. handling Wifi) and timing control for the serial port could be at danger for larger number of LEDs. But you don't need to abandon all the benefits offered by the WLED which can be a big advantage for some users.
8 |
9 | Tested with SK6812 RGBW and cheap 1.75$ noname clone of Wemos ESP8266 d1 mini CH340G. The data output is on the GPIO2 (D4 pin for Wemos mini d1), just like WLED for example. Should work with 3-channel RGB led system like WS8212b also.
10 |
11 | RGB to RGBW conversion is calibrated for the neutral white channel BTF SK6812 but it can be easily changed (for cool and warm temperature) in the code. Search for "color calibration". In HyperHDR use "1.5" gamma for red, blue and green for best effect in the "Image Processing" tab.
12 |
13 | | LED strip / Device | HyperSerialEsp8266 |
14 | |--------------------------------|:------------------:|
15 | | SK6812 cold white | yes |
16 | | SK6812 neutral white | yes |
17 | | WS281x | yes |
18 | | SPI (APA102, SK9812, HD107...) | yes |
19 |
20 | # Example of supported boards
21 |
22 | **Esp8266 Wemos D1 mini (CH340) and Wemos D1 mini pro (CP2104)**
23 |
24 | 
25 |
26 |
27 | # Data integrity check
28 |
29 | Why the data integrity check was introduced which causes incompatibility with other software? Because at 2Mb speed many chip-makers allow few percent error in the transmission. And we do not want to have any distracting flashes. Broken frames are abandon without showing them. At 100Hz for 250 leds approximately 1-5% of the frames are broken.
30 |
31 | # Why not Arduino, Raspberry Pi or WLED
32 |
33 | Because Arduino is slow, really slow even at @500000 baud. And no data integrity checking so random flashing may occure... Increasing serial port speed leads to problems with the LED library as most ATMega are at 16MHz only (for comparision Esp8266 is at 80/160MHz). For over 200leds and RGB channel we have merely ~20 frames. For RGBW is even worse. The only advantage of Arduinos is logic at 5V. For Esp8266 level shifter 3.3V to 5V may be required. You can find example of simply and efficient one here: Level shifter for 3.3V logic level
34 |
35 | Rpi is very powerful device but for the SK6812 and WS2812b protocol timing of transmission is crucial. It's hard to maintain it in the multitasking environment.
36 |
37 | WLED is a brilliant app and it's preffered solution in most cases. But sometimes Wifi is not an option: there is a problem with the signal strenght/stability caused by EM interference or obstacles which results in disturbing effects from the LED strip.You can also try https://github.com/awawa-dev/HyperSerialWLED if you don't want to abandon WLED benefits or you are looking for a version for ESP32.
38 |
39 | **Arduino: 250 RGB leds, 500000 baud, 100 Hz output from HyperHDR, real output for the LED strip is around 20Hz.**
40 | **With such slow hardware driver you don't even need 30FPS from the grabber really:**
41 |
42 |
43 |
44 |
45 | With HyperSerialEsp8266 you can have over 100Hz refresh rate for 250 RGB and over 80HZ for 250 RGBW LED strips.
46 |
47 |
48 |
49 | Statistics are sent to serial port monitor when there is no data incoming. You can read it with any serial port client. This driver is not hiding any information from you.
50 |
51 | # Flashing
52 |
53 | Recommend to use [esphome-flasher](https://github.com/esphome/esphome-flasher/releases)
54 |
55 | For **RGBW LED strip** like RGBW SK6812 NEUTRAL white choose: *firmware_SK6812_RGBW_NEUTRAL.bin*
56 |
57 | For **RGBW LED strip** like RGBW SK6812 COLD white choose: *firmware_SK6812_RGBW_COLD.bin*
58 |
59 | For **RGB LED strip** like WS8212b or RGB SK6812 variant choose: *firmware_WS281x_RGB.bin*
60 |
61 | For **SPI driven RGB LED strip** APA102: *firmware_SPI_APA102_SK9822_HD107.bin*, WS8201: *firmware_SPI_WS2801.bin*
62 |
63 | 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)
64 |
65 | For the RGBW firmware the white channel is automatically calculated and R,G,B channels are corrected.
66 |
67 | Using esphome-flasher:
68 |
69 |
70 | # Usage in HyperHDR
71 |
72 | Set `Refresh time` to zero, `Baudrate` to 2000000 and you enabled `HyperHDR's AWA protocol`.
73 | Enabling `White channel calibration` is optional, if you want to fine tune the white channel balance of your sk6812 RGBW LED strip.
74 | `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).
75 |
76 | 
77 |
78 | # Benchmarks
79 |
80 |
81 | **Refresh rate depending on requested refresh rate/LED strip length**:
82 | | RGBW LED strip / Device | WeMos D1 Mini Pro (CP2104)
HyperSerialEsp8266 v8 |
83 | |------------------------------------------------|-----------------------------------------|
84 | | 300LEDs
Refresh rate/continues output=100Hz | 71-75 |
85 | | 600LEDs
Refresh rate/continues output=60Hz | 34-35 |
86 | | 900LEDs
Refresh rate/continues output=40Hz | 23 |
87 |
88 |
89 | **Logic level analyzer, RGB (250 leds, 100Hz)**
90 |
91 |
92 |
93 | **Logic level analyzer, RGBW (250 leds, 100Hz)**
94 |
95 |
96 | # Compiling
97 |
98 | Currently we use PlatformIO to compile the project. Install [Visual Studio Code](https://code.visualstudio.com/) and add [PlatformIO plugin](https://platformio.org/).
99 | This environment will take care of everything and compile the firmware for you. Low-level LED strip support is provided by the https://github.com/Makuna/NeoPixelBus library.
100 |
101 | 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 and save it. Github Action will compile new firmware automatically in the Artifacts archive. It has never been so easy!
102 |
103 | Tutorial: https://github.com/awawa-dev/HyperSerialEsp8266/wiki
104 |
105 | # Pinout
106 |
107 | ESP8266:
108 | For non-SPI LED strip data pin is GPIO2 (usually D4).
109 | For SPI LED strip is: GPIO13 (MOSI, usually D7) and GPIO14 (SCLK/CLOCK, usually D5).
110 |
111 | # Disclaimer
112 |
113 | You use it on your own risk.
114 | Don't touch these firmwares if you don't know how to put the device in the programming mode if something goes wrong.
115 | As per the MIT license, I assume no liability for any damage to you or any other person or equipment.
116 |
--------------------------------------------------------------------------------
/extra_script.py:
--------------------------------------------------------------------------------
1 | Import("env")
2 | from pathlib import Path
3 | import shutil
4 |
5 |
6 | def post_program_action(source, target, env):
7 | program_path = target[0].get_abspath()
8 | path = Path(program_path)
9 | shutil.copy(program_path, path.parent.parent.absolute())
10 | env.AddPostAction("$BUILD_DIR/${PROGNAME}.bin", post_program_action)
11 |
12 | env.Replace(PROGNAME="firmware_%s" % env.GetProjectOption("custom_prog_version"))
13 |
14 |
--------------------------------------------------------------------------------
/include/base.h:
--------------------------------------------------------------------------------
1 | /* base.h
2 | *
3 | * MIT License
4 | *
5 | * Copyright (c) 2025 awawa-dev
6 | *
7 | * https://github.com/awawa-dev/HyperSerialEsp8266
8 | *
9 | * Permission is hereby granted, free of charge, to any person obtaining a copy
10 | * of this software and associated documentation files (the "Software"), to deal
11 | * in the Software without restriction, including without limitation the rights
12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | * copies of the Software, and to permit persons to whom the Software is
14 | * furnished to do so, subject to the following conditions:
15 | *
16 | * The above copyright notice and this permission notice shall be included in all
17 | * copies or substantial portions of the Software.
18 |
19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 | * SOFTWARE.
26 | */
27 |
28 | #ifndef BASE_H
29 | #define BASE_H
30 |
31 | class Base
32 | {
33 | // LED strip number
34 | int ledsNumber = 0;
35 | // NeoPixelBusLibrary primary object
36 | LED_DRIVER* ledStrip1 = nullptr;
37 | // frame is set and ready to render
38 | bool readyToRender = false;
39 |
40 | public:
41 | // static data buffer for the loop
42 | uint8_t buffer[MAX_BUFFER];
43 | // current queue position
44 | uint16_t queueCurrent = 0;
45 | // queue end position
46 | volatile uint16_t queueEnd = 0;
47 |
48 | inline int getLedsNumber()
49 | {
50 | return ledsNumber;
51 | }
52 |
53 | inline LED_DRIVER* getLedStrip1()
54 | {
55 | return ledStrip1;
56 | }
57 |
58 | void initLedStrip(int count)
59 | {
60 | if (ledStrip1 != nullptr)
61 | {
62 | delete ledStrip1;
63 | ledStrip1 = nullptr;
64 | }
65 |
66 | ledsNumber = count;
67 |
68 | if (ledStrip1 == nullptr)
69 | {
70 | ledStrip1 = new LED_DRIVER(ledsNumber);
71 | ledStrip1->Begin();
72 | }
73 | }
74 |
75 | /**
76 | * @brief Check if there is already prepared frame to display
77 | *
78 | * @return true
79 | * @return false
80 | */
81 | inline bool hasLateFrameToRender()
82 | {
83 | return readyToRender;
84 | }
85 |
86 | inline void renderLeds(bool newFrame)
87 | {
88 | if (newFrame)
89 | readyToRender = true;
90 |
91 | if (readyToRender &&
92 | (ledStrip1 != nullptr && ledStrip1->CanShow()))
93 | {
94 | statistics.increaseShow();
95 | readyToRender = false;
96 |
97 | // display segments
98 | ledStrip1->Show(false);
99 | }
100 | }
101 |
102 | inline bool setStripPixel(uint16_t pix, ColorDefinition &inputColor)
103 | {
104 | if (pix < ledsNumber)
105 | {
106 | ledStrip1->SetPixelColor(pix, inputColor);
107 | }
108 |
109 | return (pix + 1 < ledsNumber);
110 | }
111 | } base;
112 |
113 | #endif
--------------------------------------------------------------------------------
/include/calibration.h:
--------------------------------------------------------------------------------
1 | /* calibration.h
2 | *
3 | * MIT License
4 | *
5 | * Copyright (c) 2025 awawa-dev
6 | *
7 | * https://github.com/awawa-dev/HyperSerialEsp8266
8 | *
9 | * Permission is hereby granted, free of charge, to any person obtaining a copy
10 | * of this software and associated documentation files (the "Software"), to deal
11 | * in the Software without restriction, including without limitation the rights
12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | * copies of the Software, and to permit persons to whom the Software is
14 | * furnished to do so, subject to the following conditions:
15 | *
16 | * The above copyright notice and this permission notice shall be included in all
17 | * copies or substantial portions of the Software.
18 |
19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 | * SOFTWARE.
26 | */
27 |
28 | #ifdef NEOPIXEL_RGBW
29 | typedef RgbwColor ColorDefinition;
30 | #else
31 | typedef RgbColor ColorDefinition;
32 | #endif
33 |
34 |
35 | #if !defined(CALIBRATION_H) && (defined(NEOPIXEL_RGBW) || defined(HYPERSERIAL_TESTING))
36 | #define CALIBRATION_H
37 |
38 | #include
39 | #include
40 |
41 | #define ROUND_DIVIDE(numer, denom) (((numer) + (denom) / 2) / (denom))
42 |
43 | struct
44 | {
45 | uint8_t white[256];
46 | uint8_t red[256];
47 | uint8_t green[256];
48 | uint8_t blue[256];
49 | } channelCorrection;
50 |
51 | class CalibrationConfig
52 | {
53 | // calibration parameters
54 | uint8_t gain = 0xFF;
55 | uint8_t red = 0xA0;
56 | uint8_t green = 0xA0;
57 | uint8_t blue = 0xA0;
58 |
59 | /**
60 | * @brief Build the LUT table using provided parameters
61 | *
62 | */
63 | void prepareCalibration()
64 | {
65 | // prepare LUT calibration table, cold white is much better than "neutral" white
66 | for (uint32_t i = 0; i < 256; i++)
67 | {
68 | // color calibration
69 | uint32_t _gain = gain * i; // adjust gain
70 | uint32_t _red = red * i; // adjust red
71 | uint32_t _green = green * i; // adjust green
72 | uint32_t _blue = blue * i; // adjust blue
73 |
74 | channelCorrection.white[i] = (uint8_t)std::min(ROUND_DIVIDE(_gain, 0xFF), (uint32_t)0xFF);
75 | channelCorrection.red[i] = (uint8_t)std::min(ROUND_DIVIDE(_red, 0xFF), (uint32_t)0xFF);
76 | channelCorrection.green[i] = (uint8_t)std::min(ROUND_DIVIDE(_green,0xFF), (uint32_t)0xFF);
77 | channelCorrection.blue[i] = (uint8_t)std::min(ROUND_DIVIDE(_blue, 0xFF), (uint32_t)0xFF);
78 | }
79 | }
80 |
81 | public:
82 | CalibrationConfig()
83 | {
84 | prepareCalibration();
85 | }
86 |
87 | /**
88 | * @brief Compare base calibration settings
89 | *
90 | */
91 | bool compareCalibrationSettings(uint8_t _gain, uint8_t _red, uint8_t _green, uint8_t _blue)
92 | {
93 | return _gain == gain && _red == red && _green == green && _blue == blue;
94 | }
95 |
96 | /**
97 | * @brief Set the parameters that define RGB to RGBW transformation
98 | *
99 | * @param _gain
100 | * @param _red
101 | * @param _green
102 | * @param _blue
103 | */
104 | void setParamsAndPrepareCalibration(uint8_t _gain, uint8_t _red, uint8_t _green, uint8_t _blue)
105 | {
106 | if (gain != _gain || red != _red || green != _green || blue != _blue)
107 | {
108 | gain = _gain;
109 | red = _red;
110 | green = _green;
111 | blue = _blue;
112 | prepareCalibration();
113 | }
114 | }
115 |
116 | /**
117 | * @brief print RGBW calibration parameters when no data is received
118 | *
119 | */
120 | void printCalibration()
121 | {
122 | #ifdef SerialPort
123 | SerialPort.write("\r\nRGBW => Gain: ");
124 | SerialPort.print(gain);
125 | SerialPort.write("/255, red: ");
126 | SerialPort.print(red);
127 | SerialPort.write(" , green: ");
128 | SerialPort.print(green);
129 | SerialPort.write(" , blue: ");
130 | SerialPort.print(blue);
131 | #endif
132 | }
133 | } calibrationConfig;
134 | #endif
135 |
136 |
--------------------------------------------------------------------------------
/include/framestate.h:
--------------------------------------------------------------------------------
1 | /* framestate.h
2 | *
3 | * MIT License
4 | *
5 | * Copyright (c) 2025 awawa-dev
6 | *
7 | * https://github.com/awawa-dev/HyperSerialEsp8266
8 | *
9 | * Permission is hereby granted, free of charge, to any person obtaining a copy
10 | * of this software and associated documentation files (the "Software"), to deal
11 | * in the Software without restriction, including without limitation the rights
12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | * copies of the Software, and to permit persons to whom the Software is
14 | * furnished to do so, subject to the following conditions:
15 | *
16 | * The above copyright notice and this permission notice shall be included in all
17 | * copies or substantial portions of the Software.
18 |
19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 | * SOFTWARE.
26 | */
27 |
28 | #ifndef FRAMESTATE_H
29 | #define FRAMESTATE_H
30 |
31 | /**
32 | * @brief my AWA frame protocol definition
33 | *
34 | */
35 | enum class AwaProtocol
36 | {
37 | HEADER_A,
38 | HEADER_w,
39 | HEADER_a,
40 | HEADER_HI,
41 | HEADER_LO,
42 | HEADER_CRC,
43 | VERSION2_GAIN,
44 | VERSION2_RED,
45 | VERSION2_GREEN,
46 | VERSION2_BLUE,
47 | RED,
48 | GREEN,
49 | BLUE,
50 | FLETCHER1,
51 | FLETCHER2
52 | };
53 |
54 | /**
55 | * @brief Contains current state of the incoming frame
56 | *
57 | */
58 | class
59 | {
60 | AwaProtocol state = AwaProtocol::HEADER_A;
61 | bool protocolVersion2 = false;
62 | uint8_t CRC = 0;
63 | uint16_t count = 0;
64 | uint16_t currentLed = 0;
65 | uint16_t fletcher1 = 0;
66 | uint16_t fletcher2 = 0;
67 |
68 | public:
69 | ColorDefinition color;
70 |
71 | /**
72 | * @brief Reset statistics for new frame
73 | *
74 | * @param input
75 | */
76 | inline void init(byte input)
77 | {
78 | currentLed = 0;
79 | count = input * 0x100;
80 | CRC = input;
81 | fletcher1 = 0;
82 | fletcher2 = 0;
83 | }
84 |
85 | /**
86 | * @brief get computed CRC
87 | *
88 | * @return uint8_t
89 | */
90 | inline uint8_t getCRC()
91 | {
92 | return CRC;
93 | }
94 |
95 | /**
96 | * @brief Get the color count reported by the frame
97 | *
98 | * @return uint16_t
99 | */
100 | inline uint16_t getCount()
101 | {
102 | return count;
103 | }
104 |
105 | /**
106 | * @brief Get the Fletcher1 total sum
107 | *
108 | * @return uint16_t
109 | */
110 | inline uint16_t getFletcher1()
111 | {
112 | return fletcher1;
113 | }
114 |
115 | /**
116 | * @brief Get the Fletcher2 total sum
117 | *
118 | * @return uint16_t
119 | */
120 | inline uint16_t getFletcher2()
121 | {
122 | return fletcher2;
123 | }
124 |
125 | /**
126 | * @brief Get and increase the current Led index
127 | *
128 | * @return uint16_t
129 | */
130 | inline uint16_t getCurrentLedIndex()
131 | {
132 | return currentLed++;
133 | }
134 |
135 | /**
136 | * @brief Set if frame protocol version 2 (contains calibration data)
137 | *
138 | * @param newVer
139 | */
140 | inline void setProtocolVersion2(bool newVer)
141 | {
142 | protocolVersion2 = newVer;
143 | }
144 |
145 | /**
146 | * @brief Verify if frame protocol version 2 (contains calibration data)
147 | *
148 | * @return true
149 | * @return false
150 | */
151 | inline bool isProtocolVersion2()
152 | {
153 | return protocolVersion2;
154 | }
155 |
156 | /**
157 | * @brief Set new AWA frame state
158 | *
159 | * @param newState
160 | */
161 | inline void setState(AwaProtocol newState)
162 | {
163 | state = newState;
164 | }
165 |
166 | /**
167 | * @brief Get current AWA frame state
168 | *
169 | * @return AwaProtocol
170 | */
171 | inline AwaProtocol getState()
172 | {
173 | return state;
174 | }
175 |
176 | /**
177 | * @brief Update CRC based on current and previuos input
178 | *
179 | * @param input
180 | */
181 | inline void computeCRC(byte input)
182 | {
183 | count += input;
184 | CRC = CRC ^ input ^ 0x55;
185 | }
186 |
187 | /**
188 | * @brief Update Fletcher checksumn for incoming input
189 | *
190 | * @param input
191 | */
192 | inline void addFletcher(byte input)
193 | {
194 | fletcher1 = (fletcher1 + (uint16_t)input) % 255;
195 | fletcher2 = (fletcher2 + fletcher1) % 255;
196 | }
197 |
198 | /**
199 | * @brief Check if the calibration data was updated and calculate new one
200 | *
201 | */
202 | inline void updateIncomingCalibration()
203 | {
204 | #ifdef NEOPIXEL_RGBW
205 | if (protocolVersion2)
206 | {
207 | calibrationConfig.setParamsAndPrepareCalibration(calibration.gain, calibration.red, calibration.green, calibration.blue);
208 | }
209 | #endif
210 | }
211 |
212 |
213 | #ifdef NEOPIXEL_RGBW
214 | /**
215 | * @brief Compute && correct the white channel
216 | *
217 | */
218 | inline void rgb2rgbw()
219 | {
220 | color.W = min(channelCorrection.red[color.R],
221 | min(channelCorrection.green[color.G],
222 | channelCorrection.blue[color.B]));
223 | color.R -= channelCorrection.red[color.W];
224 | color.G -= channelCorrection.green[color.W];
225 | color.B -= channelCorrection.blue[color.W];
226 | color.W = channelCorrection.white[color.W];
227 | }
228 | #endif
229 |
230 | /**
231 | * @brief Incoming calibration data
232 | *
233 | */
234 | struct
235 | {
236 | uint8_t gain = 0;
237 | uint8_t red = 0;
238 | uint8_t green = 0;
239 | uint8_t blue = 0;
240 | } calibration;
241 |
242 | } frameState;
243 |
244 | #endif
--------------------------------------------------------------------------------
/include/main.h:
--------------------------------------------------------------------------------
1 | /* main.h
2 | *
3 | * MIT License
4 | *
5 | * Copyright (c) 2025 awawa-dev
6 | *
7 | * https://github.com/awawa-dev/HyperSerialEsp8266
8 | *
9 | * Permission is hereby granted, free of charge, to any person obtaining a copy
10 | * of this software and associated documentation files (the "Software"), to deal
11 | * in the Software without restriction, including without limitation the rights
12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | * copies of the Software, and to permit persons to whom the Software is
14 | * furnished to do so, subject to the following conditions:
15 | *
16 | * The above copyright notice and this permission notice shall be included in all
17 | * copies or substantial portions of the Software.
18 |
19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 | * SOFTWARE.
26 | */
27 |
28 | #ifndef MAIN_H
29 | #define MAIN_H
30 |
31 | #define MAX_BUFFER 5120
32 | #define HELLO_MESSAGE "\r\nWelcome!\r\nAwa driver 8."
33 |
34 | #include "calibration.h"
35 | #include "statistics.h"
36 | #include "base.h"
37 | #include "framestate.h"
38 |
39 | /**
40 | * @brief shandling serial communication
41 | *
42 | */
43 | void serialTaskHandler()
44 | {
45 | uint16_t incomingSize = min(SerialPort.available(), MAX_BUFFER);
46 |
47 | if (incomingSize > 0)
48 | {
49 | SerialPort.read(&(base.buffer[0]), incomingSize);
50 | base.queueEnd = incomingSize;
51 | base.queueCurrent = 0;
52 | }
53 | }
54 |
55 | /**
56 | * @brief process received data on core 0
57 | *
58 | */
59 | void processData()
60 | {
61 | // update and print statistics
62 | unsigned long currentTime = millis();
63 | unsigned long deltaTime = currentTime - statistics.getStartTime();
64 |
65 | if (base.queueCurrent != base.queueEnd && deltaTime >= 1000)
66 | {
67 | statistics.update(currentTime);
68 | }
69 | else if (deltaTime >= 3000)
70 | {
71 | frameState.setState(AwaProtocol::HEADER_A);
72 | statistics.print(currentTime);
73 | }
74 |
75 | // render waiting frame if available
76 | if (base.hasLateFrameToRender() && frameState.getState() == AwaProtocol::HEADER_A)
77 | base.renderLeds(false);
78 |
79 | // process received data
80 | while (base.queueCurrent != base.queueEnd)
81 | {
82 | byte input = base.buffer[base.queueCurrent++];
83 |
84 | switch (frameState.getState())
85 | {
86 | case AwaProtocol::HEADER_A:
87 | // assume it's protocol version 1, verify it later
88 | frameState.setProtocolVersion2(false);
89 | if (input == 'A')
90 | frameState.setState(AwaProtocol::HEADER_w);
91 | break;
92 |
93 | case AwaProtocol::HEADER_w:
94 | if (input == 'w')
95 | frameState.setState(AwaProtocol::HEADER_a);
96 | else
97 | frameState.setState(AwaProtocol::HEADER_A);
98 | break;
99 |
100 | case AwaProtocol::HEADER_a:
101 | // detect protocol version
102 | if (input == 'a')
103 | frameState.setState(AwaProtocol::HEADER_HI);
104 | else if (input == 'A')
105 | {
106 | frameState.setState(AwaProtocol::HEADER_HI);
107 | frameState.setProtocolVersion2(true);
108 | }
109 | else
110 | frameState.setState(AwaProtocol::HEADER_A);
111 | break;
112 |
113 | case AwaProtocol::HEADER_HI:
114 | // initialize new frame properties
115 | statistics.increaseTotal();
116 | frameState.init(input);
117 | frameState.setState(AwaProtocol::HEADER_LO);
118 | break;
119 |
120 | case AwaProtocol::HEADER_LO:
121 | frameState.computeCRC(input);
122 | frameState.setState(AwaProtocol::HEADER_CRC);
123 | break;
124 |
125 | case AwaProtocol::HEADER_CRC:
126 | // verify CRC and create/update LED driver if neccesery
127 | if (frameState.getCRC() == input)
128 | {
129 | uint16_t ledSize = frameState.getCount() + 1;
130 |
131 | // sanity check
132 | if (ledSize > 4096)
133 | frameState.setState(AwaProtocol::HEADER_A);
134 | else
135 | {
136 | if (ledSize != base.getLedsNumber())
137 | base.initLedStrip(ledSize);
138 |
139 | frameState.setState(AwaProtocol::RED);
140 | }
141 | }
142 | else if (frameState.getCount() == 0x2aa2 && (input == 0x15 || input == 0x35))
143 | {
144 | if (input == 0x15)
145 | {
146 | SerialPort.println(HELLO_MESSAGE);
147 | statistics.reset(deltaTime);
148 | }
149 | else
150 | statistics.print(currentTime);
151 |
152 | frameState.setState(AwaProtocol::HEADER_A);
153 | }
154 | else
155 | frameState.setState(AwaProtocol::HEADER_A);
156 | break;
157 |
158 | case AwaProtocol::RED:
159 | frameState.color.R = input;
160 | frameState.addFletcher(input);
161 |
162 | frameState.setState(AwaProtocol::GREEN);
163 | break;
164 |
165 | case AwaProtocol::GREEN:
166 | frameState.color.G = input;
167 | frameState.addFletcher(input);
168 |
169 | frameState.setState(AwaProtocol::BLUE);
170 | break;
171 |
172 | case AwaProtocol::BLUE:
173 | frameState.color.B = input;
174 | frameState.addFletcher(input);
175 |
176 | #ifdef NEOPIXEL_RGBW
177 | // calculate RGBW from RGB using provided calibration data
178 | frameState.rgb2rgbw();
179 | #endif
180 |
181 | // set pixel, increase the index and check if it was the last LED color to come
182 | if (base.setStripPixel(frameState.getCurrentLedIndex(), frameState.color))
183 | {
184 | frameState.setState(AwaProtocol::RED);
185 | }
186 | else
187 | {
188 | if (frameState.isProtocolVersion2())
189 | frameState.setState(AwaProtocol::VERSION2_GAIN);
190 | else
191 | frameState.setState(AwaProtocol::FLETCHER1);
192 | }
193 |
194 | break;
195 |
196 | case AwaProtocol::VERSION2_GAIN:
197 | frameState.calibration.gain = input;
198 | frameState.addFletcher(input);
199 |
200 | frameState.setState(AwaProtocol::VERSION2_RED);
201 | break;
202 |
203 | case AwaProtocol::VERSION2_RED:
204 | frameState.calibration.red = input;
205 | frameState.addFletcher(input);
206 |
207 | frameState.setState(AwaProtocol::VERSION2_GREEN);
208 | break;
209 |
210 | case AwaProtocol::VERSION2_GREEN:
211 | frameState.calibration.green = input;
212 | frameState.addFletcher(input);
213 |
214 | frameState.setState(AwaProtocol::VERSION2_BLUE);
215 | break;
216 |
217 | case AwaProtocol::VERSION2_BLUE:
218 | frameState.calibration.blue = input;
219 | frameState.addFletcher(input);
220 |
221 | frameState.setState(AwaProtocol::FLETCHER1);
222 | break;
223 |
224 | case AwaProtocol::FLETCHER1:
225 | // initial frame data integrity check
226 | if (input != frameState.getFletcher1())
227 | frameState.setState(AwaProtocol::HEADER_A);
228 | else
229 | frameState.setState(AwaProtocol::FLETCHER2);
230 | break;
231 |
232 | case AwaProtocol::FLETCHER2:
233 | // final frame data integrity check
234 | if (input == frameState.getFletcher2())
235 | {
236 | statistics.increaseGood();
237 |
238 | base.renderLeds(true);
239 |
240 | #ifdef NEOPIXEL_RGBW
241 | // if received the calibration data, update it now
242 | if (frameState.isProtocolVersion2())
243 | {
244 | frameState.updateIncomingCalibration();
245 | }
246 | #endif
247 | }
248 |
249 | frameState.setState(AwaProtocol::HEADER_A);
250 | break;
251 | }
252 | }
253 | }
254 |
255 | #endif
--------------------------------------------------------------------------------
/include/statistics.h:
--------------------------------------------------------------------------------
1 | /* stats.h
2 | *
3 | * MIT License
4 | *
5 | * Copyright (c) 2025 awawa-dev
6 | *
7 | * https://github.com/awawa-dev/HyperSerialEsp8266
8 | *
9 | * Permission is hereby granted, free of charge, to any person obtaining a copy
10 | * of this software and associated documentation files (the "Software"), to deal
11 | * in the Software without restriction, including without limitation the rights
12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | * copies of the Software, and to permit persons to whom the Software is
14 | * furnished to do so, subject to the following conditions:
15 | *
16 | * The above copyright notice and this permission notice shall be included in all
17 | * copies or substantial portions of the Software.
18 |
19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 | * SOFTWARE.
26 | */
27 |
28 | #ifndef STATISTICS_H
29 | #define STATISTICS_H
30 |
31 | // statistics (stats sent only when there is no communication)
32 | class
33 | {
34 | unsigned long startTime = 0;
35 | uint16_t goodFrames = 0;
36 | uint16_t showFrames = 0;
37 | uint16_t totalFrames = 0;
38 | uint16_t finalGoodFrames = 0;
39 | uint16_t finalShowFrames = 0;
40 | uint16_t finalTotalFrames = 0;
41 |
42 | public:
43 | /**
44 | * @brief Get the start time of the current period
45 | *
46 | * @return unsigned long
47 | */
48 | inline unsigned long getStartTime()
49 | {
50 | return startTime;
51 | }
52 |
53 | /**
54 | * @brief Detected new frame
55 | *
56 | */
57 | inline void increaseTotal()
58 | {
59 | totalFrames++;
60 | }
61 |
62 | /**
63 | * @brief The frame is received and shown
64 | *
65 | */
66 | inline void increaseShow()
67 | {
68 | showFrames++;
69 | }
70 |
71 | /**
72 | * @brief The frame is received correctly (not yet displayed)
73 | *
74 | */
75 | inline void increaseGood()
76 | {
77 | goodFrames++;
78 | }
79 |
80 | /**
81 | * @brief Get number of correctly received frames
82 | *
83 | * @return uint16_t
84 | */
85 | inline uint16_t getGoodFrames()
86 | {
87 | return goodFrames;
88 | }
89 |
90 | /**
91 | * @brief Period restart, save current statistics ans send them later if there is no incoming communication
92 | *
93 | * @param currentTime
94 | */
95 | void update(unsigned long currentTime)
96 | {
97 | if (totalFrames > 0)
98 | {
99 | finalShowFrames = showFrames;
100 | finalGoodFrames = std::min(goodFrames, totalFrames);
101 | finalTotalFrames = totalFrames;
102 | }
103 |
104 | startTime = currentTime;
105 | goodFrames = 0;
106 | totalFrames = 0;
107 | showFrames = 0;
108 | }
109 |
110 | /**
111 | * @brief Print last saved statistics to the serial port
112 | *
113 | * @param curTime
114 | */
115 | void print(unsigned long curTime)
116 | {
117 | startTime = curTime;
118 | goodFrames = 0;
119 | totalFrames = 0;
120 | showFrames = 0;
121 |
122 | SerialPort.write("\r\nHyperHDR frames: ");
123 | SerialPort.print(finalShowFrames);
124 | SerialPort.write(" (FPS), receiv.: ");
125 | SerialPort.print(finalTotalFrames);
126 | SerialPort.write(", good: ");
127 | SerialPort.print(finalGoodFrames);
128 | SerialPort.write(", incompl.: ");
129 | SerialPort.print(finalTotalFrames - finalGoodFrames);
130 | SerialPort.write(", heap: ");
131 | SerialPort.print(ESP.getFreeHeap());
132 | #if defined(NEOPIXEL_RGBW)
133 | calibrationConfig.printCalibration();
134 | #endif
135 |
136 | }
137 |
138 | /**
139 | * @brief Reset statistics
140 | *
141 | */
142 | void reset(unsigned long currentTime)
143 | {
144 | startTime = currentTime;
145 |
146 | finalShowFrames = 0;
147 | finalGoodFrames = 0;
148 | finalTotalFrames = 0;
149 |
150 | goodFrames = 0;
151 | totalFrames = 0;
152 | showFrames = 0;
153 | }
154 |
155 | } statistics;
156 |
157 | #endif
--------------------------------------------------------------------------------
/lib/README:
--------------------------------------------------------------------------------
1 |
2 | This directory is intended for project specific (private) libraries.
3 | PlatformIO will compile them to static libraries and link into executable file.
4 |
5 | The source code of each library should be placed in a an own separate directory
6 | ("lib/your_library_name/[here are source files]").
7 |
8 | For example, see a structure of the following two libraries `Foo` and `Bar`:
9 |
10 | |--lib
11 | | |
12 | | |--Bar
13 | | | |--docs
14 | | | |--examples
15 | | | |--src
16 | | | |- Bar.c
17 | | | |- Bar.h
18 | | | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
19 | | |
20 | | |--Foo
21 | | | |- Foo.c
22 | | | |- Foo.h
23 | | |
24 | | |- README --> THIS FILE
25 | |
26 | |- platformio.ini
27 | |--src
28 | |- main.c
29 |
30 | and a contents of `src/main.c`:
31 | ```
32 | #include
33 | #include
34 |
35 | int main (void)
36 | {
37 | ...
38 | }
39 |
40 | ```
41 |
42 | PlatformIO Library Dependency Finder will find automatically dependent
43 | libraries scanning project source files.
44 |
45 | More information about PlatformIO Library Dependency Finder
46 | - https://docs.platformio.org/page/librarymanager/ldf.html
47 |
--------------------------------------------------------------------------------
/platformio.ini:
--------------------------------------------------------------------------------
1 | ; You can adjust speed of the serial port communication by adjusting 'SERIALCOM_SPEED' variable in [env] section
2 | ; Also you can force to clear LEDs on the esp8266 start up (-DCLEAR_LEDS=number_of_leds_to_clear)
3 | ; The pinout can not be changed for esp8266 board
4 |
5 | [platformio]
6 | default_envs = SK6812_RGBW_COLD, SK6812_RGBW_NEUTRAL, WS281x_RGB, SPI_APA102_SK9822_HD107, SPI_WS2801
7 |
8 | [env]
9 | build_flags = -DSERIALCOM_SPEED=2000000 ;-DCLEAR_LEDS=24 ;remove leading semi-colon to uncomment
10 | extra_scripts = pre:extra_script.py
11 | platform = espressif8266
12 | board = d1_mini
13 | framework = arduino
14 | lib_deps = makuna/NeoPixelBus@2.7.0
15 |
16 | [env:SK6812_RGBW_COLD]
17 | build_flags = -DNEOPIXEL_RGBW -DCOLD_WHITE ${env.build_flags}
18 | custom_prog_version = SK6812_RGBW_COLD
19 |
20 | [env:SK6812_RGBW_NEUTRAL]
21 | build_flags = -DNEOPIXEL_RGBW ${env.build_flags}
22 | custom_prog_version = SK6812_RGBW_NEUTRAL
23 |
24 | [env:WS281x_RGB]
25 | build_flags = -DNEOPIXEL_RGB ${env.build_flags}
26 | custom_prog_version = WS281x_RGB
27 |
28 | [env:SPI_APA102_SK9822_HD107]
29 | build_flags = -DSPILED_APA102 ${env.build_flags}
30 | custom_prog_version = SPI_APA102_SK9822_HD107
31 |
32 | [env:SPI_WS2801]
33 | build_flags = -DSPILED_WS2801 ${env.build_flags}
34 | custom_prog_version = SPI_WS2801
35 |
--------------------------------------------------------------------------------
/src/main.cpp:
--------------------------------------------------------------------------------
1 | /* main.cpp
2 | *
3 | * MIT License
4 | *
5 | * Copyright (c) 2025 awawa-dev
6 | *
7 | * https://github.com/awawa-dev/HyperSerialEsp8266
8 | *
9 | * Permission is hereby granted, free of charge, to any person obtaining a copy
10 | * of this software and associated documentation files (the "Software"), to deal
11 | * in the Software without restriction, including without limitation the rights
12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | * copies of the Software, and to permit persons to whom the Software is
14 | * furnished to do so, subject to the following conditions:
15 | *
16 | * The above copyright notice and this permission notice shall be included in all
17 | * copies or substantial portions of the Software.
18 |
19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 | * SOFTWARE.
26 | */
27 |
28 | #include
29 | #include
30 |
31 | ///////////////////////////////////////////////////////////////////////////
32 | // DO NOT EDIT THIS FILE. ADJUST THE CONFIGURATION IN THE platformio.ini //
33 | ///////////////////////////////////////////////////////////////////////////
34 |
35 | #define _STR(x) #x
36 | #define _XSTR(x) _STR(x)
37 | #define VAR_NAME_VALUE(var) #var " = " _XSTR(var)
38 | #define _XSTR2(x,y) _STR(x) _STR(y)
39 | #define VAR_NAME_VALUE2(var) #var " = " _XSTR2(var)
40 |
41 | #ifdef CLEAR_LEDS
42 | #pragma message(VAR_NAME_VALUE(CLEAR_LEDS))
43 | #endif
44 | #ifdef NEOPIXEL_RGBW
45 | #pragma message(VAR_NAME_VALUE(NEOPIXEL_RGBW))
46 | #endif
47 | #ifdef NEOPIXEL_RGB
48 | #pragma message(VAR_NAME_VALUE(NEOPIXEL_RGB))
49 | #endif
50 | #ifdef COLD_WHITE
51 | #pragma message(VAR_NAME_VALUE(COLD_WHITE))
52 | #endif
53 | #ifdef SPILED_APA102
54 | #pragma message(VAR_NAME_VALUE(SPILED_APA102))
55 | #endif
56 | #ifdef SPILED_WS2801
57 | #pragma message(VAR_NAME_VALUE(SPILED_WS2801))
58 | #endif
59 | #pragma message(VAR_NAME_VALUE(SERIALCOM_SPEED))
60 |
61 | #ifdef NEOPIXEL_RGBW
62 | #define LED_DRIVER NeoPixelBus
63 | #elif NEOPIXEL_RGB
64 | #define LED_DRIVER NeoPixelBus
65 | #elif SPILED_APA102
66 | #define LED_DRIVER NeoPixelBus
67 | #elif SPILED_WS2801
68 | #define LED_DRIVER NeoPixelBus
69 | #endif
70 |
71 | #pragma message(VAR_NAME_VALUE2(LED_DRIVER))
72 |
73 | #define SerialPort Serial
74 | #include "main.h"
75 |
76 | void setup()
77 | {
78 | // Init serial port
79 | Serial.setRxBufferSize(MAX_BUFFER);
80 | Serial.begin(SERIALCOM_SPEED);
81 | while (!Serial) continue;
82 |
83 | #ifdef CLEAR_LEDS
84 | LED_DRIVER* _ledStrip = new LED_DRIVER(CLEAR_LEDS);
85 | ColorDefinition _black(0);
86 | _ledStrip->Begin();
87 | for(int i = 0; i < CLEAR_LEDS; i++)
88 | _ledStrip->SetPixelColor(i, _black);
89 | _ledStrip->Dirty();
90 | _ledStrip->Show(false);
91 | delay(10);
92 | delete _ledStrip;
93 | #endif
94 |
95 | // Display config
96 | Serial.println(HELLO_MESSAGE);
97 |
98 | // Colorspace/Led type info
99 | #if defined(NEOPIXEL_RGBW) || defined(NEOPIXEL_RGB)
100 | #ifdef NEOPIXEL_RGBW
101 | #ifdef COLD_WHITE
102 | calibrationConfig.setParamsAndPrepareCalibration(0xFF, 0xA0, 0xA0, 0xA0);
103 | Serial.println("NeoPixelBus SK6812 cold GRBW.");
104 | #else
105 | calibrationConfig.setParamsAndPrepareCalibration(0xFF, 0xB0, 0xB0, 0x70);
106 | Serial.println("NeoPixelBus SK6812 neutral GRBW.");
107 | #endif
108 | calibrationConfig.printCalibration();
109 | #else
110 | Serial.println("NeoPixelBus ws281x type (GRB).");
111 | #endif
112 | #elif defined(SPILED_APA102)
113 | Serial.println("SPI APA102 compatible type (BGR).");
114 | #elif defined(SPILED_WS2801)
115 | Serial.println("SPI WS2801 (RBG).");
116 | #endif
117 |
118 | Serial.flush();
119 | delay(50);
120 | }
121 |
122 | void loop()
123 | {
124 | serialTaskHandler();
125 | processData();
126 | }
127 |
128 |
--------------------------------------------------------------------------------
/test/test_SingleSegment/main.cpp:
--------------------------------------------------------------------------------
1 | /* test_SingleSegment/main.cpp
2 | *
3 | * MIT License
4 | *
5 | * Copyright (c) 2025 awawa-dev
6 | *
7 | * https://github.com/awawa-dev/HyperSerialEsp8266
8 | *
9 | * Permission is hereby granted, free of charge, to any person obtaining a copy
10 | * of this software and associated documentation files (the "Software"), to deal
11 | * in the Software without restriction, including without limitation the rights
12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | * copies of the Software, and to permit persons to whom the Software is
14 | * furnished to do so, subject to the following conditions:
15 | *
16 | * The above copyright notice and this permission notice shall be included in all
17 | * copies or substantial portions of the Software.
18 |
19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 | * SOFTWARE.
26 | */
27 |
28 | #define NO_GLOBAL_SERIAL
29 | #define HYPERSERIAL_TESTING
30 |
31 | #include
32 |
33 | #ifdef NEOPIXEL_RGBW
34 | struct RgbwColor
35 | {
36 | uint8_t R;
37 | uint8_t G;
38 | uint8_t B;
39 | uint8_t W;
40 | };
41 | #else
42 | struct RgbColor
43 | {
44 | uint8_t R;
45 | uint8_t G;
46 | uint8_t B;
47 | };
48 | #endif
49 |
50 | #include
51 | #include "calibration.h"
52 |
53 | ///////////////////////////////////////////////////////////////////////////////////
54 | ///////////////////////////////////////////////////////////////////////////////////
55 | /////////////////////// WHITE CHANNEL CALIBRATION TEST ////////////////////////////
56 | ///////////////////////////////////////////////////////////////////////////////////
57 | ///////////////////////////////////////////////////////////////////////////////////
58 |
59 |
60 | // old calibration params
61 | float whiteLimit;
62 | uint8_t rCorrection;
63 | uint8_t gCorrection;
64 | uint8_t bCorrection;
65 |
66 | uint8_t wChannel[256];
67 | uint8_t rChannel[256];
68 | uint8_t gChannel[256];
69 | uint8_t bChannel[256];
70 |
71 | /**
72 | * @brief Old calibration procedure
73 | *
74 | * @return void
75 | */
76 | void oldCalibration()
77 | {
78 | for (uint32_t i = 0; i < 256; i++)
79 | {
80 | // color calibration
81 | float red = rCorrection * i; // adjust red
82 | float green = gCorrection * i; // adjust green
83 | float blue = bCorrection * i; // adjust blue
84 |
85 | wChannel[i] = (uint8_t)round(std::min(whiteLimit * i, 255.0f));
86 | rChannel[i] = (uint8_t)round(std::min(red / 0xFF, 255.0f));
87 | gChannel[i] = (uint8_t)round(std::min(green / 0xFF, 255.0f));
88 | bChannel[i] = (uint8_t)round(std::min(blue / 0xFF, 255.0f));
89 | }
90 | }
91 |
92 | /**
93 | * @brief Compare old and new calibration result
94 | *
95 | * @return void
96 | */
97 | void compareLut()
98 | {
99 | for (uint32_t i = 0; i < 256; i++)
100 | {
101 | int w = std::abs(int(wChannel[i]) - int(channelCorrection.white[i]));
102 | int r = std::abs(int(rChannel[i]) - int(channelCorrection.red[i]));
103 | int g = std::abs(int(gChannel[i]) - int(channelCorrection.green[i]));
104 | int b = std::abs(int(bChannel[i]) - int(channelCorrection.blue[i]));
105 | TEST_ASSERT_LESS_THAN(1, (int)(std::max(w, std::max(r, std::max(g, b)))));
106 | }
107 | }
108 |
109 | /**
110 | * @brief Calculate the calibration table using old and new method and compare the result
111 | *
112 | * @return void
113 | */
114 | void CommonTest_OldAndNedCalibrationAlgorithm()
115 | {
116 | // cold white old calibration (full range)
117 | whiteLimit = 1.0f;
118 | rCorrection = 0xA0;
119 | gCorrection = 0xA0;
120 | bCorrection = 0xA0;
121 | oldCalibration();
122 | // new procedure
123 | calibrationConfig.setParamsAndPrepareCalibration(255, 0xA0, 0xA0, 0xA0);
124 | compareLut();
125 |
126 | // neutral white old calibration (full range)
127 | whiteLimit = 1.0f;
128 | rCorrection = 0xB0;
129 | gCorrection = 0xB0;
130 | bCorrection = 0x70;
131 | oldCalibration();
132 | // new procedure
133 | calibrationConfig.setParamsAndPrepareCalibration(255, 0xB0, 0xB0, 0x70);
134 | compareLut();
135 |
136 | // cold white old calibration (medium range)
137 | whiteLimit = 0.5019607843137255f;
138 | rCorrection = 0xA0;
139 | gCorrection = 0xA0;
140 | bCorrection = 0xA0;
141 | oldCalibration();
142 | // new procedure
143 | calibrationConfig.setParamsAndPrepareCalibration(128, 0xA0, 0xA0, 0xA0);
144 | compareLut();
145 |
146 | // neutral white old calibration (medium range)
147 | whiteLimit = 0.5019607843137255f;
148 | rCorrection = 0xB0;
149 | gCorrection = 0xB0;
150 | bCorrection = 0x70;
151 | oldCalibration();
152 | // new procedure
153 | calibrationConfig.setParamsAndPrepareCalibration(128, 0xB0, 0xB0, 0x70);
154 | compareLut();
155 | }
156 |
157 | ///////////////////////////////////////////////////////////////////////////////////
158 | ///////////////////////////////////////////////////////////////////////////////////
159 | /////////////////////// AWA PROTOCOL CORRECTNESS TEST /////////////////////////////
160 | ///////////////////////////////////////////////////////////////////////////////////
161 | ///////////////////////////////////////////////////////////////////////////////////
162 |
163 | #define TEST_LEDS_NUMBER 401
164 | uint8_t _ledBuffer[TEST_LEDS_NUMBER * 3 + 6 + 7];
165 |
166 | /**
167 | * @brief Mockup Serial class to simulate the real communition
168 | *
169 | */
170 |
171 | class SerialTester
172 | {
173 | int frameSize = 0;
174 | int sent = 0;
175 |
176 | public:
177 |
178 | void createTestFrame(bool _white_channel_calibration, uint8_t _white_channel_limit = 0,
179 | uint8_t _white_channel_red = 0, uint8_t _white_channel_green = 0,
180 | uint8_t _white_channel_blue = 0)
181 | {
182 | _ledBuffer[0] = 'A';
183 | _ledBuffer[1] = 'w';
184 | _ledBuffer[2] = (_white_channel_calibration) ? 'A' : 'a';
185 | _ledBuffer[4] = (TEST_LEDS_NUMBER-1) & 0xff;
186 | _ledBuffer[3] = ((TEST_LEDS_NUMBER-1) >> 8) & 0xff;
187 | _ledBuffer[5] = _ledBuffer[3] ^ _ledBuffer[4] ^ 0x55;
188 |
189 | uint8_t* writer = &(_ledBuffer[6]);
190 | uint8_t* hasher = writer;
191 |
192 | for(int i=0; i < TEST_LEDS_NUMBER; i++)
193 | {
194 | *(writer++)=random(255);
195 | *(writer++)=random(255);
196 | *(writer++)=random(255);
197 | }
198 |
199 | if (_white_channel_calibration)
200 | {
201 | *(writer++) = _white_channel_limit;
202 | *(writer++) = _white_channel_red;
203 | *(writer++) = _white_channel_green;
204 | *(writer++) = _white_channel_blue;
205 | }
206 |
207 | uint16_t fletcher1 = 0, fletcher2 = 0;
208 | while (hasher < writer)
209 | {
210 | fletcher1 = (fletcher1 + *(hasher++)) % 255;
211 | fletcher2 = (fletcher2 + fletcher1) % 255;
212 | }
213 | *(writer++) = (uint8_t)fletcher1;
214 | *(writer++) = (uint8_t)fletcher2;
215 |
216 | frameSize = (int)(writer - _ledBuffer);
217 | sent = 0;
218 | }
219 |
220 |
221 | inline size_t write(const char * s)
222 | {
223 | return 0;
224 | }
225 |
226 | inline size_t print(unsigned char, int = DEC)
227 | {
228 | return 0;
229 | }
230 |
231 | int available(void)
232 | {
233 | if (sent < frameSize)
234 | {
235 | return std::min(std::max((int)(random(64)), 1), frameSize - sent);
236 | }
237 |
238 | return 0;
239 | }
240 |
241 | int toSend(void)
242 | {
243 | return frameSize - sent;
244 | }
245 |
246 | int getFrameSize()
247 | {
248 | return frameSize;
249 | }
250 |
251 | size_t read(uint8_t *buffer, size_t size)
252 | {
253 | int max = std::min(frameSize - sent, (int)size);
254 | if (max > 0)
255 | {
256 | memcpy(buffer, &(_ledBuffer[sent]), max);
257 | sent += max;
258 | return max;
259 | }
260 | return 0;
261 | }
262 |
263 | void println(const String &s)
264 | {
265 |
266 | }
267 | } SerialPort;
268 |
269 |
270 | /**
271 | * @brief Mockup LED driver to verify correctness of the received LEDs color values
272 | *
273 | */
274 |
275 | class ProtocolTester {
276 | int ledCount;
277 | int currentIndex = 0;
278 | int lastCount = 0;
279 |
280 | public:
281 | ProtocolTester(int count, int b)
282 | {
283 | ledCount = count;
284 |
285 | }
286 |
287 | ProtocolTester(int count)
288 | {
289 | ledCount = count;
290 | }
291 |
292 | bool CanShow()
293 | {
294 | return true;
295 | }
296 |
297 | void Show(bool safe = true)
298 | {
299 | lastCount = currentIndex;
300 | currentIndex = 0;
301 | }
302 |
303 | void Begin()
304 | {
305 |
306 | }
307 |
308 | void Begin(int a, int b, int c, int d)
309 | {
310 |
311 | }
312 |
313 | int getLastCount()
314 | {
315 | return lastCount;
316 | }
317 |
318 | /**
319 | * @brief Very important: verify LED color, compare it to the origin
320 | *
321 | * @param indexPixel
322 | * @param color
323 | */
324 | #ifdef NEOPIXEL_RGBW
325 | void SetPixelColor(uint16_t indexPixel, RgbwColor color)
326 | {
327 | TEST_ASSERT_EQUAL_INT_MESSAGE(currentIndex, indexPixel, "Unexpected LED index");
328 | TEST_ASSERT_LESS_THAN_MESSAGE(TEST_LEDS_NUMBER, indexPixel, "LED index out of scope");
329 | uint8_t *c = &(_ledBuffer[6 + indexPixel * 3]);
330 | uint8_t r = *(c++);
331 | uint8_t g = *(c++);
332 | uint8_t b = *(c++);
333 |
334 | uint8_t w = min(channelCorrection.red[r],
335 | min(channelCorrection.green[g],
336 | channelCorrection.blue[b]));
337 | r -= channelCorrection.red[w];
338 | g -= channelCorrection.green[w];
339 | b -= channelCorrection.blue[w];
340 | w = channelCorrection.white[w];
341 |
342 | TEST_ASSERT_EQUAL_UINT8(r, color.R);
343 | TEST_ASSERT_EQUAL_UINT8(g, color.G);
344 | TEST_ASSERT_EQUAL_UINT8(b, color.B);
345 | TEST_ASSERT_EQUAL_UINT8(w, color.W);
346 |
347 | currentIndex = indexPixel + 1;
348 | lastCount = 0;
349 | }
350 | #else
351 | void SetPixelColor(uint16_t indexPixel, RgbColor color)
352 | {
353 | TEST_ASSERT_EQUAL_INT_MESSAGE(currentIndex, indexPixel, "Unexpected LED index");
354 | TEST_ASSERT_LESS_THAN_MESSAGE(TEST_LEDS_NUMBER, indexPixel, "LED index out of scope");
355 | uint8_t *c = &(_ledBuffer[6 + indexPixel * 3]);
356 | uint8_t r = *(c++);
357 | uint8_t g = *(c++);
358 | uint8_t b = *(c++);
359 |
360 | TEST_ASSERT_EQUAL_UINT8(r, color.R);
361 | TEST_ASSERT_EQUAL_UINT8(g, color.G);
362 | TEST_ASSERT_EQUAL_UINT8(b, color.B);
363 |
364 | currentIndex = indexPixel + 1;
365 | lastCount = 0;
366 | }
367 | #endif
368 | };
369 |
370 | #define LED_DRIVER ProtocolTester
371 | #include "main.h"
372 |
373 |
374 |
375 | /**
376 | * @brief Send RGBW calibration data and verify it all (including proper colors rendering)
377 | *
378 | */
379 | void SingleSegmentTest_SendRgbwCalibration()
380 | {
381 | // set all calibration values to test
382 | SerialPort.createTestFrame(true, 10, 20, 30, 40);
383 | base.queueCurrent = 0;
384 | base.queueEnd = 0;
385 | statistics.update(0);
386 |
387 | TEST_ASSERT_EQUAL_INT_MESSAGE(0, statistics.getGoodFrames(), "Unexpected initial stats value");
388 |
389 | while(SerialPort.toSend() > 0)
390 | {
391 | serialTaskHandler();
392 | processData();
393 | }
394 |
395 | TEST_ASSERT_EQUAL_INT_MESSAGE(1, statistics.getGoodFrames(), "Frame is not received");
396 | TEST_ASSERT_EQUAL_INT_MESSAGE(TEST_LEDS_NUMBER, base.getLedStrip1()->getLastCount(), "Not all LEDs were set up");
397 | TEST_ASSERT_EQUAL_MESSAGE(true, calibrationConfig.compareCalibrationSettings(10,20,30,40), "Incorrect calibration result");
398 |
399 | // should not change if the frame doesnt contain calibration data
400 | SerialPort.createTestFrame(false);
401 | statistics.update(0);
402 |
403 | TEST_ASSERT_EQUAL_INT_MESSAGE(0, statistics.getGoodFrames(), "Unexpected initial stats value");
404 |
405 | while(SerialPort.toSend() > 0)
406 | {
407 | serialTaskHandler();
408 | processData();
409 | }
410 |
411 | TEST_ASSERT_EQUAL_INT_MESSAGE(1, statistics.getGoodFrames(), "Frame is not received");
412 | TEST_ASSERT_EQUAL_INT_MESSAGE(TEST_LEDS_NUMBER, base.getLedStrip1()->getLastCount(), "Not all LEDs were set up");
413 | TEST_ASSERT_EQUAL_MESSAGE(true, calibrationConfig.compareCalibrationSettings(10,20,30,40), "Incorrect calibration result");
414 |
415 | // last test
416 | SerialPort.createTestFrame(true, 255, 128, 128, 128);
417 | statistics.update(0);
418 |
419 | TEST_ASSERT_EQUAL_INT_MESSAGE(0, statistics.getGoodFrames(), "Unexpected initial stats value");
420 |
421 | while(SerialPort.toSend() > 0)
422 | {
423 | serialTaskHandler();
424 | processData();
425 | }
426 |
427 | TEST_ASSERT_EQUAL_INT_MESSAGE(1, statistics.getGoodFrames(), "Frame is not received");
428 | TEST_ASSERT_EQUAL_INT_MESSAGE(TEST_LEDS_NUMBER, base.getLedStrip1()->getLastCount(), "Not all LEDs were set up");
429 | TEST_ASSERT_EQUAL_MESSAGE(true, calibrationConfig.compareCalibrationSettings(255,128,128,128), "Incorrect calibration result");
430 | }
431 |
432 | /**
433 | * @brief Send 100 RGB/RGBW frames and verify it all (including proper colors rendering)
434 | *
435 | */
436 | void SingleSegmentTest_Send100Frames()
437 | {
438 | base.queueCurrent = 0;
439 | base.queueEnd = 0;
440 |
441 | for(int i = 0; i < 100; i++)
442 | {
443 | SerialPort.createTestFrame(false);
444 | statistics.update(0);
445 |
446 | TEST_ASSERT_EQUAL_INT_MESSAGE(0, statistics.getGoodFrames(), "Unexpected initial stats value");
447 |
448 | while(SerialPort.toSend() > 0)
449 | {
450 | serialTaskHandler();
451 | processData();
452 | }
453 |
454 | TEST_ASSERT_EQUAL_INT_MESSAGE(1, statistics.getGoodFrames(), "Frame is not received");
455 | TEST_ASSERT_EQUAL_INT_MESSAGE(TEST_LEDS_NUMBER, base.getLedStrip1()->getLastCount(), "Not all LEDs were set up");
456 | }
457 | }
458 |
459 | /**
460 | * @brief Send 200 RGB/RGBW valid/invalid frames and verify it all (including proper colors rendering)
461 | *
462 | */
463 | void SingleSegmentTest_Send200UncertainFrames()
464 | {
465 | base.queueCurrent = 0;
466 | base.queueEnd = 0;
467 |
468 | for(int i = 0; i < 200; i++)
469 | {
470 | SerialPort.createTestFrame(false);
471 | statistics.update(0);
472 |
473 | bool damaged = (random(255) % 2) == 0;
474 | int index;
475 | uint8_t backup;
476 | if (damaged)
477 | {
478 | index = random(SerialPort.getFrameSize());
479 | backup = _ledBuffer[index];
480 | _ledBuffer[index] = backup ^ 0xfe;
481 | }
482 |
483 | TEST_ASSERT_EQUAL_INT_MESSAGE(0, statistics.getGoodFrames(), "Unexpected initial stats value");
484 |
485 | while(SerialPort.toSend() > 0)
486 | {
487 | serialTaskHandler();
488 | processData();
489 | }
490 |
491 | if (damaged)
492 | {
493 | char buffer[128];
494 | snprintf(buffer, sizeof(buffer), "Damaged frame was received: [%d]=>%d", index, backup);
495 | TEST_ASSERT_EQUAL_INT_MESSAGE(0, statistics.getGoodFrames(), buffer);
496 | base.getLedStrip1()->Show();
497 | frameState.setState(AwaProtocol::HEADER_A);
498 | }
499 | else
500 | {
501 | TEST_ASSERT_EQUAL_INT_MESSAGE(1, statistics.getGoodFrames(), "Frame is not received");
502 | TEST_ASSERT_EQUAL_INT_MESSAGE(TEST_LEDS_NUMBER, base.getLedStrip1()->getLastCount(), "Not all LEDs were set up");
503 | }
504 | }
505 | }
506 |
507 | ///////////////////////////////////////////////////////////////////////////////////
508 | ///////////////////////////////////////////////////////////////////////////////////
509 | ///////////////////////////// UNIT TEST ROUTINES //////////////////////////////////
510 | ///////////////////////////////////////////////////////////////////////////////////
511 | ///////////////////////////////////////////////////////////////////////////////////
512 |
513 | void setup()
514 | {
515 | delay(1000);
516 | randomSeed(analogRead(0));
517 | UNITY_BEGIN();
518 | #ifdef NEOPIXEL_RGBW
519 | RUN_TEST(CommonTest_OldAndNedCalibrationAlgorithm);
520 | RUN_TEST(SingleSegmentTest_SendRgbwCalibration);
521 | #endif
522 | RUN_TEST(SingleSegmentTest_Send100Frames);
523 | RUN_TEST(SingleSegmentTest_Send200UncertainFrames);
524 | UNITY_END();
525 | }
526 |
527 | void loop()
528 | {
529 | }
530 |
--------------------------------------------------------------------------------