SBK_BarDrive Library 2.2.0
LED bar meter control and queued animations for Arduino
Loading...
Searching...
No Matches
SBK_BarDrive.h
Go to the documentation of this file.
1
31
52
53#pragma once
54
62#ifndef SBK_BARDRIVE_WITH_ANIM
63#pragma message(" ⚠️ SBK_BARDRIVE_WITH_ANIM is not defined. SBK BarDrive Built-in animations are disabled and cannot be accessed, saving memory if they are not needed. If you do need the animations library, define SBK_BARDRIVE_WITH_ANIM before including SBK_BarDrive.h.")
64#endif
65
66// IMPORTANT: Include the appropriate driver before SBK_BarDrive.h
67// e.g., #include <SBK_MAX72xxSoft.h>, <SBK_MAX72xxHard.h> or <SBK_HT16K33.h>
68#if !defined(SBK_MAX72xx_IS_DEFINED) && !defined(SBK_HT16K33_IS_DEFINED)
69#pragma message(" ⚠️ SBK BarDrive library note: Include an SBK_MAX72xx or SBK_HT16K33 driver header and declare a driver before creating a bar meter instance.")
70#endif
71
72#include <Arduino.h>
73#ifdef SBK_BARDRIVE_WITH_ANIM
75#endif
76
93
102enum class BarDirection : uint8_t
103{
106};
107
124template <typename DriverT>
126{
127public:
141 SBK_BarMeter(DriverT *driver,
142 uint8_t devIdx,
145 uint8_t rowOffset = 0,
146 uint8_t colOffset = 0)
147 : _driver(driver),
148 _devIdx(constrain(devIdx, 0, 7)),
149 _matrixPreset(matrixPreset),
150 _direction(direction)
151 {
152 _isMatrixMapped = true;
153 if (_devIdx > (driver->devsNum() - 1))
154 {
155 _initializePresetMapping(MatrixPreset::NONE);
156 _segsNum = 0;
157 _rowsNum = 0;
158 _colsNum = 0;
159 _rowOffset = 0;
160 _colOffset = 0;
161 }
162 else
163 {
164 _initializePresetMapping(matrixPreset);
165 _rowOffset = constrain(rowOffset, 0, _driver->maxRows(_devIdx) - 1);
166 _colOffset = constrain(colOffset, 0, _driver->maxColumns() - 1);
167 }
168 }
169
189 SBK_BarMeter(DriverT *driver,
190 uint8_t devIdx,
191 uint8_t rowsNum,
192 uint8_t colsNum,
194 uint8_t rowOffset = 0,
195 uint8_t colOffset = 0)
196 : _driver(driver),
197 _devIdx(constrain(devIdx, 0, 7)),
198 _direction(direction)
199 {
200 _isMatrixMapped = true;
201 if (_devIdx > (driver->devsNum() - 1))
202 {
203 _segsNum = 0;
204 _rowsNum = 0;
205 _colsNum = 0;
206 _rowOffset = 0;
207 _colOffset = 0;
208 }
209 else
210 {
211 _rowsNum = constrain(rowsNum, 1, 255);
212 _colsNum = constrain(colsNum, 1, driver->maxColumns());
213 _segsNum = _rowsNum * _colsNum;
214 _rowOffset = constrain(rowOffset, 0, _driver->maxRows(_devIdx) - 1);
215 _colOffset = constrain(colOffset, 0, _driver->maxColumns() - 1);
216 }
217 }
218
236 SBK_BarMeter(DriverT *driver,
237 uint8_t devIdx,
238 uint8_t segsNum,
240 uint8_t segOffset = 0)
241 : _driver(driver),
242 _devIdx(constrain(devIdx, 0, 7)),
243 _direction(direction),
244 _segsNum(segsNum)
245 {
246 _isMatrixMapped = false;
247 if (_devIdx > (driver->devsNum() - 1))
248 {
249 _segsNum = 0;
250 _rowsNum = 0;
251 _colsNum = 0;
252 _segOffset = 0;
253 }
254 else
255 {
256 _rowsNum = _driver->maxRows(_devIdx);
257 _colsNum = _driver->maxColumns();
258 _segOffset = constrain(segOffset, 0, _driver->maxSegments(_devIdx) - 1);
259 }
260 }
261
282 template <size_t N>
283 SBK_BarMeter(DriverT *driver,
284 uint8_t devIdx,
285 const uint8_t (&mapping)[N][3],
287 bool progmem = false,
288 uint8_t rowOffset = 0,
289 uint8_t colOffset = 0)
290 : _driver(driver),
291 _devIdx(constrain(devIdx, 0, 7)),
292 _customMapping(mapping),
293 _direction(direction),
294 _userMappingIsProgmem(progmem)
295 {
296 _isMatrixMapped = true;
297 if (_devIdx > (driver->devsNum() - 1))
298 {
299 _segsNum = 0;
300 _rowsNum = 0;
301 _colsNum = 0;
302 _rowOffset = 0;
303 _colOffset = 0;
304 }
305 else
306 {
307 _segsNum = N;
308 _rowsNum = _driver->maxRows(_devIdx);
309 _colsNum = _driver->maxColumns();
310 _rowOffset = constrain(rowOffset, 0, _driver->maxRows(_devIdx) - 1);
311 _colOffset = constrain(colOffset, 0, _driver->maxColumns() - 1);
312 }
313 }
314
315 ~SBK_BarMeter() { /* Nothing to clean up for now*/ }
316
326 void show() { _driver->show(); }
327
331 void clear()
332 {
333 for (uint8_t i = 0; i < _segsNum; ++i)
334 setPixel(i, false);
335 }
336
341 void setDirection(BarDirection dir) { _direction = dir; }
342
347 BarDirection getDirection() const { return _direction; }
348
353 uint8_t getSegsNum() const { return _segsNum; }
354
368 void debugSegmentMapping(Stream &stream = Serial)
369 {
370 for (uint8_t i = 0; i < _segsNum; ++i)
371 {
372 uint8_t devIdx = _devIdx;
373 uint8_t rowIdx, colIdx;
374 _getMappedDevRowCol(i, &devIdx, &rowIdx, &colIdx);
375 stream.print(F("Segment "));
376 stream.print(i);
377 stream.print(F(" → Device "));
378 stream.print(devIdx);
379 stream.print(F(", Row "));
380 stream.print(rowIdx);
381 stream.print(F(", Col "));
382 stream.println(colIdx);
383 }
384 }
385
396
397 void setPixel(uint8_t segment, uint8_t state)
398 {
399 if (segment >= _segsNum || !_driver)
400 return;
401
402 uint8_t devIdx = _devIdx;
403 uint8_t rowIdx, colIdx;
404 _getMappedDevRowCol(segment, &devIdx, &rowIdx, &colIdx);
405 _driver->setLed(devIdx, rowIdx, colIdx, state != 0);
406 }
407
420 uint8_t getPixelState(uint8_t segment) const
421 {
422 if (segment >= _segsNum || !_driver)
423 return false;
424
425 uint8_t devIdx = _devIdx;
426 uint8_t rowIdx, colIdx;
427
428 _getMappedDevRowCol(segment, &devIdx, &rowIdx, &colIdx);
429 return _driver->getLed(devIdx, rowIdx, colIdx);
430 }
431
442 {
443 _segOffset = offset;
444 return *this;
445 }
446
457 SBK_BarMeter &setMatrixOffset(uint8_t rowOffset, uint8_t colOffset)
458 {
459 _rowOffset = rowOffset;
460 _colOffset = colOffset;
461 return *this;
462 }
463
464private:
465 void _initializePresetMapping(MatrixPreset matrixPreset)
466 {
467
468 if (matrixPreset == MatrixPreset::SBK_BarMeter_SK28)
469 matrixPreset = MatrixPreset::BL28_3005SK;
470 if (matrixPreset == MatrixPreset::SBK_BarMeter_SA28)
471 matrixPreset = MatrixPreset::BL28_3005SA;
472
473 switch (matrixPreset)
474 {
476 _rowsNum = _driver->maxRows(_devIdx);
477 _colsNum = _driver->maxColumns();
478 _segsNum = _rowsNum * _colsNum;
479 _isMatrixMapped = false;
480 break;
482 _segsNum = 28;
483 _rowsNum = 4;
484 _colsNum = 7;
485 _isMatrixMapped = true;
486 break;
488 _segsNum = 28;
489 _rowsNum = 7;
490 _colsNum = 4;
491 _isMatrixMapped = true;
492 break;
493 default:
494 _rowsNum = _driver->maxRows(_devIdx);
495 _colsNum = _driver->maxColumns();
496 _segsNum = _rowsNum * _colsNum;
497 _isMatrixMapped = false;
498 break;
499 }
500 }
501
502 void _getMappedDevRowCol(uint8_t seg, uint8_t *devIdx, uint8_t *rowIdx, uint8_t *colIdx) const
503 {
504 // Apply direction correction
505 uint8_t mappedSeg = (_direction == BarDirection::REVERSE)
506 ? (_segsNum - 1 - seg)
507 : seg;
508
509 // Add segment offset only in segment-based mode
510 mappedSeg += (_isMatrixMapped ? 0 : _segOffset);
511
512 if (_customMapping)
513 {
514 // Handle custom mappings
515 if (_userMappingIsProgmem)
516 {
517 *devIdx = pgm_read_byte(&_customMapping[mappedSeg][0]);
518 *rowIdx = pgm_read_byte(&_customMapping[mappedSeg][1]);
519 *colIdx = pgm_read_byte(&_customMapping[mappedSeg][2]);
520 }
521 else
522 {
523 *devIdx = _customMapping[mappedSeg][0];
524 *rowIdx = _customMapping[mappedSeg][1] + _rowOffset;
525 *colIdx = _customMapping[mappedSeg][2] + _colOffset;
526 }
527 return;
528 }
529
530 // Auto mapping
531
532 const uint8_t segPerDev = _driver->maxRows(*devIdx) * _driver->maxColumns();
533 const uint8_t baseDevIdx = *devIdx + (mappedSeg / segPerDev);
534 const uint8_t localIdx = mappedSeg % segPerDev;
535
536 if (_isMatrixMapped)
537 {
538 // Matrix-style layout: column-major mapping
539 uint8_t baseRowIdx = localIdx % _rowsNum;
540 uint8_t baseColIdx = localIdx / _rowsNum;
541
542 *devIdx = baseDevIdx;
543 *rowIdx = baseRowIdx + _rowOffset;
544 *colIdx = baseColIdx + _colOffset;
545 }
546 else
547 {
548 // Segment-style layout: row-major linear index
549 uint8_t baseRowIdx = localIdx / _driver->maxColumns();
550 uint8_t baseColIdx = localIdx % _driver->maxColumns();
551
552 *devIdx = baseDevIdx;
553 *rowIdx = baseRowIdx;
554 *colIdx = baseColIdx;
555 }
556 }
557
558 DriverT *_driver;
559 const uint8_t _devIdx;
560 MatrixPreset _matrixPreset = MatrixPreset::NONE;
561 const uint8_t (*_customMapping)[3] = nullptr;
562 BarDirection _direction;
563 uint8_t _segOffset = 0; // existing logic
564 uint8_t _rowOffset = 0; // for matrix displays
565 uint8_t _colOffset = 0; // for matrix displays
566 bool _isMatrixMapped = false; // whether to apply row/col offset
567 uint8_t _segsNum;
568 uint8_t _rowsNum = 0;
569 uint8_t _colsNum = 0;
570 bool _userMappingIsProgmem = false;
571};
572
573// -----------------------------
574// SBK_BarDrive wrapper
575// -----------------------------
589template <typename DriverT>
591{
592public:
606 SBK_BarDrive(DriverT *driver,
607 uint8_t devIdx,
608 MatrixPreset matrixPreset,
610 uint8_t rowOffset = 0,
611 uint8_t colOffset = 0)
612 : _barMeter(driver, devIdx, matrixPreset, direction, rowOffset, colOffset)
613#ifdef SBK_BARDRIVE_WITH_ANIM
614 ,
615 _barAnimations(_barMeter)
616#endif
617 {
618#ifdef SBK_BARDRIVE_WITH_ANIM
619 _barAnimations.setSegsNum(_barMeter.getSegsNum());
620#endif
621 }
622
642 SBK_BarDrive(DriverT *driver,
643 uint8_t devIdx,
644 uint8_t rowsNum,
645 uint8_t colsNum,
647 uint8_t rowOffset = 0,
648 uint8_t colOffset = 0)
649 : _barMeter(driver, devIdx, rowsNum, colsNum, direction, rowOffset, colOffset)
650#ifdef SBK_BARDRIVE_WITH_ANIM
651 ,
652 _barAnimations(_barMeter)
653#endif
654 {
655#ifdef SBK_BARDRIVE_WITH_ANIM
656 _barAnimations.setSegsNum(_barMeter.getSegsNum());
657#endif
658 }
659
677 SBK_BarDrive(DriverT *driver,
678 uint8_t devIdx,
679 uint8_t segsNum,
681 uint8_t segOffset = 0)
682 : _barMeter(driver, devIdx, segsNum, direction, segOffset)
683#ifdef SBK_BARDRIVE_WITH_ANIM
684 ,
685 _barAnimations(_barMeter)
686#endif
687 {
688#ifdef SBK_BARDRIVE_WITH_ANIM
689 _barAnimations.setSegsNum(_barMeter.getSegsNum());
690#endif
691 }
692
713 template <size_t N>
714 SBK_BarDrive(DriverT *driver,
715 uint8_t devIdx,
716 const uint8_t (&mapping)[N][3],
718 bool progmem = false,
719 uint8_t rowOffset = 0,
720 uint8_t colOffset = 0)
721 : _barMeter(driver, devIdx, mapping, direction, progmem, rowOffset, colOffset)
722#ifdef SBK_BARDRIVE_WITH_ANIM
723 ,
724 _barAnimations(_barMeter)
725#endif
726 {
727#ifdef SBK_BARDRIVE_WITH_ANIM
728 _barAnimations.setSegsNum(_barMeter.getSegsNum());
729#endif
730 }
731
732 ~SBK_BarDrive() { /* Nothing to clean up for now*/ }
733
738 SBK_BarMeter<DriverT> &barmeter() { return _barMeter; }
739#ifdef SBK_BARDRIVE_WITH_ANIM
745#endif
746
756 void show() { _barMeter.show(); }
757
761 void clear() { _barMeter.clear(); }
762
767 void setDirection(BarDirection dir) { _barMeter.setDirection(dir); }
768
773 BarDirection getDirection() { return _barMeter.getDirection(); }
774
779 uint8_t getSegsNum() { return _barMeter.getSegsNum(); }
780
794 void debugSegmentMapping(Stream &stream = Serial) { _barMeter.debugSegmentMapping(stream); }
795
806 void setPixel(uint8_t segment, uint8_t state) { _barMeter.setPixel(segment, state); }
807
820 uint8_t getPixelState(uint8_t segment) { return _barMeter.getPixelState(segment); }
821
832 {
833 _barMeter.setSegmentOffset(offset);
834 return *this;
835 }
836
847 SBK_BarDrive &setMatrixOffset(uint8_t rowOffset, uint8_t colOffset)
848 {
849 _barMeter.setMatrixOffset(rowOffset, colOffset);
850 return *this;
851 }
852
853private:
854 SBK_BarMeter<DriverT> _barMeter;
855#ifdef SBK_BARDRIVE_WITH_ANIM
861#endif
862};
MatrixPreset
Enables built-in animation support for SBK_BarDrive.
@ SBK_BarMeter_SA28
Alias for SBK BarMeter SA28, uses the BL28_3005SA preset internally.
@ BL28_3005SK
Native mapping for BL28-3005SK (4 anodes × 7 cathodes), common cathode.
@ SBK_BarMeter_SK28
Alias for SBK BarMeter SK28, uses the BL28_3005SK preset internally.
@ NONE
No preset selected; manual segment mapping is required.
@ BL28_3005SA
Native mapping for BL28-3005SA (7 anodes × 4 cathodes), common anode.
BarDirection
Direction of bar fill or animation progression.
@ REVERSE
From last segment to first.
@ FORWARD
From first segment to last.
Built-in animation engine for SBK_BarMeter-based LED displays.
SBK_BarDrive(DriverT *driver, uint8_t devIdx, const uint8_t(&mapping)[N][3], BarDirection direction=BarDirection::FORWARD, bool progmem=false, uint8_t rowOffset=0, uint8_t colOffset=0)
Construct a SBK_BarDrive using a custom pixel mapping array.
void clear()
Clear all bar segments.
uint8_t getPixelState(uint8_t segment)
Get the current state of a bar segment (pixel).
uint8_t getSegsNum()
Get the total number of segments in the bar.
void show()
Push the current LED state buffer to the physical display.
void setDirection(BarDirection dir)
Set the bar fill direction.
SBK_BarDrive(DriverT *driver, uint8_t devIdx, MatrixPreset matrixPreset, BarDirection direction=BarDirection::FORWARD, uint8_t rowOffset=0, uint8_t colOffset=0)
Construct a SBK_BarDrive using a predefined matrix-style layout.
void setPixel(uint8_t segment, uint8_t state)
Set the on/off state of a logical segment (LED).
SBK_BarDrive & setSegmentOffset(uint8_t offset)
Apply a segment-wise offset (shifts segment index before mapping to row/col).
SBK_BarDrive & setMatrixOffset(uint8_t rowOffset, uint8_t colOffset)
Apply a matrix-style physical offset for row/column positioning.
SBK_BarDrive(DriverT *driver, uint8_t devIdx, uint8_t segsNum, BarDirection direction=BarDirection::FORWARD, uint8_t segOffset=0)
Construct a SBK_BarDrive with a fixed number of vertical segments (non-matrix layout).
void debugSegmentMapping(Stream &stream=Serial)
Print the segment-to-device mapping for debugging purposes.
SBK_BarDrive(DriverT *driver, uint8_t devIdx, uint8_t rowsNum, uint8_t colsNum, BarDirection direction=BarDirection::FORWARD, uint8_t rowOffset=0, uint8_t colOffset=0)
Construct a SBK_BarDrive with a custom matrix size (rows × columns layout).
BarDirection getDirection()
Get the current bar fill direction.
SBK_BarMeterAnimations< SBK_BarMeter< DriverT > > & animations()
Get the animation controller instance (if enabled).
SBK_BarMeter< DriverT > & barmeter()
Get the underlying SBK_BarMeter instance.
Templated animation controller for SBK_BarMeter<T>.
Template class for controlling segment-based LED bar meters using row/column mappings.
SBK_BarMeter & setMatrixOffset(uint8_t rowOffset, uint8_t colOffset)
Apply a matrix-style physical offset for row/column positioning.
void debugSegmentMapping(Stream &stream=Serial)
Print the segment-to-device mapping for debugging purposes.
void clear()
Clear all bar segments.
SBK_BarMeter(DriverT *driver, uint8_t devIdx, uint8_t rowsNum, uint8_t colsNum, BarDirection direction=BarDirection::FORWARD, uint8_t rowOffset=0, uint8_t colOffset=0)
Construct a SBK_BarMeter with a custom matrix size (rows × columns layout).
void show()
Push the current LED state buffer to the physical display.
SBK_BarMeter(DriverT *driver, uint8_t devIdx, const uint8_t(&mapping)[N][3], BarDirection direction=BarDirection::FORWARD, bool progmem=false, uint8_t rowOffset=0, uint8_t colOffset=0)
Construct a SBK_BarMeter using a custom pixel mapping array.
SBK_BarMeter(DriverT *driver, uint8_t devIdx, MatrixPreset matrixPreset=MatrixPreset::BL28_3005SK, BarDirection direction=BarDirection::FORWARD, uint8_t rowOffset=0, uint8_t colOffset=0)
Construct a SBK_BarMeter using a predefined matrix-style layout.
void setDirection(BarDirection dir)
Set the bar fill direction.
BarDirection getDirection() const
Get the current bar fill direction.
SBK_BarMeter & setSegmentOffset(uint8_t offset)
Apply a segment-wise offset (shifts segment index before mapping to row/col).
void setPixel(uint8_t segment, uint8_t state)
Set the on/off state of a logical segment (LED).
uint8_t getPixelState(uint8_t segment) const
Get the current state of a bar segment (pixel).
SBK_BarMeter(DriverT *driver, uint8_t devIdx, uint8_t segsNum, BarDirection direction=BarDirection::FORWARD, uint8_t segOffset=0)
Construct a SBK_BarMeter with a fixed number of vertical segments (non-matrix layout).
uint8_t getSegsNum() const
Get the total number of segments in the bar.