SBK_BarDrive Library 2.1.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 _rowOffset = constrain(rowOffset, 0, _driver->maxRows(_devIdx) - 1);
214 _colOffset = constrain(colOffset, 0, _driver->maxColumns() - 1);
215 }
216 }
217
235 SBK_BarMeter(DriverT *driver,
236 uint8_t devIdx,
237 uint8_t segsNum,
239 uint8_t segOffset = 0)
240 : _driver(driver),
241 _devIdx(constrain(devIdx, 0, 7)),
242 _direction(direction),
243 _segsNum(segsNum)
244 {
245 _isMatrixMapped = false;
246 if (_devIdx > (driver->devsNum() - 1))
247 {
248 _segsNum = 0;
249 _rowsNum = 0;
250 _colsNum = 0;
251 _segOffset = 0;
252 }
253 else
254 {
255 _rowsNum = _driver->maxRows(_devIdx);
256 _colsNum = _driver->maxColumns();
257 _segOffset = constrain(segOffset, 0, _driver->maxSegments(_devIdx) - 1);
258 }
259 }
260
281 template <size_t N>
282 SBK_BarMeter(DriverT *driver,
283 uint8_t devIdx,
284 const uint8_t (&mapping)[N][3],
286 bool progmem = false,
287 uint8_t rowOffset = 0,
288 uint8_t colOffset = 0)
289 : _driver(driver),
290 _devIdx(constrain(devIdx, 0, 7)),
291 _customMapping(mapping),
292 _direction(direction),
293 _userMappingIsProgmem(progmem)
294 {
295 _isMatrixMapped = true;
296 if (_devIdx > (driver->devsNum() - 1))
297 {
298 _segsNum = 0;
299 _rowsNum = 0;
300 _colsNum = 0;
301 _rowOffset = 0;
302 _colOffset = 0;
303 }
304 else
305 {
306 _segsNum = N;
307 _rowsNum = _driver->maxRows(_devIdx);
308 _colsNum = _driver->maxColumns();
309 _rowOffset = constrain(rowOffset, 0, _driver->maxRows(_devIdx) - 1);
310 _colOffset = constrain(colOffset, 0, _driver->maxColumns() - 1);
311 }
312 }
313
314 ~SBK_BarMeter() { /* Nothing to clean up for now*/ }
315
325 void show() { _driver->show(); }
326
330 void clear()
331 {
332 for (uint8_t i = 0; i < _segsNum; ++i)
333 setPixel(i, false);
334 }
335
340 void setDirection(BarDirection dir) { _direction = dir; }
341
346 BarDirection getDirection() const { return _direction; }
347
352 uint8_t getSegsNum() const { return _segsNum; }
353
367 void debugSegmentMapping(Stream &stream = Serial)
368 {
369 for (uint8_t i = 0; i < _segsNum; ++i)
370 {
371 uint8_t devIdx = _devIdx;
372 uint8_t rowIdx, colIdx;
373 _getMappedDevRowCol(i, &devIdx, &rowIdx, &colIdx);
374 stream.print(F("Segment "));
375 stream.print(i);
376 stream.print(F(" → Device "));
377 stream.print(devIdx);
378 stream.print(F(", Row "));
379 stream.print(rowIdx);
380 stream.print(F(", Col "));
381 stream.println(colIdx);
382 }
383 }
384
395
396 void setPixel(uint8_t segment, uint8_t state)
397 {
398 if (segment >= _segsNum || !_driver)
399 return;
400
401 uint8_t devIdx = _devIdx;
402 uint8_t rowIdx, colIdx;
403 _getMappedDevRowCol(segment, &devIdx, &rowIdx, &colIdx);
404 _driver->setLed(devIdx, rowIdx, colIdx, state != 0);
405 }
406
419 uint8_t getPixelState(uint8_t segment) const
420 {
421 if (segment >= _segsNum || !_driver)
422 return false;
423
424 uint8_t devIdx = _devIdx;
425 uint8_t rowIdx, colIdx;
426
427 _getMappedDevRowCol(segment, &devIdx, &rowIdx, &colIdx);
428 return _driver->getLed(devIdx, rowIdx, colIdx);
429 }
430
441 {
442 _segOffset = offset;
443 return *this;
444 }
445
456 SBK_BarMeter &setMatrixOffset(uint8_t rowOffset, uint8_t colOffset)
457 {
458 _rowOffset = rowOffset;
459 _colOffset = colOffset;
460 return *this;
461 }
462
463private:
464 void _initializePresetMapping(MatrixPreset matrixPreset)
465 {
466
467 if (matrixPreset == MatrixPreset::SBK_BarMeter_SK28)
468 matrixPreset = MatrixPreset::BL28_3005SK;
469 if (matrixPreset == MatrixPreset::SBK_BarMeter_SA28)
470 matrixPreset = MatrixPreset::BL28_3005SA;
471
472 switch (matrixPreset)
473 {
475 _rowsNum = _driver->maxRows(_devIdx);
476 _colsNum = _driver->maxColumns();
477 _segsNum = _rowsNum * _colsNum;
478 _isMatrixMapped = false;
479 break;
481 _segsNum = 28;
482 _rowsNum = 4;
483 _colsNum = 7;
484 _isMatrixMapped = true;
485 break;
487 _segsNum = 28;
488 _rowsNum = 7;
489 _colsNum = 4;
490 _isMatrixMapped = true;
491 break;
492 default:
493 _rowsNum = _driver->maxRows(_devIdx);
494 _colsNum = _driver->maxColumns();
495 _segsNum = _rowsNum * _colsNum;
496 _isMatrixMapped = false;
497 break;
498 }
499 }
500
501 void _getMappedDevRowCol(uint8_t seg, uint8_t *devIdx, uint8_t *rowIdx, uint8_t *colIdx) const
502 {
503 // Apply direction correction
504 uint8_t mappedSeg = (_direction == BarDirection::REVERSE)
505 ? (_segsNum - 1 - seg)
506 : seg;
507
508 // Add segment offset only in segment-based mode
509 mappedSeg += (_isMatrixMapped ? 0 : _segOffset);
510
511 if (_customMapping)
512 {
513 // Handle custom mappings
514 if (_userMappingIsProgmem)
515 {
516 *devIdx = pgm_read_byte(&_customMapping[mappedSeg][0]);
517 *rowIdx = pgm_read_byte(&_customMapping[mappedSeg][1]);
518 *colIdx = pgm_read_byte(&_customMapping[mappedSeg][2]);
519 }
520 else
521 {
522 *devIdx = _customMapping[mappedSeg][0];
523 *rowIdx = _customMapping[mappedSeg][1] + _rowOffset;
524 *colIdx = _customMapping[mappedSeg][2] + _colOffset;
525 }
526 return;
527 }
528
529 // Auto mapping
530
531 const uint8_t segPerDev = _driver->maxRows(*devIdx) * _driver->maxColumns();
532 const uint8_t baseDevIdx = *devIdx + (mappedSeg / segPerDev);
533 const uint8_t localIdx = mappedSeg % segPerDev;
534
535 if (_isMatrixMapped)
536 {
537 // Matrix-style layout: column-major mapping
538 uint8_t baseRowIdx = localIdx % _rowsNum;
539 uint8_t baseColIdx = localIdx / _rowsNum;
540
541 *devIdx = baseDevIdx;
542 *rowIdx = baseRowIdx + _rowOffset;
543 *colIdx = baseColIdx + _colOffset;
544 }
545 else
546 {
547 // Segment-style layout: row-major linear index
548 uint8_t baseRowIdx = localIdx / _driver->maxColumns();
549 uint8_t baseColIdx = localIdx % _driver->maxColumns();
550
551 *devIdx = baseDevIdx;
552 *rowIdx = baseRowIdx;
553 *colIdx = baseColIdx;
554 }
555 }
556
557 DriverT *_driver;
558 const uint8_t _devIdx;
559 MatrixPreset _matrixPreset = MatrixPreset::NONE;
560 const uint8_t (*_customMapping)[3] = nullptr;
561 BarDirection _direction;
562 uint8_t _segOffset = 0; // existing logic
563 uint8_t _rowOffset = 0; // for matrix displays
564 uint8_t _colOffset = 0; // for matrix displays
565 bool _isMatrixMapped = false; // whether to apply row/col offset
566 uint8_t _segsNum;
567 uint8_t _rowsNum = 0;
568 uint8_t _colsNum = 0;
569 bool _userMappingIsProgmem = false;
570};
571
572// -----------------------------
573// SBK_BarDrive wrapper
574// -----------------------------
588template <typename DriverT>
590{
591public:
605 SBK_BarDrive(DriverT *driver,
606 uint8_t devIdx,
607 MatrixPreset matrixPreset,
609 uint8_t rowOffset = 0,
610 uint8_t colOffset = 0)
611 : _barMeter(driver, devIdx, matrixPreset, direction, rowOffset, colOffset)
612#ifdef SBK_BARDRIVE_WITH_ANIM
613 ,
614 _barAnimations(_barMeter)
615#endif
616 {
617#ifdef SBK_BARDRIVE_WITH_ANIM
618 _barAnimations.setSegsNum(_barMeter.getSegsNum());
619#endif
620 }
621
641 SBK_BarDrive(DriverT *driver,
642 uint8_t devIdx,
643 uint8_t rowsNum,
644 uint8_t colsNum,
646 uint8_t rowOffset = 0,
647 uint8_t colOffset = 0)
648 : _barMeter(driver, devIdx, rowsNum, colsNum, direction, rowOffset, colOffset)
649#ifdef SBK_BARDRIVE_WITH_ANIM
650 ,
651 _barAnimations(_barMeter)
652#endif
653 {
654#ifdef SBK_BARDRIVE_WITH_ANIM
655 _barAnimations.setSegsNum(_barMeter.getSegsNum());
656#endif
657 }
658
676 SBK_BarDrive(DriverT *driver,
677 uint8_t devIdx,
678 uint8_t segsNum,
680 uint8_t segOffset = 0)
681 : _barMeter(driver, devIdx, segsNum, direction, segOffset)
682#ifdef SBK_BARDRIVE_WITH_ANIM
683 ,
684 _barAnimations(_barMeter)
685#endif
686 {
687#ifdef SBK_BARDRIVE_WITH_ANIM
688 _barAnimations.setSegsNum(_barMeter.getSegsNum());
689#endif
690 }
691
712 template <size_t N>
713 SBK_BarDrive(DriverT *driver,
714 uint8_t devIdx,
715 const uint8_t (&mapping)[N][3],
717 bool progmem = false,
718 uint8_t rowOffset = 0,
719 uint8_t colOffset = 0)
720 : _barMeter(driver, devIdx, mapping, direction, progmem, rowOffset, colOffset)
721#ifdef SBK_BARDRIVE_WITH_ANIM
722 ,
723 _barAnimations(_barMeter)
724#endif
725 {
726#ifdef SBK_BARDRIVE_WITH_ANIM
727 _barAnimations.setSegsNum(_barMeter.getSegsNum());
728#endif
729 }
730
731 ~SBK_BarDrive() { /* Nothing to clean up for now*/ }
732
737 SBK_BarMeter<DriverT> &barmeter() { return _barMeter; }
738#ifdef SBK_BARDRIVE_WITH_ANIM
744#endif
745
755 void show() { _barMeter.show(); }
756
760 void clear() { _barMeter.clear(); }
761
766 void setDirection(BarDirection dir) { _barMeter.setDirection(dir); }
767
772 BarDirection getDirection() { return _barMeter.getDirection(); }
773
778 uint8_t getSegsNum() { return _barMeter.getSegsNum(); }
779
793 void debugSegmentMapping(Stream &stream = Serial) { _barMeter.debugSegmentMapping(stream); }
794
805 void setPixel(uint8_t segment, uint8_t state) { _barMeter.setPixel(segment, state); }
806
819 uint8_t getPixelState(uint8_t segment) { return _barMeter.getPixelState(segment); }
820
831 {
832 _barMeter.setSegmentOffset(offset);
833 return *this;
834 }
835
846 SBK_BarDrive &setMatrixOffset(uint8_t rowOffset, uint8_t colOffset)
847 {
848 _barMeter.setMatrixOffset(rowOffset, colOffset);
849 return *this;
850 }
851
852private:
853 SBK_BarMeter<DriverT> _barMeter;
854#ifdef SBK_BARDRIVE_WITH_ANIM
860#endif
861};
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.