AskSin++
Dimmer.h
1 //- -----------------------------------------------------------------------------------------------------------------------
2 // AskSin++
3 // 2017-03-29 papa Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
4 //- -----------------------------------------------------------------------------------------------------------------------
5 
6 #ifndef __DIMMER_H__
7 #define __DIMMER_H__
8 
9 #include "MultiChannelDevice.h"
10 #include "Register.h"
11 #include "actors/PWM.h"
12 
13 #include <stdarg.h>
14 
15 #define LOGIC_INACTIVE 0
16 #define LOGIC_OR 1
17 #define LOGIC_AND 2
18 #define LOGIC_XOR 3
19 #define LOGIC_NOR 4
20 #define LOGIC_NAND 5
21 #define LOGIC_ORINVERS 6
22 #define LOGIC_ANDINVERS 7
23 #define LOGIC_PLUS 8
24 #define LOGIC_MINUS 9
25 #define LOGIC_MUL 10
26 #define LOGIC_PLUSINVERS 11
27 #define LOGIC_MINUSINVERS 12
28 #define LOGIC_MULINVERS 13
29 #define LOGIC_INVERSPLUS 14
30 #define LOGIC_INVERSMINUS 15
31 #define LOGIC_INVERSMUL 16
32 
33 // rampos & rampoff for dimmer is refon & refoff
34 #undef AS_CM_JT_RAMPON
35 #define AS_CM_JT_RAMPON AS_CM_JT_REFON
36 #undef AS_CM_JT_RAMPOFF
37 #define AS_CM_JT_RAMPOFF AS_CM_JT_REFOFF
38 
39 namespace as {
40 
41 DEFREGISTER(DimmerReg1,CREG_AES_ACTIVE,CREG_TRANSMITTRYMAX,CREG_OVERTEMPLEVEL,
42  CREG_REDUCETEMPLEVEL,CREG_REDUCELEVEL,CREG_POWERUPACTION,CREG_STATUSINFO,
43  CREG_CHARACTERISTIC,CREG_LOGICCOMBINATION)
44 
45 class DimmerList1 : public RegList1<DimmerReg1> {
46 public:
47  DimmerList1 (uint16_t addr) : RegList1<DimmerReg1>(addr) {}
48  void defaults () {
49  clear();
50  // aesActive(false);
51  transmitTryMax(6);
52  // powerUpAction(false);
53  statusInfoMinDly(4);
54  statusInfoRandom(1);
55 
56  overTempLevel(80);
57  reduceTempLevel(75);
58  reduceLevel(80);
59  characteristic(true);
60  logicCombination(LOGIC_OR);
61  }
62 };
63 
64 DEFREGISTER(DimmerReg3,PREG_CTRAMPONOFF,PREG_CTDELAYONOFF,PREG_CTONOFF,
65  PREG_CONDVALUELOW,PREG_CONDVALUEHIGH,PREG_ONDELAYTIME,PREG_ONTIME,
66  PREG_OFFDELAYTIME,PREG_OFFTIME,PREG_ACTIONTYPE,PREG_JTONOFF,
67  PREG_JTDELAYONOFF,PREG_JTRAMPONOFF,PREG_DELAYMODE,PREG_OFFLEVEL,
68  PREG_ONMINLEVEL,PREG_ONLEVEL,PREG_RAMPSTARTSTEP,PREG_RAMPONTIME,
69  PREG_RAMPOFFTIME,PREG_DIMMINLEVEL,PREG_DIMMAXLEVEL,PREG_DIMSTEP,
70  PREG_OFFDELAYSTEP,PREG_OFFDELAYNEWTIME,PREG_OFFDELAYOLDTIME,
71  PREG_ELSEACTIONTYPE,PREG_ELSEJTONOFF,PREG_ELSEJTDELAYONOFF,
72  PREG_ELSEJTRAMPONOFF)
73 
74 typedef RegList3<DimmerReg3> DimmerPeerList;
75 
76 class DimmerList3 : public ShortLongList<DimmerPeerList> {
77 public:
78  DimmerList3 (uint16_t addr) : ShortLongList<DimmerPeerList>(addr) {}
79  void defaults() {
80  DimmerPeerList ssl = sh();
81  ssl.clear();
82 // ssl.ctRampOn(0);
83 // ssl.ctRampOff(0);
84 // ssl.ctDlyOn(0);
85 // ssl.ctDlyOff(0);
86 // ssl.ctOn(0);
87 // ssl.ctOff(0);
88  ssl.ctValLo(0x32);
89  ssl.ctValHi(0x64);
90 // ssl.onDly(0);
91  ssl.onTime(0xff);
92 // ssl.offDly(0);
93  ssl.offTime(0xff);
94  ssl.actionType(AS_CM_ACTIONTYPE_JUMP_TO_TARGET);
95 // ssl.offTimeMode(false);
96 // ssl.onTimeMode(false);
97  ssl.offDelayBlink(true);
98 // ssl.offLevel(0);
99  ssl.onMinLevel(2);
100  ssl.onLevel(200); // 201 ???
101  ssl.rampStartStep(10);
102  ssl.rampOnTime(10);
103  ssl.rampOffTime(10);
104  ssl.dimMinLevel(10);
105  ssl.dimMaxLevel(200);
106  ssl.dimStep(5);
107  ssl.offDelayStep(10);
108  ssl.offDelayNewTime(5);
109  ssl.offDelayOldTime(5);
110  ssl.elseActionType(AS_CM_ACTIONTYPE_INACTIVE);
111 // ssl.elseOffTimeMode(false);
112 // ssl.elseOnTimeMode(false);
113  ssl.elseJtOn(AS_CM_JT_ONDELAY);
114  ssl.elseJtOff(AS_CM_JT_ONDELAY);
115  ssl.elseJtDlyOn(AS_CM_JT_ONDELAY);
116  ssl.elseJtDlyOff(AS_CM_JT_ONDELAY);
117  ssl.elseJtRampOn(AS_CM_JT_ONDELAY);
118  ssl.elseJtRampOff(AS_CM_JT_ONDELAY);
119 
120  ssl = lg();
121  ssl.clear();
122 // ssl.ctRampOn(0);
123 // ssl.ctRampOff(0);
124 // ssl.ctDlyOn(0);
125 // ssl.ctDlyOff(0);
126 // ssl.ctOn(0);
127 // ssl.ctOff(0);
128  ssl.ctValLo(0x32);
129  ssl.ctValHi(0x64);
130 // ssl.onDly(0);
131  ssl.onTime(0xff);
132 // ssl.offDly(0);
133  ssl.offTime(0xff);
134  ssl.actionType(AS_CM_ACTIONTYPE_JUMP_TO_TARGET);
135  ssl.multiExec(true);
136 // ssl.offTimeMode(false);
137 // ssl.onTimeMode(false);
138  ssl.offDelayBlink(true);
139 // ssl.offLevel(0);
140  ssl.onMinLevel(2);
141  ssl.onLevel(200); // 201 ???
142  ssl.rampStartStep(10);
143  ssl.rampOnTime(10);
144  ssl.rampOffTime(10);
145  ssl.dimMinLevel(10);
146  ssl.dimMaxLevel(200);
147  ssl.dimStep(5);
148  ssl.offDelayStep(10);
149  ssl.offDelayNewTime(5);
150  ssl.offDelayOldTime(5);
151  ssl.elseActionType(AS_CM_ACTIONTYPE_INACTIVE);
152 // ssl.elseOffTimeMode(false);
153 // ssl.elseOnTimeMode(false);
154  ssl.elseJtOn(AS_CM_JT_ONDELAY);
155  ssl.elseJtOff(AS_CM_JT_ONDELAY);
156  ssl.elseJtDlyOn(AS_CM_JT_ONDELAY);
157  ssl.elseJtDlyOff(AS_CM_JT_ONDELAY);
158  ssl.elseJtRampOn(AS_CM_JT_ONDELAY);
159  ssl.elseJtRampOff(AS_CM_JT_ONDELAY);
160  }
161 
162  void odd() {
163  defaults();
164  DimmerPeerList ssl = sh();
165  ssl.jtOn(AS_CM_JT_OFFDELAY);
166  ssl.jtOff(AS_CM_JT_OFF);
167  ssl.jtDlyOn(AS_CM_JT_RAMPOFF);
168  ssl.jtDlyOff(AS_CM_JT_RAMPOFF);
169  ssl.jtRampOn(AS_CM_JT_RAMPOFF);
170  ssl.jtRampOff(AS_CM_JT_OFF);
171  ssl = lg();
172  ssl.actionType(AS_CM_ACTIONTYPE_DOWNDIM);
173  }
174 
175  void even() {
176  defaults();
177  DimmerPeerList ssl = sh();
178  ssl.jtOn(AS_CM_JT_ON);
179  ssl.jtOff(AS_CM_JT_ONDELAY);
180  ssl.jtDlyOn(AS_CM_JT_RAMPON);
181  ssl.jtDlyOff(AS_CM_JT_RAMPON);
182  ssl.jtRampOn(AS_CM_JT_ON);
183  ssl.jtRampOff(AS_CM_JT_RAMPON);
184  ssl = lg();
185  ssl.actionType(AS_CM_ACTIONTYPE_UPDIM);
186  }
187 
188  void single() {
189  defaults();
190  DimmerPeerList ssl = sh();
191  ssl.jtOn(AS_CM_JT_OFFDELAY);
192  ssl.jtOff(AS_CM_JT_ONDELAY);
193  ssl.jtDlyOn(AS_CM_JT_RAMPON);
194  ssl.jtDlyOff(AS_CM_JT_RAMPOFF);
195  ssl.jtRampOn(AS_CM_JT_ON);
196  ssl.jtRampOff(AS_CM_JT_OFF);
197  ssl = lg();
198  ssl.actionType(AS_CM_ACTIONTYPE_TOGGLEDIM_TO_COUNTER);
199  }
200 
201 };
202 
203 
205 
206 #define DELAY_NO 0x00
207 #define DELAY_INFINITE 0xffffffff
208 
209  class ChangedAlarm : public Alarm {
210  DimmerStateMachine& sm;
211  public:
212  ChangedAlarm (DimmerStateMachine& s) : Alarm(0), sm(s) {}
213  virtual ~ChangedAlarm () {}
214  void set (uint32_t t,AlarmClock& clock) {
215  clock.cancel(*this);
216  Alarm::set(t);
217  clock.add(*this);
218  }
219  virtual void trigger (__attribute__((unused)) AlarmClock& clock) {
220  sm.change = true;
221  }
222  };
223 
224 
225  class RampAlarm : public Alarm {
226  public:
227  DimmerStateMachine& sm;
228  DimmerPeerList lst;
229  uint32_t delay, tack;
230  uint8_t destlevel;
231  uint8_t dx;
232 
233  RampAlarm (DimmerStateMachine& m) : Alarm(0), sm(m), lst(0), delay(0), tack(0), destlevel(0), dx(5) {}
234  void list (DimmerPeerList l) { lst=l; delay=tack=0; destlevel=sm.status(); }
235  void init (uint8_t state,DimmerPeerList l) {
236  uint8_t destlevel = state == AS_CM_JT_RAMPOFF ? 0 : 200;
237  if( l.valid() == true ) {
238  destlevel = state == AS_CM_JT_RAMPOFF ? l.offLevel() : l.onLevel();
239  }
240  init(sm.getDelayForState(state,l),destlevel,l.valid() ? 0 : DELAY_INFINITE,l);
241  }
242  void init (uint32_t ramptime,uint8_t level,uint32_t dly,DimmerPeerList l=DimmerPeerList(0)) {
243  DPRINT(F("Ramp/Level: "));DDEC(ramptime);DPRINT(F("/"));DDECLN(level);
244  // check that we start with the defined minimum
245  if( lst.valid() && sm.status() !=0 && sm.status() < lst.onMinLevel() ) {
246  sm.updateLevel(lst.onMinLevel());
247  }
248  lst=l;
249  destlevel = level==201 ? sm.lastonlevel : level;
250  delay = dly;
251  sm.updateState(destlevel==0 ? AS_CM_JT_RAMPOFF : AS_CM_JT_RAMPON, delay);
252  uint8_t curlevel = sm.status();
253  uint32_t diff;
254  if( curlevel > destlevel ) { // dim down
255  diff = curlevel - destlevel;
256  }
257  else { // dim up
258  diff = destlevel - curlevel;
259  }
260  if (diff == 0) {
261  dx = 0;
262  tack = 1;
263  }
264  else if( ramptime > diff ) {
265  dx = 1;
266  tack = ramptime / diff;
267  }
268  else {
269  tack = 1;
270  dx = uint8_t(diff / (ramptime > 0 ? ramptime : 1));
271  }
272  set(tack);
273  //DPRINT("Level/Dx/Tack: ");DDEC(curlevel);DPRINT("/");DDEC(dx);DPRINT("/");DDECLN(tack);
274  }
275  virtual void trigger (AlarmClock& clock) {
276  uint8_t curlevel = sm.status();
277  // DHEX(curlevel);DPRINT(" ");DHEXLN(destlevel);
278  if( sm.status() != destlevel ) {
279  if( curlevel > destlevel ) { // dim down
280  uint8_t rest = curlevel - destlevel;
281  sm.updateLevel( curlevel - (rest < dx ? rest : dx));
282  }
283  else { // dim up
284  uint8_t rest = destlevel - curlevel;
285  sm.updateLevel( curlevel + (rest < dx ? rest : dx));
286  }
287  }
288  // we catch our destination level -> go to next state
289  if( sm.status() == destlevel ) {
290  uint8_t next = sm.getNextState();
291  if( delay == 0 && lst.valid() == true ) {
292  delay = sm.getDelayForState(next,lst);
293  }
294  sm.setState(next,delay,lst);
295  }
296  else { // enable again for next ramp step
297  set(tack);
298  clock.add(*this);
299  }
300  }
301  };
302 
303  class BlinkAlarm : public Alarm {
304  public:
305  DimmerStateMachine& sm;
306  uint8_t origlevel;
307  uint8_t tack;
308  uint8_t diff;
309 
310  BlinkAlarm(DimmerStateMachine& m) : Alarm(0), sm(m), tack(millis2ticks(500)), diff(0) {}
311  void init(DimmerPeerList l) {
312  if (!l.offDelayBlink()) return;
313  origlevel = sm.status();
314  //if (origlevel < diff + 20) return;
315  diff = origlevel / 4;
316  set(tack);
317  sysclock.add(*this);
318  //DPRINT("blink: "); DPRINT(l.offDelayBlink()); DPRINT(", level: "); DDEC(origlevel); DPRINT(" - "); DDECLN(millis());
319  }
320  void end() {
321  sysclock.cancel(*this);
322  sm.updateLevel(origlevel);
323  }
324  virtual void trigger(AlarmClock& clock) {
325  uint8_t destlevel = (sm.status() == origlevel) ? origlevel - diff : origlevel;
326  sm.updateLevel(destlevel);
327  set(tack);
328  clock.add(*this);
329  }
330  };
331 
332  void updateLevel (uint8_t newlevel) {
333  // DPRINT("UpdLevel: ");DDECLN(newlevel);
334  level = newlevel;
335  }
336 
337  void updateState (uint8_t next,uint32_t delay) {
338  if( state != next ) {
339  switchState(state, next,delay);
340  state = next;
341  if ( state == AS_CM_JT_ON || state == AS_CM_JT_OFF ) {
342  triggerChanged();
343  }
344  }
345  }
346 
347  void triggerChanged () {
348  calarm.set(decis2ticks(list1.statusInfoMinDly()*5),sysclock);
349  }
350 
351  void setState (uint8_t next,uint32_t delay,const DimmerPeerList& lst=DimmerPeerList(0),uint8_t deep=0) {
352  // check deep to prevent infinite recursion
353  if( next != AS_CM_JT_NONE && deep < 4) {
354  // first cancel possible running alarm
355  sysclock.cancel(alarm);
356  // if state is different
357  if (state != next) {
358  if (next == AS_CM_JT_OFFDELAY) blink.init(lst);
359  if (state == AS_CM_JT_OFFDELAY) blink.end();
360  updateState(next,delay);
361  }
362  if ( state == AS_CM_JT_RAMPON || state == AS_CM_JT_RAMPOFF ) {
363  alarm.init(state,lst);
364  sysclock.add(alarm);
365  }
366  else {
367  if (delay == DELAY_NO) {
368  // go immediately to the next state
369  next = getNextState();
370  delay = getDelayForState(next,lst);
371  setState(next, delay, lst, ++deep);
372  }
373  else if (delay != DELAY_INFINITE) {
374  alarm.list(lst);
375  alarm.set(delay);
376  sysclock.add(alarm);
377  }
378  }
379  }
380  }
381 
382 protected:
383  uint8_t state : 4;
384  bool change : 1;
385  bool toggledimup : 1;
386  bool erroverheat : 1;
387  bool erroroverload : 1;
388  bool errreduced : 1;
389  uint8_t level, lastonlevel;
390  RampAlarm alarm;
391  BlinkAlarm blink;
392  ChangedAlarm calarm;
393  DimmerList1 list1;
394 
395 public:
396  DimmerStateMachine() : state(AS_CM_JT_NONE), change(false), toggledimup(true), erroverheat(false), erroroverload(false), errreduced(false),
397  level(0), lastonlevel(200), alarm(*this), blink(*this), calarm(*this), list1(0) {}
398  virtual ~DimmerStateMachine () {}
399 
400  bool changed () const { return change; }
401  void changed (bool c) { change=c; }
402 
403  void overheat(bool value) {
404  erroverheat = value;
405  }
406 
407  void overload(bool value){
408  erroroverload = value;
409  }
410  bool getoverload(){
411  return erroroverload;
412  }
413  void reduced (bool value) {
414  errreduced = value;
415  }
416 
417  void setup(DimmerList1 l1) {
418  list1 = l1;
419  }
420 
421  virtual void switchState(__attribute__ ((unused)) uint8_t oldstate,uint8_t newstate,__attribute__ ((unused)) uint32_t stateDelay) {
422  // DPRINT("Dimmer State: ");DHEX(oldstate);DPRINT(" -> ");DHEX(newstate);DPRINT(" Level: ");DHEXLN(level);
423  if( newstate == AS_CM_JT_ON ) {
424  lastonlevel = level;
425  }
426  }
427 
428  void jumpToTarget(const DimmerPeerList& lst) {
429  uint8_t next = getJumpTarget(state,lst);
430  // DPRINT("Jmp: ");DHEX(state);DPRINT(" - ");DHEXLN(next);
431  if( next != AS_CM_JT_NONE ) {
432  // get delay
433  uint32_t dly = getDelayForState(next,lst);
434  // on/off time mode / absolute / minimal
435  if( next == state && (next == AS_CM_JT_ON || next == AS_CM_JT_OFF) && dly < DELAY_INFINITE) {
436  bool minimal = next == AS_CM_JT_ON ? lst.onTimeMode() : lst.offTimeMode();
437  // if minimal is set - we jump out if the new delay is shorter
438  if( minimal == true ) {
439  // DPRINT("Minimal");DDECLN(dly);
440  uint32_t curdly = sysclock.get(alarm); // 0 means DELAY_INFINITE
441  if( curdly == 0 || curdly > dly ) {
442  // DPRINTLN(F("Skip short Delay"));
443  return;
444  }
445  }
446  }
447  // switch to next
448  setState(next,dly,lst);
449  }
450  }
451 
452  void toggleState () {
453  if( state == AS_CM_JT_OFF ) {
454  setLevel(lastonlevel,5,0xffff);
455  }
456  else {
457  setLevel(0,5,0xffff);
458  }
459  }
460 
461  uint8_t getNextState () {
462  switch( state ) {
463  case AS_CM_JT_ONDELAY: return AS_CM_JT_RAMPON;
464  case AS_CM_JT_RAMPON: return AS_CM_JT_ON;
465  case AS_CM_JT_ON: return AS_CM_JT_OFFDELAY;
466  case AS_CM_JT_OFFDELAY: return AS_CM_JT_RAMPOFF;
467  case AS_CM_JT_RAMPOFF: return AS_CM_JT_OFF;
468  case AS_CM_JT_OFF: return AS_CM_JT_ONDELAY;
469  }
470  return AS_CM_JT_NONE;
471  }
472 
473  uint8_t getJumpTarget(uint8_t stat,const DimmerPeerList& lst) const {
474  switch( stat ) {
475  case AS_CM_JT_ONDELAY: return lst.jtDlyOn();
476  case AS_CM_JT_RAMPON: return lst.jtRampOn();
477  case AS_CM_JT_ON: return lst.jtOn();
478  case AS_CM_JT_OFFDELAY: return lst.jtDlyOff();
479  case AS_CM_JT_RAMPOFF: return lst.jtRampOff();
480  case AS_CM_JT_OFF: return lst.jtOff();
481  }
482  return AS_CM_JT_NONE;
483  }
484 
485  uint8_t getConditionForState(uint8_t stat,const DimmerPeerList& lst) const {
486  switch( stat ) {
487  case AS_CM_JT_ONDELAY: return lst.ctDlyOn();
488  case AS_CM_JT_RAMPON: return lst.ctRampOn();
489  case AS_CM_JT_ON: return lst.ctOn();
490  case AS_CM_JT_OFFDELAY: return lst.ctDlyOff();
491  case AS_CM_JT_RAMPOFF: return lst.ctRampOff();
492  case AS_CM_JT_OFF: return lst.ctOff();
493  }
494  return AS_CM_CT_X_GE_COND_VALUE_LO;
495  }
496 
497  uint32_t getDelayForState(uint8_t stat,const DimmerPeerList& lst) const {
498  if( lst.valid() == false ) {
499  return getDefaultDelay(stat);
500  }
501  uint8_t value = 0;
502  switch( stat ) {
503  case AS_CM_JT_ONDELAY: value = lst.onDly(); break;
504  case AS_CM_JT_RAMPON: value = lst.rampOnTime(); break;
505  case AS_CM_JT_ON: value = lst.onTime(); break;
506  case AS_CM_JT_OFFDELAY: value = lst.offDly(); break;
507  case AS_CM_JT_RAMPOFF: value = lst.rampOffTime(); break;
508  case AS_CM_JT_OFF: value = lst.offTime(); break;
509  }
510  return AskSinBase::byteTimeCvt(value);
511  }
512 
513  uint32_t getDefaultDelay(uint8_t stat) const {
514  switch( stat ) {
515  case AS_CM_JT_ON:
516  case AS_CM_JT_OFF:
517  return DELAY_INFINITE;
518  case AS_CM_JT_RAMPON:
519  case AS_CM_JT_RAMPOFF:
520  return decis2ticks(5);
521  }
522  return DELAY_NO;
523  }
524 
525  bool delayActive () const { return sysclock.get(alarm) > 0; }
526 
527  void dimUp (const DimmerPeerList& lst) {
528  uint8_t dx = lst.dimStep();
529  uint8_t newlevel = level+dx;
530  if( newlevel > lst.dimMaxLevel() ) {
531  newlevel = lst.dimMaxLevel();
532  }
533  updateState(AS_CM_JT_RAMPON,getDelayForState(AS_CM_JT_RAMPON, lst));
534  updateLevel(newlevel);
535  updateState(AS_CM_JT_ON,getDelayForState(AS_CM_JT_ON, lst));
536  }
537 
538  void dimDown (const DimmerPeerList& lst) {
539  uint8_t dx = lst.dimStep();
540  uint8_t newlevel = level - (dx < level ? dx : level);
541  if( newlevel < lst.dimMinLevel() ) {
542  newlevel = lst.dimMinLevel();
543  }
544  uint8_t newstate = newlevel > lst.onMinLevel() ? AS_CM_JT_RAMPON : AS_CM_JT_RAMPOFF;
545  updateState(newstate,getDelayForState(newstate, lst));
546  updateLevel(newlevel);
547  newstate = newlevel > lst.onMinLevel() ? AS_CM_JT_ON : AS_CM_JT_OFF;
548  updateState(newstate,getDelayForState(newstate, lst));
549  }
550 
551  bool set (uint8_t value,uint16_t ramp,uint16_t delay) {
552  setLevel(value,ramp,delay);
553  return true;
554  }
555 
556  void remote (const DimmerPeerList& lst,uint8_t counter) {
557  // perform action as defined in the list
558  switch (lst.actionType()) {
559  case AS_CM_ACTIONTYPE_JUMP_TO_TARGET:
560  jumpToTarget(lst);
561  break;
562  case AS_CM_ACTIONTYPE_TOGGLE_TO_COUNTER:
563  setState((counter & 0x01) == 0x01 ? AS_CM_JT_RAMPON : AS_CM_JT_RAMPOFF, DELAY_INFINITE, lst);
564  break;
565  case AS_CM_ACTIONTYPE_TOGGLE_INVERSE_TO_COUNTER:
566  setState((counter & 0x01) == 0x01 ? AS_CM_JT_RAMPON : AS_CM_JT_RAMPOFF, DELAY_INFINITE, lst);
567  break;
568  case AS_CM_ACTIONTYPE_UPDIM:
569  dimUp(lst);
570  break;
571  case AS_CM_ACTIONTYPE_DOWNDIM:
572  dimDown(lst);
573  break;
574  case AS_CM_ACTIONTYPE_TOGGLEDIM:
575  if( toggledimup == true ) dimUp(lst);
576  else dimDown(lst);
577  toggledimup = ! toggledimup;
578  break;
579  case AS_CM_ACTIONTYPE_TOGGLEDIM_TO_COUNTER:
580  (counter & 0x01) == 0x01 ? dimUp(lst) : dimDown(lst);
581  break;
582  case AS_CM_ACTIONTYPE_TOGGLEDIM_TO_COUNTER_INVERSE:
583  (counter & 0x01) == 0x00 ? dimUp(lst) : dimDown(lst);
584  break;
585  }
586 
587  }
588 
589  void sensor (const DimmerPeerList& lst,uint8_t counter,uint8_t value) {
590  uint8_t cond = getConditionForState(state,lst);
591  bool doit = false;
592  switch( cond ) {
593  case AS_CM_CT_X_GE_COND_VALUE_LO:
594  doit = (value >= lst.ctValLo());
595  break;
596  case AS_CM_CT_X_GE_COND_VALUE_HI:
597  doit = (value >= lst.ctValHi());
598  break;
599  case AS_CM_CT_X_LT_COND_VALUE_LO:
600  doit = (value < lst.ctValLo());
601  break;
602  case AS_CM_CT_X_LT_COND_VALUE_HI:
603  doit = (value < lst.ctValHi());
604  break;
605  case AS_CM_CT_COND_VALUE_LO_LE_X_LT_COND_VALUE_HI:
606  doit = ((lst.ctValLo() <= value) && (value < lst.ctValHi()));
607  break;
608  case AS_CM_CT_X_LT_COND_VALUE_LO_OR_X_GE_COND_VALUE_HI:
609  doit =((value < lst.ctValLo()) || (value >= lst.ctValHi()));
610  break;
611  }
612  if( doit == true ) {
613  remote(lst,counter);
614  }
615  else {
616  // TODO use else jump table
617  }
618  }
619 
620  void setLevel (uint8_t level, uint16_t ramp, uint16_t delay) {
621  // DPRINT("SetLevel: ");DHEX(level);DPRINT(" ");DHEX(ramp);DPRINT(" ");DHEXLN(delay);
622  sysclock.cancel(alarm);
623  if( ramp==0 ) {
624  alarm.destlevel=level;
625  updateLevel(level);
626  setState(level==0 ? AS_CM_JT_OFF : AS_CM_JT_ON, AskSinBase::intTimeCvt(delay));
627  }
628  else {
629  alarm.init(AskSinBase::intTimeCvt(ramp), level, AskSinBase::intTimeCvt(delay));
630  sysclock.add(alarm);
631  }
632  }
633 
634  void stop () {}
635 
636  uint8_t status () const {
637  return level;
638  }
639 
640  uint8_t flags () const {
641  uint8_t f = delayActive() ? 0x40 : 0x00;
642  if( erroverheat == true ) {
643  f |= AS_CM_EXTSTATE_OVERHEAT;
644  }
645  if( erroroverload == true) {
646  f |= AS_CM_EXTSTATE_OVERLOAD;
647  }
648  if( errreduced == true ) {
649  f |= AS_CM_EXTSTATE_REDUCED;
650  }
651  if( alarm.destlevel < level) {
652  f |= AS_CM_EXTSTATE_DOWN;
653  }
654  else if( alarm.destlevel > level) {
655  f |= AS_CM_EXTSTATE_UP;
656  }
657  return f;
658  }
659 };
660 
661 template <class HalType,int PeerCount,class List0Type=List0>
662 class DimmerChannel : public ActorChannel<HalType,DimmerList1,DimmerList3,PeerCount,List0Type,DimmerStateMachine> {
663  uint8_t* phys;
664 protected:
666 public:
667  DimmerChannel () : BaseChannel(), phys(0) {}
668 
669  void setPhysical(uint8_t& p) {
670  phys = &p;
671  }
672 
673  void patchStatus (Message& msg) {
674  if( msg.length() == 0x0e ) {
675  msg.length(0x0f);
676  if( phys != 0 ) {
677  msg.data()[3] = *phys;
678  }
679  }
680  }
681 };
682 
683 template<class HalType,class ChannelType,int ChannelCount,int VirtualCount,class List0Type=List0>
684 class DimmerDevice : public MultiChannelDevice<HalType,ChannelType,ChannelCount,List0Type> {
685 public:
687 
688  DimmerDevice (const DeviceInfo& info,uint16_t addr) : DeviceType(info,addr) {}
689  virtual ~DimmerDevice () {}
690 
691  /* the following definitions are needed for the DimmerControler */
692  static int const channelCount = ChannelCount;
693  static int const virtualCount = VirtualCount;
694  typedef ChannelType DimmerChannelType;
695  DimmerChannelType& dimmerChannel(uint8_t ch) {
696  return this->channel(ch);
697  }
698 };
699 
700 
701 template<class HalType,class DimChannelType,class RmtChannelType,int DimChannelCount,int DimVirtualCount,int RmtChannelCount, class List0Type=List0>
702 class DimmerAndRemoteDevice : public ChannelDevice<HalType, VirtBaseChannel<HalType, List0Type>, DimChannelCount + RmtChannelCount, List0Type> {
703 
704 public:
707  public:
708  typedef ChannelDevice<HalType, VirtBaseChannel<HalType, List0Type>, DimChannelCount + RmtChannelCount, List0Type> DeviceType;
709  DimmerAndRemoteDevice (const DeviceInfo& info, uint16_t addr) : DeviceType(info, addr) {
710  for( uint8_t i=0; i<RmtChannelCount; ++i ) {
711  DeviceType::registerChannel(rmc[i], i+1);
712  }
713  for( uint8_t j=0; j<DimChannelCount; ++j ) {
714  DeviceType::registerChannel(dmc[j], j+RmtChannelCount+1);
715  }
716  }
717  virtual ~DimmerAndRemoteDevice () {}
718 
719  /* the following definitions are needed for the DimmerControler */
720  static int const channelCount = DimChannelCount;
721  static int const virtualCount = DimVirtualCount;
722  typedef DimChannelType DimmerChannelType;
723  DimmerChannelType& dimmerChannel(uint8_t ch) {
724  return this->dmc[ch-1];
725  }
726  typedef RmtChannelType RemoteChannelType;
727  RemoteChannelType& remoteChannel(uint8_t re){
728  return this->rmc[re-1];
729  }
730 };
731 
732 template<class HalType,class DimmerType,class PWM>
734 protected:
735  DimmerType& dimmer;
736  PWM pwms[DimmerType::channelCount/DimmerType::virtualCount];
737  uint8_t physical[DimmerType::channelCount/DimmerType::virtualCount];
738  uint8_t factor[DimmerType::channelCount/DimmerType::virtualCount];
739 private:
740  uint8_t counter;
741  uint8_t overloadcounter;
742 
743  class ChannelCombiner : public Alarm {
745  public:
746  ChannelCombiner (DimmerControl<HalType,DimmerType,PWM>& d) : Alarm(0), control(d) {}
747  virtual ~ChannelCombiner () {}
748  virtual void trigger (AlarmClock& clock) {
749  control.updatePhysical();
750  set(millis2ticks(10));
751  clock.add(*this);
752  }
753  } cb;
754 
755 public:
756  DimmerControl (DimmerType& dim) : dimmer(dim), counter(0), overloadcounter(0), cb(*this) {}
757  virtual ~DimmerControl () {}
758 
759  uint8_t channelCount () { return DimmerType::channelCount; }
760  uint8_t virtualCount () { return DimmerType::virtualCount; }
761  uint8_t physicalCount () { return DimmerType::channelCount/DimmerType::virtualCount; }
762 
763  void firstinit () {
764  for( uint8_t i=1; i<=channelCount(); ++i ) {
765  if( i <= physicalCount() ){
766  dimmer.dimmerChannel(i).getList1().logicCombination(LOGIC_OR);
767  }
768  else {
769  dimmer.dimmerChannel(i).getList1().logicCombination(LOGIC_INACTIVE);
770  }
771  }
772  }
773 
774  bool init (HalType& hal,...) {
775  bool first = dimmer.init(hal);
776  if( first == true ) {
777  firstinit();
778  }
779  va_list argp;
780  va_start(argp, hal);
781  for( uint8_t i=0; i<physicalCount(); ++i ) {
782  uint8_t p = va_arg(argp, int);
783  pwms[i].init(p);
784  physical[i] = 0;
785  factor[i] = 200; // 100%
786  }
787  va_end(argp);
788  initChannels();
789  cb.trigger(sysclock);
790  return first;
791  }
792 
793  bool init (HalType& hal,const uint8_t pins[]) {
794  bool first = dimmer.init(hal);
795  if( first == true ) {
796  firstinit();
797  }
798  for( uint8_t i=0; i<physicalCount(); ++i ) {
799  pwms[i].init(pins[i]);
800  physical[i] = 0;
801  factor[i] = 200; // 100%
802  }
803  initChannels();
804  cb.trigger(sysclock);
805  return first;
806  }
807 
808  PWM& pwm (uint8_t num) { return pwms[num]; }
809 
810  void initChannels () {
811  for( uint8_t i=1; i<=physicalCount(); ++i ) {
812  for( uint8_t j=i; j<=channelCount(); j+=physicalCount() ) {
813  dimmer.dimmerChannel(j).setPhysical(physical[i-1]);
814  bool powerup = dimmer.dimmerChannel(j).getList1().powerUpAction();
815  dimmer.dimmerChannel(j).setLevel(powerup == true ? 200 : 0,0,0xffff);
816  }
817  }
818  }
819 
820  virtual void updatePhysical () {
821  // DPRINT("Pin ");DHEX(pin);DPRINT(" Val ");DHEXLN(calcPwm());
822  for( uint8_t i=0; i<physicalCount(); ++i ) {
823  uint8_t value = (uint8_t)combineChannels(i+1);
824  value = (((uint16_t)factor[i] * (uint16_t)value) / 200);
825  if( physical[i] != value ) {
826  // DPRINT("Ch: ");DDEC(i+1);DPRINT(" Phy: ");DDECLN(value);
827  physical[i] = value;
828  pwms[i].set(physical[i]);
829  }
830  }
831  }
832 
833  uint16_t combineChannels (uint8_t start) {
834  if( virtualCount() == 1 ) {
835  return dimmer.dimmerChannel(start).status();
836  }
837  else {
838  uint16_t value = 0;
839  for( uint8_t i=start; i<=channelCount(); i+=physicalCount() ) {
840  uint8_t level = dimmer.dimmerChannel(i).status();
841  switch( dimmer.dimmerChannel(i).getList1().logicCombination() ) {
842  default:
843  case LOGIC_INACTIVE:
844  break;
845  case LOGIC_OR:
846  value = value > level ? value : level;
847  break;
848  case LOGIC_AND:
849  value = value < level ? value : level;
850  break;
851  case LOGIC_XOR:
852  value = value==0 ? level : (level==0 ? value : 0);
853  break;
854  case LOGIC_NOR:
855  value = 200 - (value > level ? value : level);
856  break;
857  case LOGIC_NAND:
858  value = 200 - (value < level ? value : level);
859  break;
860  case LOGIC_ORINVERS:
861  level = 200 - level;
862  value = value > level ? value : level;
863  break;
864  case LOGIC_ANDINVERS:
865  level = 200 - level;
866  value = value < level ? value : level;
867  break;
868  case LOGIC_PLUS:
869  value += level;
870  if( value > 200 ) value = 200;
871  break;
872  case LOGIC_MINUS:
873  if( level > value ) value = 0;
874  else value -= level;
875  break;
876  case LOGIC_MUL:
877  value = value * level / 200;
878  break;
879  case LOGIC_PLUSINVERS:
880  level = 200 - level;
881  value += level;
882  if( value > 200 ) value = 200;
883  break;
884  break;
885  case LOGIC_MINUSINVERS:
886  level = 200 - level;
887  if( level > value ) value = 0;
888  else value -= level;
889  break;
890  case LOGIC_MULINVERS:
891  level = 200 - level;
892  value = value * level / 200;
893  break;
894  case LOGIC_INVERSPLUS:
895  value += level;
896  if( value > 200 ) value = 200;
897  value = 200 - value;
898  break;
899  case LOGIC_INVERSMINUS:
900  if( level > value ) value = 0;
901  else value -= level;
902  value = 200 - value;
903  break;
904  case LOGIC_INVERSMUL:
905  value = value * level / 200;
906  value = 200 - value;
907  break;
908  }
909  }
910  // DHEXLN(value);
911  return value;
912  }
913  }
914 
915 
916  void setOverload (bool overload=false) {
917  counter++;
918  if ( overload ){
919  overloadcounter++;
920 
921  }
922  for( uint8_t i=1; i<=physicalCount(); ++i ) {
923  typename DimmerType::DimmerChannelType& c = dimmer.dimmerChannel(i);
924  if ( counter > 5 ){
925  if((counter - overloadcounter) <= 2 ){
926  factor[i-1] = 0;
927  c.overload(true);
928  }
929  else{
930  counter = 0;
931  overloadcounter = 0;
932  }
933 
934  }
935  else if ( c.getoverload()) {
936  c.overload(false);
937  factor[i-1] = 200;
938  }
939  }
940  }
941 
942  void setTemperature (uint16_t temp) {
943  uint8_t t = temp/10;
944  for( uint8_t i=1; i<=physicalCount(); ++i ) {
945  typename DimmerType::DimmerChannelType& c = dimmer.dimmerChannel(i);
946  if( c.getList1().overTempLevel() <= t ) {
947  factor[i-1] = 0; // overtemp -> switch off
948  c.overheat(true);
949  c.reduced(false);
950  }
951  else if( c.getList1().reduceTempLevel() <= t ) {
952  factor[i-1] = c.getList1().reduceLevel();
953  c.overheat(false);
954  c.reduced(true);
955  }
956  else {
957  factor[i-1] = 200; // 100%
958  c.overheat(false);
959  c.reduced(false);
960  }
961  }
962  }
963 };
964 
965 
966 template<class HalType,class DimmerType,class PWM>
967 class DualWhiteControl : public DimmerControl<HalType,DimmerType,PWM> {
968 public:
970  DualWhiteControl (DimmerType& dim) : BaseControl(dim) {
971 #ifndef NDEBUG
972  if( this->physicalCount() != 2 ) {
973  DPRINTLN(F("DualWhiteControl needs physical count == 2"));
974  }
975 #endif
976  }
977  virtual ~DualWhiteControl () {}
978 
979  virtual void updatePhysical () {
980  uint16_t bright = this->combineChannels(1);
981  uint16_t adjust = this->combineChannels(2);
982  // set the values
983  if( this->physical[0] != bright || this->physical[1] != adjust) {
984  this->physical[0] = bright;
985  this->physical[1] = adjust;
986  // adjust the color temp
987 // uint8_t pwm0 = (bright * (200-adjust)) / 200;
988 // uint8_t pwm1 = (bright * adjust) / 200;
989  uint8_t pwm0 = bright;
990  uint8_t pwm1 = bright;
991  if( adjust < 100 ) {
992  pwm1 = (bright * adjust) / 100;
993  }
994  else {
995  pwm0 = (bright * (200-adjust)) / 100;
996  }
997  this->pwms[0].set(pwm0);
998  this->pwms[1].set(pwm1);
999  }
1000  }
1001 };
1002 
1003 }
1004 
1005 #endif
as::Alarm
Definition: Alarm.h:15
as::DimmerChannel
Definition: Dimmer.h:662
as::VirtChannel
Definition: Channel.h:392
as::Message
Definition: Message.h:51
as::AlarmClock
Definition: AlarmClock.h:32
as::MultiChannelDevice
Definition: MultiChannelDevice.h:505
as::DeviceInfo
Definition: Device.h:70
as::ChannelDevice
Definition: MultiChannelDevice.h:28
as::DimmerControl
Definition: Dimmer.h:733
as::DualWhiteControl
Definition: Dimmer.h:967
as::DimmerList3
Definition: Dimmer.h:76
as::ShortLongList
Definition: Register.h:827
as::DimmerAndRemoteDevice
Definition: Dimmer.h:702
as::DimmerStateMachine
Definition: Dimmer.h:204
as::ActorChannel
Definition: Channel.h:263
as::DimmerDevice
Definition: Dimmer.h:684