We have reached the end of the series on WS2812B addressable LEDs. At least for now. In this part, I will discuss splitting an LED strip into fully configurable and independent segments. This means that each segment can operate in a different mode, at a different speed, and have a different length. This gives huge possibilities to tailor it to your needs. Let’s go!
This article is part of the series:
The idea of splitting into segments
Since LED strips can theoretically be infinitely long, it would be a shame to waste their potential on setting up just one effect. You can route such a strip in different places, bend it, and do other tricks. For example, when lining a cabinet under the TV, we wouldn’t necessarily want every edge to glow in the same way. That’s why I modified the library and added segment splitting to it. The Arduino library that inspired me also has this capability, but it is implemented in a slightly different way.
One of my assumptions was dynamically creating the appropriate array of segments. Thanks to this, if you want only one segment, only one “object” of a segment is created in memory. The number of segments can be changed on the fly. The code ensures there are no memory leaks and the segment array scales on an ongoing basis. One segment occupies 56 bytes in memory and looks like this:
typedef struct ws2812bfx_s
{
volatile uint32_t ModeDelay; // Segment SW timer counter
uint16_t IdStart; // Start segment point
uint16_t IdStop; // End segment point
uint8_t Running : 1; // Is sector running
uint8_t ActualMode; // Sector mode setting
uint8_t Reverse : 1; // Is reverted mode
uint32_t CounterModeCall; // Numbers of calls
uint32_t CounterModeStep; // Call step
uint16_t Speed; // Segment speed
uint32_t ModeColor[NUM_COLORS]; // Mode color 32 bit representation
ws2812b_color ModeColor_w[NUM_COLORS]; // Mode color struct representation
uint8_t AuxParam; // Computing variable
uint16_t AuxParam16b; // Computing variable
uint8_t Cycle : 1; // Cycle variable
void (*mModeCallback)(void); // Sector mode callback
} ws2812bfx_s;
I am aware that some parameters are redundant for certain modes. Fortunately, this is not a huge cost. I will optimize it in the future as needed.
Library fundamentals
For proper operation, the library requires software timers and a callback in the main loop.
void WS2812BFX_SysTickCallback(void);
I place the SysTick callback in the predefined SysTick callback located in the HAL libraries. Since it is a weak function, I conveniently put it in the fourth user code section of the main.c file. This is similar to the previous version of the library.
void WS2812BFX_Callback(void);
The FX library callback function, similar to the previous version, handles invoking subsequent effects. It must be placed in the main loop.
Creating segments
In the example, I split the entire strip into 3 segments. After initializing LED control, you need to call the effect initialization using the function
FX_STATUS WS2812BFX_Init(uint8_t Segments);
This function sets up the array of structures responsible for segments. It takes as an argument the number of segments into which the strip will be divided. This function divides the strip into equal parts. The function can also add another segment while remembering the settings of all previous ones. Removing a segment causes the information about the last one to be irretrievably lost, while every new segment above the previous count has default settings and is paused – you need to start it. For convenient addition and removal of segments, you can use the prepared functions.
FX_STATUS WS2812BFX_SegmentIncrease(void); FX_STATUS WS2812BFX_SegmentDecrease(void);
Segment configuration
Alright, but I’d like different lengths for these segments. I anticipated that with several convenient functions.
FX_STATUS WS2812BFX_SegmentIncreaseEnd(uint8_t Segment); FX_STATUS WS2812BFX_SegmentDecreaseEnd(uint8_t Segment); FX_STATUS WS2812BFX_SegmentIncreaseStart(uint8_t Segment); FX_STATUS WS2812BFX_SegmentDecreaseStart(uint8_t Segment);
As the names suggest, with them we can move the first and last point within a segment. These operations are safe and do not allow two neighboring segments to overlap. Therefore, for example, to increase segment no. 1 at the end, you must first move the start of segment no. 2.
There is one more, less safe function.
FX_STATUS WS2812BFX_SetSegmentSize(uint8_t Segment, uint16_t Start, uint16_t Stop);
It no longer prevents segments from overlapping. I didn’t implement guarding for a simple reason. Everyone may imagine shifting a neighboring segment differently. Because what should be done when the neighboring segment already has only one LED? Remove it or shorten another one? Or maybe shorten all segments evenly as you expand? I leave that to you, and with this function you can freely configure the lengths of individual effects.
Setting colors
Each mode requires at least one color to operate. The maximum number of colors required by any mode is 3. Each has its own ID by which it can be referenced. A color can be set in four ways.
void WS2812BFX_SetColorStruct(uint8_t id, ws2812b_color c); void WS2812BFX_SetColorRGB(uint8_t id, uint8_t r, uint8_t g, uint8_t b); void WS2812BFX_SetColorHSV(uint8_t id, uint16_t h, uint8_t s, uint8_t v); void WS2812BFX_SetColor(uint8_t id, uint32_t c);
The effect of calling any of the above functions is the same. Note – these functions assign the color to temporary variables. To apply them to a specific segment, you must afterward call the function that starts the selected operating mode on the given segment.
Operations on modes
Changing the mode of individual segments seems to be the most important function in the operation of a WS2812B LED strip.
FX_STATUS WS2812BFX_SetMode(uint8_t Segment, fx_mode Mode); FX_STATUS WS2812BFX_NextMode(uint8_t Segment); FX_STATUS WS2812BFX_PrevMode(uint8_t Segment); FX_STATUS WS2812BFX_SetReverse(uint8_t Segment, uint8_t Reverse);
The names, as usual, speak for themselves. You can set a specific mode for a selected segment or choose the next or previous mode. The functions do not allow you to go beyond the list of available modes.
The last two functions concern setting the direction of the animation for some modes, either one way or the other.
Besides changing modes, the library allows you to control the operation of a given segment.
FX_STATUS WS2812BFX_Start(uint8_t Segment); FX_STATUS WS2812BFX_Stop(uint8_t Segment);
These functions start or stop the animation on the selected segment. At the moment of start, the mode’s timers are reset and the colors stored in the temporary color settings variables are assigned – the segment will run from the beginning rather than continue from the stop point.
Animation speed
The animation speed for each segment is set separately using the functions below.
FX_STATUS WS2812BFX_SetSpeed(uint8_t Segment, uint16_t Speed); FX_STATUS WS2812BFX_IncreaseSpeed(uint8_t Segment, uint16_t Speed); FX_STATUS WS2812BFX_DecreaseSpeed(uint8_t Segment, uint16_t Speed);
The value of Speed in the first function is expressed in milliseconds between each step in a mode. The exceptions are two modes – FX_MODE_WHITE_TO_COLOR and FX_MODE_BLACK_TO_COLOR – for which this value is the time needed for the transition black/white -> color -> black/white, also expressed in milliseconds.
The last two functions increase the already set speed in the segment by the value from the Speed argument.
Collecting information about segments
For the purposes of external communication, e.g., via UART or USB, there is a need to remotely check the configuration of the entire strip controller.
uint8_t WS2812BFX_GetSegmentsQuantity(void); FX_STATUS WS2812BFX_IsRunning(uint8_t Segment, uint8_t *Running); FX_STATUS WS2812BFX_GetMode(uint8_t Segment, fx_mode *Mode); FX_STATUS WS2812BFX_GetReverse(uint8_t Segment, uint8_t *Reverse); FX_STATUS WS2812BFX_GetSegmentSize(uint8_t Segment, uint16_t *Start, uint16_t *Stop); FX_STATUS WS2812BFX_GetSpeed(uint8_t Segment, uint16_t *Speed); FX_STATUS WS2812BFX_GetColorRGB(uint8_t id, uint8_t *r, uint8_t *g, uint8_t *b);
All these functions return the appropriate operating parameters of the strip and segments. They return them via the pointers provided in the arguments. As you can see, each function returns an FX_STATUS value. This is related to the successful execution of the function. FX_STATUS has two states.
typedef enum {
FX_OK = 0,
FX_ERROR = 1
} FX_STATUS;
FX_ERROR, as is easy to guess, indicates an error. For example, it may be an out-of-range error. Such an error will occur when trying to read the mode of segment no. 7 while only 5 are active.
Example
I recorded a video that showcases the operation of segments and their configuration. For this purpose, I implemented USB communication via a virtual COM port and simple message parsing. Please don’t pay attention to the correctness of the parsing because there are many ways to do it, and the one I implemented here is quite primitive.
Summary
As I mentioned in the first paragraph, for now this is the end of the series on WS2812B addressable LEDs. As possibilities and reader feedback allow, I will fix possible bugs and implement interesting proposals. I encourage you to send me your ideas.
Splitting an LED strip into segments offers amazing customization options. I will probably set something up on the window or on the Christmas tree during the upcoming holidays. I also see the use of segments in interior or furniture decoration. Only our imagination limits us 😉
Thank you for reading this post. If you like this kind of topic, let me know in the comments. I would also be grateful for suggestions of topics you’d like me to cover.
The code is, as usual, available on my GitHub: link
If you have noticed any error, disagree with something, would like to add something important, or simply want to discuss this topic, write a comment. Remember that the discussion should be polite and in accordance with the rules of the Polish language.
This article is part of the series:



0 Comments