SBK_BarDrive Library 2.0.4
Modular animation and control library for bar meter LEDs
Loading...
Searching...
No Matches
SBK_BarDrive.h
Go to the documentation of this file.
1
31
52
53#pragma once
54
63#ifndef SBK_BARDRIVE_WITH_ANIM
64#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.")
65#endif
66
67// IMPORTANT: Include the appropriate driver before SBK_BarDrive.h
68// e.g., #include <SBK_MAX72xxSoft.h>, <SBK_MAX72xxHard.h> or <SBK_HT16K33.h>
69#if !defined(SBK_MAX72xx_IS_DEFINED) && !defined(SBK_HT16K33_IS_DEFINED)
70#pragma message(" ⚠️ SBK BarDrive library note : Neither SBK_MAX72xx.h or SBK_HT16K33.h are included in the code. You need to include the file for your driver matrixPreset and declare a driver before any bar meter instance can be created.")
71#endif
72
73#include <Arduino.h>
74#ifdef SBK_BARDRIVE_WITH_ANIM
76#endif
77
94
103enum class BarDirection : uint8_t
104{
107};
108
125template <typename DriverT>
127{
128public:
142 SBK_BarMeter(DriverT *driver,
143 uint8_t devIdx,
146 uint8_t rowOffset = 0,
147 uint8_t colOffset = 0)
148 : _driver(driver),
149 _devIdx(constrain(devIdx, 0, 7)),
150 _matrixPreset(matrixPreset),
151 _direction(direction)
152 {
153 _isMatrixMapped = true;
154 if (_devIdx > (driver->devsNum() - 1))
155 {
156 _initializePresetMapping(MatrixPreset::NONE);
157 _segsNum = 0;
158 _rowsNum = 0;
159 _colsNum = 0;
160 _rowOffset = 0;
161 _colOffset = 0;
162 }
163 else
164 {
165 _initializePresetMapping(matrixPreset);
166 _rowOffset = constrain(rowOffset, 0, _driver->maxRows(_devIdx) - 1);
167 _colOffset = constrain(colOffset, 0, _driver->maxColumns() - 1);
168 }
169 }
170
190 SBK_BarMeter(DriverT *driver,
191 uint8_t devIdx,
192 uint8_t rowsNum,
193 uint8_t colsNum,
195 uint8_t rowOffset = 0,
196 uint8_t colOffset = 0)
197 : _driver(driver),
198 _devIdx(constrain(devIdx, 0, 7)),
199 _direction(direction)
200 {
201 _isMatrixMapped = true;
202 if (_devIdx > (driver->devsNum() - 1))
203 {
204 _segsNum = 0;
205 _rowsNum = 0;
206 _colsNum = 0;
207 _rowOffset = 0;
208 _colOffset = 0;
209 }
210 else
211 {
212 _rowsNum = constrain(rowsNum, 1, 255);
213 _colsNum = constrain(colsNum, 1, driver->maxColumns());
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 _segsNum(segsNum),
244 _direction(direction)
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 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 const uint8_t (*_customMapping)[3] = nullptr;
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
744 SBK_BarMeterAnimations<SBK_BarMeter<DriverT>> &animations() { return _barAnimations; }
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
Preset types for common matrix-style bar meter hardware configurations.
Definition SBK_BarDrive.h:87
@ SBK_BarMeter_SA28
Alias for SBK BarMeter SA28, uses the BL28_3005SA preset internally.
Definition SBK_BarDrive.h:90
@ BL28_3005SK
Native mapping for BL28-3005SK (4 anodes × 7 cathodes), common cathode.
Definition SBK_BarDrive.h:91
@ SBK_BarMeter_SK28
Alias for SBK BarMeter SK28, uses the BL28_3005SK preset internally.
Definition SBK_BarDrive.h:89
@ NONE
No preset selected; manual segment mapping is required.
Definition SBK_BarDrive.h:88
@ BL28_3005SA
Native mapping for BL28-3005SA (7 anodes × 4 cathodes), common anode.
Definition SBK_BarDrive.h:92
BarDirection
Direction of bar fill or animation progression.
Definition SBK_BarDrive.h:104
@ REVERSE
From last segment to first.
Definition SBK_BarDrive.h:106
@ FORWARD
From first segment to last.
Definition SBK_BarDrive.h:105
Built-in animation engine for SBK_BarMeter-based LED displays.
Unified interface combining SBK_BarMeter logic with optional built-in animation support.
Definition SBK_BarDrive.h:591
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.
Definition SBK_BarDrive.h:714
void clear()
Clear all bar segments.
Definition SBK_BarDrive.h:761
uint8_t getPixelState(uint8_t segment)
Get the current state of a bar segment (pixel).
Definition SBK_BarDrive.h:820
uint8_t getSegsNum()
Get the total number of segments in the bar.
Definition SBK_BarDrive.h:779
void show()
Push the current LED state buffer to the physical display.
Definition SBK_BarDrive.h:756
void setDirection(BarDirection dir)
Set the bar fill direction.
Definition SBK_BarDrive.h:767
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.
Definition SBK_BarDrive.h:606
void setPixel(uint8_t segment, uint8_t state)
Set the on/off state of a logical segment (LED).
Definition SBK_BarDrive.h:806
SBK_BarDrive & setSegmentOffset(uint8_t offset)
Apply a segment-wise offset (shifts segment index before mapping to row/col).
Definition SBK_BarDrive.h:831
SBK_BarDrive & setMatrixOffset(uint8_t rowOffset, uint8_t colOffset)
Apply a matrix-style physical offset for row/column positioning.
Definition SBK_BarDrive.h:847
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).
Definition SBK_BarDrive.h:677
void debugSegmentMapping(Stream &stream=Serial)
Print the segment-to-device mapping for debugging purposes.
Definition SBK_BarDrive.h:794
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).
Definition SBK_BarDrive.h:642
BarDirection getDirection()
Get the current bar fill direction.
Definition SBK_BarDrive.h:773
SBK_BarMeter< DriverT > & barmeter()
Get the underlying SBK_BarMeter instance.
Definition SBK_BarDrive.h:738
Templated animation controller for SBK_BarMeter<T>.
Definition SBK_BarMeterAnimations.h:49
Template class for controlling segment-based LED bar meters using row/column mappings.
Definition SBK_BarDrive.h:127
SBK_BarMeter & setMatrixOffset(uint8_t rowOffset, uint8_t colOffset)
Apply a matrix-style physical offset for row/column positioning.
Definition SBK_BarDrive.h:457
void debugSegmentMapping(Stream &stream=Serial)
Print the segment-to-device mapping for debugging purposes.
Definition SBK_BarDrive.h:368
void clear()
Clear all bar segments.
Definition SBK_BarDrive.h:331
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).
Definition SBK_BarDrive.h:190
void show()
Push the current LED state buffer to the physical display.
Definition SBK_BarDrive.h:326
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.
Definition SBK_BarDrive.h:283
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.
Definition SBK_BarDrive.h:142
void setDirection(BarDirection dir)
Set the bar fill direction.
Definition SBK_BarDrive.h:341
BarDirection getDirection() const
Get the current bar fill direction.
Definition SBK_BarDrive.h:347
SBK_BarMeter & setSegmentOffset(uint8_t offset)
Apply a segment-wise offset (shifts segment index before mapping to row/col).
Definition SBK_BarDrive.h:441
void setPixel(uint8_t segment, uint8_t state)
Set the on/off state of a logical segment (LED).
Definition SBK_BarDrive.h:397
uint8_t getPixelState(uint8_t segment) const
Get the current state of a bar segment (pixel).
Definition SBK_BarDrive.h:420
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).
Definition SBK_BarDrive.h:236
uint8_t getSegsNum() const
Get the total number of segments in the bar.
Definition SBK_BarDrive.h:353