-
-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathledchaincomm.hpp
More file actions
698 lines (553 loc) · 30.1 KB
/
ledchaincomm.hpp
File metadata and controls
698 lines (553 loc) · 30.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
// SPDX-License-Identifier: GPL-3.0-or-later
//
// Copyright (c) 2016-2025 plan44.ch / Lukas Zeller, Zurich, Switzerland
//
// Author: Lukas Zeller <luz@plan44.ch>
//
// This file is part of p44utils.
//
// p44utils is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// p44utils is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with p44utils. If not, see <http://www.gnu.org/licenses/>.
//
#ifndef __p44utils__ledchaincomm__
#define __p44utils__ledchaincomm__
#include "p44utils_main.hpp"
#include "colorutils.hpp"
#if ENABLE_P44LRGRAPHICS
#include "p44view.hpp"
#endif
#ifndef LEDCHAIN_LEGACY_API
#define LEDCHAIN_LEGACY_API (!ENABLE_P44LRGRAPHICS) // with p44graphics, we don't need legacy API any more
#endif
#ifndef LEDCHAIN_READBACK
#define LEDCHAIN_READBACK (!ENABLE_P44LRGRAPHICS) // with p44graphics, we don't need reading back LED values
#endif
#ifndef ENABLE_LEDCHAIN_UART
#define ENABLE_LEDCHAIN_UART (!ESP_PLATFORM && !ENABLE_RPIWS281X) // only for standard leddata-to-device platforms (ledchain...)
#endif
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#define LED_UPDATE_STATS 1 // 1 = just stats counters, 2 = step log that uses some ram
#ifdef ESP_PLATFORM
// we use the esp32_ws281x code using RMT peripheral to generate correct timing
extern "C" {
#include "esp32_ws281x.h"
}
#elif ENABLE_RPIWS281X
// we use the rpi_ws281x library to communicate with the LED chains on RaspberryPi
extern "C" {
#include "clk.h"
#include "gpio.h"
#include "dma.h"
#include "pwm.h"
#include "ws2811.h"
}
#endif // ENABLE_RPIWS281X
using namespace std;
namespace p44 {
#define PWMBITS 16 // number of PWM bit resolution
#define PWMMAX ((1<<PWMBITS)-1)
#if PWMBITS>8
typedef uint16_t LEDChannelPower;
#else
typedef uint8_t LEDChannelPower;
#endif
class LEDChainComm;
typedef boost::intrusive_ptr<LEDChainComm> LEDChainCommPtr;
class LEDPowerTable : public P44Obj
{
public:
LEDChannelPower mData[PIXELMAX+1];
};
typedef boost::intrusive_ptr<LEDPowerTable> LEDPowerTablePtr;
class LEDPowerConverter : public P44Obj
{
LEDPowerTablePtr mTables[4];
/// create an exponential brightness-to-power table
/// @param aTableNo 0..3 table index
/// @param aExponent the exponent for the table
/// @param aMinPower power to output at brightness==1, or 0 to use the curve without offset.
/// When >0, the curve is offset and scaled to fi
void createExpTable(int aTableNo, double aExponent, LEDChannelPower aMinPower);
protected:
// fast access pointers to conversion tables
const LEDChannelPower* mRedPowers;
const LEDChannelPower* mGreenPowers;
const LEDChannelPower* mBluePowers;
const LEDChannelPower* mWhitePowers;
public:
LEDPowerConverter(double aExponent, LEDChannelPower aMinPower);
LEDPowerConverter(double aColorExponent, LEDChannelPower aMinColorPower, double aWhiteExponent, LEDChannelPower aMinWhitePower);
LEDPowerConverter(double aColorExponent, LEDChannelPower aMinRedPower, LEDChannelPower aMinGreenPower, LEDChannelPower aMinBluePower, LEDChannelPower aMinWhitePower);
virtual ~LEDPowerConverter();
static LEDPowerConverter& standardPowerConverter();
/// get powers for color components
/// @param aDimDown if zero: no dimming=100%, otherwise, 255..1 proportional dimming
void powersForComponents(
PixelColorComponent aDimDown,
PixelColorComponent aRed, PixelColorComponent aGreen, PixelColorComponent aBlue, PixelColorComponent aWhite,
LEDChannelPower& aRedPwr, LEDChannelPower& aGreenPwr, LEDChannelPower& aBluePwr, LEDChannelPower& aWhitePwr
) const;
};
typedef boost::intrusive_ptr<LEDPowerConverter> LEDPowerConverterPtr;
class LEDChainComm : public P44Obj
{
friend class LEDChainArrangement;
public:
typedef enum {
ledlayout_none, ///< if MSB of LEDCHAIN_PARAM_LEDTYPE is set to this, MSB=layout, LSB=chip
ledlayout_rgb,
ledlayout_grb,
ledlayout_rgbw,
ledlayout_grbw,
// new modes added 2022-11-23 (also in p44-ledchain)
ledlayout_rbg,
ledlayout_gbr,
ledlayout_brg,
ledlayout_bgr,
ledlayout_rbgw,
ledlayout_gbrw,
ledlayout_brgw,
ledlayout_bgrw,
num_ledlayouts
} LedLayout;
// Note: this enum must correspond with p44-ledchain kernel module's LedChip_t
typedef enum {
ledchip_none,
ledchip_ws2811,
ledchip_ws2812,
ledchip_ws2813,
ledchip_ws2815,
ledchip_p9823,
ledchip_sk6812,
ledchip_ws2816,
ledchip_ws2813n,
num_ledchips
} LedChip;
typedef struct {
const char *name; // name of the chip
int idleChipMw; // [mW] idle power consumption per LED chip (LEDs all off)
int rgbChannelMw; // [mW] power consumption per R,G,B LED channel with full brightness (or total in case of rgbCommonCurrent==true)
int whiteChannelMw; // [mW] power consumption of white channel (0 if none)
uint8_t numBytesPerChannel; // number of bytes per channel
bool rgbCommonCurrent; // if set, the LEDs are in a serial circuit with bridging PWM shortcuts, so max(r,g,b) defines consumption, not sum(r,g,b)
bool slowTiming; // if set, slower WS2811/12 timing will be used when sending via UART
} LedChipDesc;
private:
typedef P44Obj inherited;
LedChip mLedChip; ///< type of LED chips in the chain
LedLayout mLedLayout; ///< color layout in the LED chips
uint16_t mTMaxPassive_uS; ///< max passive bit time in uS
uint8_t mMaxRetries; ///< max number of update retries (for some low level drivers that may need to retry when IRQ hits at the wrong time)
string mDeviceName; ///< the LED device name
uint16_t mInactiveStartLeds; ///< number of inactive LEDs at the start of the chain
uint16_t mInactiveBetweenLeds; ///< number of inactive LEDs between physical rows
uint16_t mInactiveEndLeds; ///< number of inactive LEDs at the end of the chain (we have buffer for them, but do not set colors for them)
uint16_t mNumLeds; ///< number of LEDs
uint16_t mLedsPerRow; ///< number of LEDs per row (physically, along WS2812 chain)
uint16_t mNumRows; ///< number of rows (sections of WS2812 chain)
bool mXReversed; ///< even (0,2,4...) rows go backwards, or all if not alternating
bool mYReversed; ///< columns go backwards
bool mAlternating; ///< direction changes after every row
bool mXYSwap; ///< x and y swapped
bool mInitialized;
uint8_t mNumColorComponents; // depends on ledType
uint8_t mNumBytesPerComponent; // depends on ledType
LEDChainCommPtr mChainDriver; // the LED chain used for outputting LED values. Usually: myself, but if this instance just maps a second part of another chain, this will point to the other chain
LEDPowerConverterPtr mLEDPowerConverter; // the converter to use for converting pixel colors to output values
#ifdef ESP_PLATFORM
int mGpioNo; // the GPIO to be used
Esp_ws281x_LedChain* mEspLedChain; // handle for the chain
Esp_ws281x_pixel* mPixels; // the pixel buffer
#elif ENABLE_RPIWS281X
ws2811_t mRPiWS281x; // the descriptor for the rpi_ws281x library
#else
int mLedFd; // the file descriptor for the LED device (p44ledchain or UART)
#if ENABLE_LEDCHAIN_UART
bool mUartOutput; // if true, output device is UART and we'll synthesize WS28xx timing with 7-1-N @ 2.5/3.0 MBaud
pthread_t mUartSenderThread;
pthread_mutex_t mUartSenderMutex;
pthread_cond_t mUartSenderCond;
bool mUartSendflag;
bool mUartSenderShutdown;
#endif // ENABLE_LEDCHAIN_UART
uint8_t *mRawBuffer; // the raw bytes to be sent to the WS28xx device (possibly translated in UART mode)
size_t mRawBytes; // number of bytes to send from ledbuffer, including header
uint8_t *mLedBuffer; // the first led in the raw buffer (in case there is a header)
#endif
#if ENABLE_P44LRGRAPHICS
Row3 mLEDWhite; ///< the color and intensity of the white channel LED (if any). @note this is used only in LEDChainArrangement
#endif
public:
/// create driver for a WS2812 LED chain
/// @param aLedType type of LED chips in "<chip>.<layout>[.<TMaxPassive_uS>]" form or just "<ledtypename>"
/// @param aDeviceName the name of the LED chain device (if any, depends on platform)
/// - ledchain device: full path like /dev/ledchain1
/// - ESP32: name must be "gpioX" with X being the output pin to be used
/// - RPi library (ENABLE_RPIWS281X): unused
/// @param aNumLeds number of LEDs in the chain (physically)
/// @param aLedsPerRow number of consecutive LEDs in the WS2812 chain that build a row (active LEDs, not counting ainactiveBetweenLeds)
/// (usually x direction, y if swapXY was set). Set to 0 for single line of LEDs
/// @param aXReversed X direction is reversed
/// @param aAlternating X direction is reversed in first row, normal in second, reversed in third etc..
/// @param aSwapXY X and Y reversed (for up/down wiring)
/// @param aYReversed Y direction is reversed
/// @param aInactiveStartLeds number of extra LEDs at the beginning of the chain that are not active
/// @param aInactiveBetweenLeds number of extra LEDs between rows that are not active
/// @param aInactiveEndLeds number of LEDs at the end of the chain that are not mapped by this instance (but might be in use by other instances which use this one with setChainDriver())
LEDChainComm(
const string aLedType,
const string aDeviceName,
uint16_t aNumLeds,
uint16_t aLedsPerRow=0,
bool aXReversed=false,
bool aAlternating=false,
bool aXYSwap=false,
bool aYReversed=false,
uint16_t aInactiveStartLeds=0,
uint16_t aInactiveBetweenLeds=0,
uint16_t aInactiveEndLeds=0
);
/// destructor
~LEDChainComm();
/// @return device (driver) name of this chain. Must be unqiuely identify the actual hardware output channel
const string &getDeviceName() { return mDeviceName; };
/// set this LedChainComm to use another chain's actual output driver, i.e. only act as mapping
/// layer. This allows to have multiple different mappings on the same chain (i.e. part of the chain wound as tube,
/// extending into a linear chain etc.)
/// @param aLedChainComm a LedChainComm to be used to output LED values
/// @note must be called before begin()
void setChainDriver(LEDChainCommPtr aLedChainComm);
/// sets a custom pixelcolor-to-outputpower converter
/// @param aLedPowerConverter the converter to be used
/// @note if none is set, the standard converter will be used
void setPowerConverter(LEDPowerConverterPtr aLedPowerConverter);
/// @return the current power converter
/// note: will create the standard converter on demand if none has been set before
const LEDPowerConverter& powerConverter();
/// @return true if this LedChainComm acts as a hardware driver (and not as a secondary)
bool isHardwareDriver() { return mChainDriver==NULL; };
/// begin using the driver
/// @param aHintAtTotalChains if not 0, this is a hint to the total number of total chains, which the driver can use
/// for efficiently allocating internal resources (e.g. ESP32 driver can use more RMT memory when less channels are in use)
bool begin(size_t aHintAtTotalChains = 0);
/// end using the driver
void end();
/// transfer RGB values to LED chain
/// @note this must be called to update the actual LEDs after modifying RGB values
/// with setColor() and/or setColorDimmed()
/// @note if this is a secondary mapping, show() does nothing - only the driving chain can transfer value to the hardware
void show();
/// clear all LEDs to off (but must call show() to actually show it
void clear();
/// get minimal color intensity that does not completely switch off the color channel of the LED
/// @return minimum r,g,b value
PixelColorComponent getMinVisibleColorIntensity();
/// @return true if chains has a separate (fourth) white channel
bool hasWhite() { return mNumColorComponents>=4; }
/// @return the LED chip descriptor which includes information about power consumption
const LedChipDesc &ledChipDescriptor() const;
#if ENABLE_LEDCHAIN_UART
void uartSenderThreadWorker();
void triggerUartSend();
#endif
#if LEDCHAIN_LEGACY_API && PWMBITS==8
/// set color of one LED
/// @param aRed intensity of red component, 0..255
/// @param aGreen intensity of green component, 0..255
/// @param aBlue intensity of blue component, 0..255
/// @param aWhite intensity of separate white component for RGBW LEDs, 0..255
/// @note aLedNumber is the logical LED number, and aX/aY are logical coordinates, excluding any inactive LEDs
void setColorXY(uint16_t aX, uint16_t aY, uint8_t aRed, uint8_t aGreen, uint8_t aBlue, uint8_t aWhite = 0);
void setColor(uint16_t aLedNumber, uint8_t aRed, uint8_t aGreen, uint8_t aBlue, uint8_t aWhite=0);
/// set color of one LED, scaled by a visible brightness (non-linear) factor
/// @param aRed intensity of red component, 0..255
/// @param aGreen intensity of green component, 0..255
/// @param aBlue intensity of blue component, 0..255
/// @param aWhite intensity of separate white component for RGBW LEDs, 0..255
/// @param aBrightness overall brightness, 0..255
/// @note aLedNumber is the logical LED number, and aX/aY are logical coordinates, excluding any inactive LEDs
void setColorDimmedXY(uint16_t aX, uint16_t aY, uint8_t aRed, uint8_t aGreen, uint8_t aBlue, uint8_t aWhite, uint8_t aBrightness);
void setColorDimmed(uint16_t aLedNumber, uint8_t aRed, uint8_t aGreen, uint8_t aBlue, uint8_t aWhite, uint8_t aBrightness);
#if LEDCHAIN_READBACK
/// get current color of LED
/// @param aRed set to intensity of red component, 0..255
/// @param aGreen set to intensity of green component, 0..255
/// @param aBlue set to intensity of blue component, 0..255
/// @param aWhite set to intensity of separate white component for RGBW LEDs, 0..255
/// @note for LEDs set with setColorDimmed(), this returns the scaled down RGB values,
/// not the original r,g,b parameters. Note also that internal brightness resolution is 5 bits only.
/// @note aLedNumber is the logical LED number, and aX/aY are logical coordinates, excluding any inactive LEDs
void getColorXY(uint16_t aX, uint16_t aY, uint8_t &aRed, uint8_t &aGreen, uint8_t &aBlue, uint8_t &aWhite);
void getColor(uint16_t aLedNumber, uint8_t &aRed, uint8_t &aGreen, uint8_t &aBlue, uint8_t &aWhite);
#endif // LEDCHAIN_READBACK
#endif // LEDCHAIN_LEGACY_API && PWMBITS==8
#if LEDCHAIN_READBACK
/// set power (PWM value) of one LED
/// @param aX logical X coordinate
/// @param aY logical Y coordinate
/// @param aRed set to power of red component, 0..PWMMAX
/// @param aGreen set to power of green component, 0..PWMMAX
/// @param aBlue set to power of blue component, 0..PWMMAX
/// @param aWhite set to power of separate white component for RGBW LEDs, 0..PWMMAX
void getPowerXY(uint16_t aX, uint16_t aY, LEDChannelPower &aRed, LEDChannelPower &aGreen, LEDChannelPower &aBlue, LEDChannelPower &aWhite);
#endif // LEDCHAIN_READBACK
/// set raw power (PWM value) of one LED
/// @param aX logical X coordinate
/// @param aY logical Y coordinate
/// @param aRed power of red component, 0..PWMMAX
/// @param aGreen power of green component, 0..PWMMAX
/// @param aBlue power of blue component, 0..PWMMAX
/// @param aWhite power of separate white component for RGBW LEDs, 0..PWMMAX
void setPowerXY(uint16_t aX, uint16_t aY, LEDChannelPower aRed, LEDChannelPower aGreen, LEDChannelPower aBlue, LEDChannelPower aWhite = 0);
/// set raw power (PWM value) of one LED
/// @param aLedNumber is the logical LED number
/// @param aRed power of red component, 0..PWMMAX
/// @param aGreen power of green component, 0..PWMMAX
/// @param aBlue power of blue component, 0..PWMMAX
/// @param aWhite power of separate white component for RGBW LEDs, 0..PWMMAX
void setPower(uint16_t aLedNumber, LEDChannelPower aRed, LEDChannelPower aGreen, LEDChannelPower aBlue, LEDChannelPower aWhite = 0);
/// @return number of active LEDs in the chain (that are active, i.e. minus inactiveStartLeds/inactiveBetweenLeds/inactiveEndLeds)
uint16_t getNumLeds();
/// @return size of array in X direction (x range is 0..getSizeX()-1)
uint16_t getSizeX();
/// @return size of array in Y direction (y range is 0..getSizeY()-1)
uint16_t getSizeY();
private:
uint16_t ledIndexFromXY(uint16_t aX, uint16_t aY);
/// set power at raw LED index with no mapping calculations in between
void setPowerAtLedIndex(uint16_t aLedIndex, LEDChannelPower aRed, LEDChannelPower aGreen, LEDChannelPower aBlue, LEDChannelPower aWhite);
#if LEDCHAIN_READBACK
/// get power at raw LED index with no mapping calculations in between
void getPowerAtLedIndex(uint16_t aLedIndex, LEDChannelPower &aRed, LEDChannelPower &aGreen, LEDChannelPower &aBlue, LEDChannelPower &aWhite);
#endif // LEDCHAIN_READBACK
#if LEDCHAIN_LEGACY_API && PWMBITS==8
/// set color at raw LED index with no mapping calculations in between
void setColorAtLedIndex(uint16_t aLedIndex, uint8_t aRed, uint8_t aGreen, uint8_t aBlue, uint8_t aWhite);
#if LEDCHAIN_READBACK
/// set color from raw LED index with no mapping calculations in between
void getColorAtLedIndex(uint16_t aLedIndex, uint8_t &aRed, uint8_t &aGreen, uint8_t &aBlue, uint8_t &aWhite);
#endif // LEDCHAIN_READBACK
#endif // LEDCHAIN_LEGACY_API && PWMBITS==8
};
#if ENABLE_P44LRGRAPHICS
class LEDChainArrangement;
typedef boost::intrusive_ptr<LEDChainArrangement> LEDChainArrangementPtr;
class LEDChainArrangement : public P44LoggingObj
{
class LEDChainFixture {
public:
LEDChainFixture(LEDChainCommPtr aLedChain, PixelRect aCovers, PixelPoint aOffset) : ledChain(aLedChain), covers(aCovers), offset(aOffset) {};
LEDChainCommPtr ledChain; ///< a LED chain
PixelRect covers; ///< the rectangle in the arrangement covered by this LED chain
PixelPoint offset; ///< offset within the LED chain where to start coverage
};
typedef vector<LEDChainFixture> LedChainVector;
LedChainVector mLedChains;
P44ViewPtr mRootView;
bool mStarted; // set when started
PixelRect mCovers;
MLTicket mAutoStepTicket;
MLMicroSeconds mNextAutoStep; ///< next scheduled autostep
MLMicroSeconds mCurrentStepShowTime; ///< time we are calculating for in this step (usually in the future)
MLMicroSeconds mNextStepShowTime; ///< time we should calculate for again for the next step (definitely in the future)
MLMicroSeconds mEarliestNextDispApply; ///< when we can apply the next display update, earliest
MLMicroSeconds mRenderPendingFor; ///< time we should render the current view state for
MLMicroSeconds mDispApplyPendingFor; ///< time when display update should be applied next time (as precisely as possible)
uint32_t mPowerLimitMw; ///< max power (accumulated PWM values of all LEDs)
uint32_t mRequestedLightPowerMw; ///< light power currently requested (but possibly not actually output if >powerLimit)
uint32_t mActualLightPowerMw; ///< light power actually used after dimming down because of limit
bool mPowerLimited; ///< set while power is limited
MLMicroSeconds mSlowDetected; ///< when last timing hickup was detected and warned
MLMicroSeconds mMinUpdateInterval; ///< minimum interval kept between updates to LED chain hardware
MLMicroSeconds mMaxPriorityInterval; ///< maximum interval during which noisy view children or base view animations are prevented from requesting non-aligned rendering updates
MLMicroSeconds mBufferTime; ///< how much in advance of real time should we run the step calculation time
bool mRenderWithApply; ///< if true, rendering is done with apply at apply time (assuming render time is mostly constant and does not cause jitter)
uint16_t mMainDim; // 255 = 1:1, 0..254 dimmed down, 256..65535 = dimmed up (max factor 255)
#if LED_UPDATE_STATS
MLMicroSeconds mStatsBase;
long mNumSteps;
long mNumViewSteps;
long mNumRenders;
long mNumApplys;
MLMicroSeconds mMinStepTime;
MLMicroSeconds mMaxStepTime;
MLMicroSeconds mMinRenderTime;
MLMicroSeconds mMaxRenderTime;
MLMicroSeconds mMinApplyDelay;
MLMicroSeconds mMaxApplyDelay;
long mNumBufferTimesInserted;
#if LED_UPDATE_STATS>1
typedef struct {
MLMicroSeconds entered; // real time of entering the step
long looped;
MLMicroSeconds currentStepShowTime;
MLMicroSeconds nextStepShowTime;
MLMicroSeconds renderPendingFor;
MLMicroSeconds dispApplyPendingFor;
MLMicroSeconds dispApplied;
MLMicroSeconds schedulenext;
MLMicroSeconds left; // realtime of leaving the step
} StepLogEntry;
static const size_t cStepLogSize = 1000;
StepLogEntry mStepLog[cStepLogSize];
size_t mStepLogIdx;
size_t mLogged;
#endif // LED_UPDATE_STATS>1
#endif // LED_UPDATE_STATS
public:
#if LED_UPDATE_STATS
void resetStats();
void showStats();
#endif
LEDChainArrangement();
virtual ~LEDChainArrangement();
/// @return the object type (used for context descriptions such as logging context)
virtual string contextType() const P44_OVERRIDE { return "LEDchains"; };
/// clear all LEDs to off
void clear();
/// set the root view
/// @param aRootView the view to ask for pixels in the arrangement
void setRootView(P44ViewPtr aRootView);
/// @return return current root view
P44ViewPtr getRootView() { return mRootView; }
/// add a LED chain to the arrangement
/// @param aLedChain the LED chain
/// @param aCover the rectangle of pixels in the arrangement the led chain covers
/// @param aOffset an offset within the chain where coverage starts
void addLEDChain(LEDChainCommPtr aLedChain, PixelRect aCover, PixelPoint aOffset);
/// add a LED chain to the arrangement by specification
/// @param aChainSpec string describing the LED chain parameters and the coverage in the arrangement
/// @note config syntax is as follows:
/// [chaintype:[leddevicename:]]numberOfLeds[:x:dx:y:dy:firstoffset:betweenoffset][:XYSA]
/// - chaintype can be SK6812, P9823 or WS281x
/// - XYSA are optional flags for X,Y reversal, x<>y Swap, Alternating chain direction
void addLEDChain(const string &aChainSpec);
/// remove all LED chains
void removeAllChains();
/// returns the enclosing rectangle over all LED chains
PixelRect totalCover() { return mCovers; }
/// get minimal color intensity that does not completely switch off the color channel of the LED
/// @return minimum r,g,b value
uint8_t getMinVisibleColorIntensity();
/// get the current overall dim factor
/// @return the overall brightness dimming factor (1=100%)
double getMainDimFactor();
/// set the current overall dim factor
/// @param aMainDimFactor the overall brightness dimming factor (1=100%)
void setMainDimFactor(double aMainDimFactor);
/// limit total power, dim LED chain output accordingly
/// @param aMilliWatts how many milliwatts (approximatively) the total arrangement chains may use, 0=no limit
void setPowerLimit(int aMilliWatts);
/// get current power limit
/// @return currently set power limit in milliwatts, 0=no limit
int getPowerLimit();
/// Return the power it *would* need to display the current state (altough power limiting might actually reducing it)
/// @return how many milliwatts (approximatively) the total of all chains would use if not limited
int getNeededPower();
/// Return the current power (possibly limited)
/// @return how many milliwatts (approximatively) all chains currently consume
int getCurrentPower();
/// Return minimal update interval between LED chain updates (= max frame rate)
/// @param aMinUpdateInterval the minimal interval between LED chain updates
MLMicroSeconds getMinUpdateInterval() { return mMinUpdateInterval; }
/// Set the minimal update interval between LED chain updates (= max frame rate)
/// The installed LED chain drivers will not be called more often than that, and
/// the root view will get informed of that update interval as a hint for scheduling
/// of animations etc.
/// @param aMinUpdateInterval the minimal interval between LED chain updates
/// @note this also sets a default buffer time
void setMinUpdateInterval(MLMicroSeconds aMinUpdateInterval);
/// Set the buffer time separately
/// @note setMinUpdateInterval automatically sets the buffer time to a default value
/// so call this *after* setMinUpdateInterval() to set a non-default buffer time
void setBufferTime(MLMicroSeconds aBufferTime);
/// Set the rendering timing mode:
/// - true means rendering late just before displaying (catching changes from multiple steps)
/// - false means rendering early (as soon as a step returns a change, possibly shifting further changes to the next display cycle)
void setRenderWithApply(bool aRenderWithApply);
/// Set the maximum priority interval, that is the interval after an display update when only prioritized views
/// (such as scrollers that must run smoothly) will request updates when they get dirty. Other views
/// getting dirty during that time will have to wait.
/// @param aMaxPriorityInterval the maximum interval after display updates can only be requested by prioritized views
void setMaxPriorityInterval(MLMicroSeconds aMaxPriorityInterval);
/// start LED chains
/// @param aAutoStep if true, step() will be called automatically as demanded by the view hierarchy
void begin(bool aAutoStep);
/// start chains only recently added (after first call to begin()
void startChains();
/// request re-rendering now
/// @note this should be called when views have been changed from the outside
/// @note a step() will be triggered ASAP
void render();
/// stop LED chains and auto updates
void end();
/// Factory helper to create ledchain arrangement with one or multiple led chains from --ledchain command line options
/// @param aLedChainArrangement if not set, new arrangement will be created, otherwise existing one is used
/// @param aChainSpec string describing the LED chain parameters and the coverage in the arrangement,
/// or "none" to not really add a ledchain, but instantiate an empty arrangement that can be configured later
/// e.g. by using p44script.
static void addLEDChain(LEDChainArrangementPtr &aLedChainArrangement, const string &aChainSpec);
#if ENABLE_APPLICATION_SUPPORT
/// - option to construct LEDChainArrangement from command line
#define CMDLINE_LEDCHAIN_OPTIONS \
{ 0, "ledchain", true, "[ledtype:[leddevicename:]]numberOfLeds:[x:dx:y:dy:firstoffs:betweenoffs][XYSA][W#whitecolor];" \
" enable support for adressable LED chains forming RGB(W) pixel display areas:" \
"\n- ledtype is of <chip>.<layout> form, or one of WS2812, WS2813, SK6812, P9823 standard types." \
"\n Chips: WS2811,WS2812,WS2813,WS2815,SK6812,P9823,none" \
"\n Layouts: RGB,GRB,RGBW,GRBW,RBG,GBR,BRG,BGR,RBGW,GBRW,BRGW,BGRW" \
"\n- leddevicename specifies the hardware output (usually LED device path)" \
"\n- x,dx,y,dy,firstoffs,betweenoffs specify how the chain is mapped to the display space." \
"\n- XYSA are flags: X or Y: x or y reversed, S: x/y swapped, A: alternating (zigzag)." \
"\n- #whitecolor is a web color specification for the white channel of RGBW chains." \
"\nNote: this option can be used multiple times to combine ledchains." }, \
{ 0, "ledpowerlimit", true, "max_mW;maximal power in milliwatts the entire LED arrangement is allowed to consume (approximately)." \
"If power would exceed limit, all LEDs are dimmed to stay below limit. Defaults to 0=no limit." }, \
{ 0, "ledrefresh", true, "update_ms;minimal number of milliseconds between LED chain updates. Defaults to 15ms." }
/// process ledchain arrangement specific command line options
void processCmdlineOptions();
#endif // ENABLE_APPLICATION_SUPPORT
private:
/// perform needed LED chain updates
/// @return time when step() should be called again latest
MLMicroSeconds step();
void recalculateCover();
/// render the LED update from the current view state, but do not yet apply it to the hardware
void renderViewState();
/// apply the already calculated display update
/// @note: this routine should be called with as precise timing as possible for smooth rendering
void applyDisplayUpdate();
void autoStep(MLTimer &aTimer);
void externalUpdateRequest();
};
#if ENABLE_P44SCRIPT
namespace P44Script {
/// represents the (singular) LED chain arrangement in this setup
class LEDChainLookup : public BuiltInMemberLookup
{
typedef BuiltInMemberLookup inherited;
LEDChainArrangement& mLedChainArrangement;
public:
LEDChainLookup(LEDChainArrangement& aLedChainArrangement);
LEDChainArrangement& ledChainArrangement() { return mLedChainArrangement; }
};
} // namespace P44Script
#endif // ENABLE_P44SCRIPT
#endif // ENABLE_P44LRGRAPHICS
} // namespace p44
#endif // __p44utils__ledchaincomm__