AskSin++
Motion.h
1 //- -----------------------------------------------------------------------------------------------------------------------
2 // AskSin++
3 // 2017-05-10 papa Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
4 //- -----------------------------------------------------------------------------------------------------------------------
5 
6 #ifndef LIBRARIES_ASKSINPP_MOTION_H_
7 #define LIBRARIES_ASKSINPP_MOTION_H_
8 
9 #include "MultiChannelDevice.h"
10 #include "Register.h"
11 #include "Sensors.h"
12 
13 
14 namespace as {
15 
16 DEFREGISTER(MotionReg1,CREG_EVENTFILTER,CREG_INTERVAL,CREG_AES_ACTIVE,CREG_LEDONTIME)
17 static const uint8_t MotionReg1Defaults[] = {0x11,0x74,0x00,0x64};
18 class MotionList1 : public RegList1<MotionReg1> {
19 public:
20  MotionList1 (uint16_t addr) : RegList1<MotionReg1>(addr) {}
21  void defaults () {
22  init(MotionReg1Defaults,sizeof(MotionReg1Defaults));
23 // clear();
24 // eventFilterPeriod(1);
25 // eventFilterNumber(1);
26 // minInterval(4);
27 // captureWithinInterval(false);
28 // brightnessFilter(7);
29 // aesActive(false);
30 // ledOntime(100);
31  }
32 };
33 
34 class MotionEventMsg : public Message {
35 public:
36  void init(uint8_t msgcnt,uint8_t ch,uint8_t counter,uint8_t brightness,uint8_t next) {
37  Message::init(0xd,msgcnt,AS_MESSAGE_SENSOR_EVENT,Message::BIDI|Message::WKMEUP,ch & 0x3f,counter);
38  pload[0] = brightness;
39  pload[1] = (next+4) << 4;
40  }
41 };
42 
43 template <class HalType,int PeerCount,class List0Type=List0,class BrightnessSensor=Brightness>
44 class MotionChannel : public Channel<HalType,MotionList1,EmptyList,DefList4,PeerCount,List0Type>, public Alarm {
45 
46  class QuietMode : public Alarm {
47  public:
48  bool enabled;
49  bool motion;
50  MotionChannel& channel;
51  QuietMode (MotionChannel& c) : Alarm(0), enabled(false), motion(false), channel(c) {}
52  virtual ~QuietMode () {}
53  virtual void trigger (__attribute__ ((unused)) AlarmClock& clock) {
54  DPRINTLN(F("minInterval End"));
55  enabled = false;
56  if( motion == true ) {
57  motion = false;
58  channel.motionDetected();
59  }
60  }
61  };
62 
63  // send the brightness every 5 minutes to the master
64  #define LIGHTCYCLE seconds2ticks(5*60)
65  class Cycle : public Alarm {
66  public:
67  MotionChannel& channel;
68  Cycle (MotionChannel& c) : Alarm(LIGHTCYCLE), channel(c) {}
69  virtual ~Cycle () {}
70  virtual void trigger (AlarmClock& clock) {
71  tick = LIGHTCYCLE;
72  clock.add(*this);
73  channel.sendState();
74  }
75  };
76 
77  // return timer ticks
78  uint32_t getMinInterval () {
79  switch( this->getList1().minInterval() ) {
80  case 0: return seconds2ticks(15);
81  case 1: return seconds2ticks(30);
82  case 2: return seconds2ticks(60);
83  case 3: return seconds2ticks(120);
84  }
85  return seconds2ticks(240);
86  }
87 
88 private:
89  uint8_t counter;
90  QuietMode quiet;
91  Cycle cycle;
92  volatile bool isrenabled : 1;
93  BrightnessSensor brightsens;
94  uint32_t maxbright;
95 
96 public:
98 
99  MotionChannel () : ChannelType(), Alarm(0), counter(0), quiet(*this), cycle(*this), isrenabled(true),
100  brightsens(), maxbright(1) {}
101  virtual ~MotionChannel () {}
102 
103  BrightnessSensor& brightnessSensor () {
104  return brightsens;
105  }
106 
107  void setup(Device<HalType,List0Type>* dev,uint8_t number,uint16_t addr) {
108  ChannelType::setup(dev,number,addr);
109  sysclock.add(cycle);
110  pirInterruptOn();
111  brightsens.init();
112  }
113 
114  uint8_t status () {
115  brightsens.measure();
116  uint32_t bright = brightsens.brightness();
117  if( bright > maxbright ) {
118  maxbright = bright;
119  }
120  // scale to value between 0 - 255s
121  return (uint8_t)(bright * 255UL / maxbright);
122  }
123 
124  uint8_t flags () const {
125  return this->device().battery().low() ? 0x80 : 0x00;
126  }
127 
128  void sendState () {
129  pirInterruptOff();
130  typename ChannelType::DeviceType& d = this->device();
131  d.sendInfoActuatorStatus(d.getMasterID(),d.nextcount(),*this);
132  pirInterruptOn();
133  }
134 
135  void pirInterruptOn () {
136  isrenabled=true;
137  }
138 
139  void pirInterruptOff () {
140  isrenabled=false;
141  }
142 
143  // this runs synch to application
144  virtual void trigger (__attribute__ ((unused)) AlarmClock& clock) {
145  if( quiet.enabled == false ) {
146  // reset state timer because motion will be send now
147  sysclock.cancel(cycle);
148  cycle.set(LIGHTCYCLE);
149  sysclock.add(cycle);
150 
151  DPRINTLN(F("Motion"));
152  // start timer to end quiet interval
153  quiet.tick = getMinInterval();
154  quiet.enabled = true;
155  sysclock.add(quiet);
156  // blink led
157  if( this->device().led().active() == false ) {
158  this->device().led().ledOn( centis2ticks(this->getList1().ledOntime()) / 2);
159  }
160  MotionEventMsg& msg = (MotionEventMsg&)this->device().message();
161  msg.init(this->device().nextcount(),this->number(),++counter,status(),this->getList1().minInterval());
162 
163  pirInterruptOff();
164  this->device().sendPeerEvent(msg,*this);
165  pirInterruptOn();
166  }
167  else if ( this->getList1().captureWithinInterval() == true ) {
168  // we have had a motion during quiet interval
169  quiet.motion = true;
170  }
171  }
172 
173  // runs in interrupt
174  void motionDetected () {
175  if( isrenabled==true ) {
176  // cancel may not needed but anyway
177  sysclock.cancel(*this);
178  // activate motion message handler
179  sysclock.add(*this);
180  }
181  }
182 
183  // we are in quiet mode - means we have a motion detected
184  bool isMotion () const {
185  return quiet.enabled == true;
186  }
187 
188 };
189 
190 #define motionISR(device,chan,pin) class device##chan##ISRHandler { \
191  public: \
192  static void isr () { device.channel(chan).motionDetected(); } \
193 }; \
194 pinMode(pin,INPUT); \
195 if( digitalPinToInterrupt(pin) == NOT_AN_INTERRUPT ) \
196  enableInterrupt(pin,device##chan##ISRHandler::isr,RISING); \
197 else \
198  attachInterrupt(digitalPinToInterrupt(pin),device##chan##ISRHandler::isr,RISING);
199 
200 #define motionChannelISR(chan,pin) class __##pin##ISRHandler { \
201  public: \
202  static void isr () { chan.motionDetected(); } \
203 }; \
204 pinMode(pin,INPUT); \
205 if( digitalPinToInterrupt(pin) == NOT_AN_INTERRUPT ) \
206  enableInterrupt(pin,__##pin##ISRHandler::isr,RISING); \
207 else \
208  attachInterrupt(digitalPinToInterrupt(pin),__##pin##ISRHandler::isr,RISING);
209 
210 } // end namespace
211 
212 #endif /* LIBRARIES_ASKSINPP_MOTION_H_ */
as::Alarm
Definition: Alarm.h:15
as::Channel
Definition: Channel.h:21
as::Message
Definition: Message.h:51
as::AlarmClock
Definition: AlarmClock.h:32
as::Device
Definition: Device.h:80
as::MotionEventMsg
Definition: Motion.h:34
as::RegList1
Definition: Register.h:415
as::MotionChannel
Definition: Motion.h:44
as::MotionList1
Definition: Motion.h:18