AskSin++
MultiChannelDevice.h
1 //- -----------------------------------------------------------------------------------------------------------------------
2 // AskSin++
3 // 2016-10-31 papa Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
4 //- -----------------------------------------------------------------------------------------------------------------------
5 
6 #ifndef __MULTICHANNELDEVICE_H__
7 #define __MULTICHANNELDEVICE_H__
8 
9 #include <Device.h>
10 #include <Defines.h>
11 #include <cm.h>
12 #include <Led.h>
13 #include <Activity.h>
14 
15 #if ARDUINO_ARCH_AVR
16 #include <avr/wdt.h>
17 #endif
18 
19 namespace as {
20 
21 extern void(* resetFunc) (void);
22 
23 #define REPLAY_NO 0
24 #define REPLAY_ACK 1
25 #define REPLAY_NACK 2
26 
27 template <class HalType,class ChannelType,int ChannelCount,class List0Type=List0>
28 class ChannelDevice : public Device<HalType,List0Type> {
29 
30  List0Type list0;
31  ChannelType* devchannels[ChannelCount];
32  uint8_t cfgChannel;
33  GenericList cfgList;
34 
35 public:
36 
38 
39  ChannelDevice (const DeviceInfo& i,uint16_t addr) : DeviceType(i,addr,list0,ChannelCount), list0(addr + this->keystore().size()), cfgChannel(0xff) {}
40 
41  virtual ~ChannelDevice () {}
42 
43  void registerChannel(ChannelType& ch,uint8_t num) {
44  if( num >= 1 && num <= ChannelCount) {
45  devchannels[num-1] = &ch;
46  }
47  }
48 
49  void layoutChannels () {
50  uint16_t addr = list0.address() + list0.size();
51  for( uint8_t i=0; i<this->channels(); ++i ) {
52  devchannels[i]->setup(this,i+1,addr);
53  addr += devchannels[i]->size();
54  }
55  }
56 
57  void channels (uint8_t num) {
58  DeviceType::channels(min(num,(uint8_t)ChannelCount));
59  }
60 
61  uint8_t channels () const {
62  return DeviceType::channels();
63  }
64 
65  void dumpSize () {
66  DPRINT(F("Address Space: "));DDEC(this->keystore().address());DPRINT(F(" - "));DDECLN(getUserStorage().getAddress());
67  }
68 
69  // get object to access free EEPROM after device data
70  UserStorage getUserStorage () {
71  ChannelType& ch = channel(this->channels());
72  return UserStorage(ch.address() + ch.size());
73  }
74 
75  uint16_t checksum () {
76  uint16_t crc = 0;
77 #ifndef NOCRC
78  // size of keystore data
79  crc = HalType::crc16(crc,DeviceType::keystore().size());
80  // add register of list0
81  for( uint8_t i=0; i<list0.size(); ++i ) {
82  crc = HalType::crc16(crc,list0.getRegister(i));
83  }
84  // add number of channels
85  for( uint8_t c=1; c<=this->channels(); ++c ) {
86  ChannelType& ch = channel(c);
87  // add register list 1
88  GenericList l = ch.getList1();
89  for( uint8_t i=0; i<l.getSize(); ++i ) {
90  crc = HalType::crc16(crc,l.getRegister(i));
91  }
92  // add register list 3
93  l = ch.getList3(0);
94  for( uint8_t i=0; i<l.getSize(); ++i ) {
95  crc = HalType::crc16(crc,l.getRegister(i));
96  }
97  // add register list 4
98  l = ch.getList4(0);
99  for( uint8_t i=0; i<l.getSize(); ++i ) {
100  crc = HalType::crc16(crc,l.getRegister(i));
101  }
102  // add number of peers
103  crc = HalType::crc16(crc,ch.peers());
104  }
105 #endif
106  return crc;
107  }
108 
109  List0Type& getList0 () {
110  return list0;
111  }
112 
113  bool init (HalType& hal) {
114  layoutChannels();
115  dumpSize();
116  // first initialize EEProm if needed
117  bool first = storage().setup(checksum());
118  if( first == true ) {
119  firstinit();
120  storage().store();
121  }
122  this->keystore().init();
123  this->setHal(hal);
124  HMID id;
125  this->getDeviceID(id);
126  hal.init(id);
127  hal.config(this->getConfigArea());
128  return first;
129  }
130 
131  void initDone () {
132  // trigger initial config changed - to allow scan/caching of list data
133  this->configChanged();
134  for( uint8_t cdx=1; cdx<=channels(); ++cdx ) {
135  channel(cdx).configChanged();
136  }
137  // trigger save of storage
138  storage().store();
139  }
140 
141  void firstinit () {
142  this->keystore().defaults(); // init aes key infrastructure
143  list0.defaults();
144  for( uint8_t i=0; i<this->channels(); ++i ) {
145  devchannels[i]->firstinit();
146  }
147  }
148 
149  void reset () {
150  if( getList0().localResetDisable() == false ) {
151  DPRINTLN(F("RESET"));
152  storage().reset();
153  storage().store();
154 #if ARDUINO_ARCH_AVR
155  resetFunc();
156 #elif ARDUINO_ARCH_STM32F1
157  nvic_sys_reset();
158 #endif
159  }
160  }
161 
162  void bootloader () {
163  DPRINTLN(F("BOOTLOADER"));
164 #if ARDUINO_ARCH_AVR
165  wdt_enable(WDTO_250MS);
166  while(1);
167 #endif
168  }
169 
170  void startPairing () {
171  this->sendDeviceInfo();
172  this->led().set(LedStates::pairing);
173  this->activity().stayAwake( seconds2ticks(20) ); // 20 seconds
174  }
175 
176  ChannelType& channel(uint8_t ch) {
177  return *devchannels[ch-1];
178  }
179 
180  bool pollRadio () {
181  bool worked = DeviceType::pollRadio();
182  for( uint8_t i=1; i<=this->channels(); ++i ) {
183  ChannelType& ch = channel(i);
184  if( ch.changed() == true ) {
185  this->sendInfoActuatorStatus(this->getMasterID(),this->nextcount(),ch);
186  worked = true;
187  }
188  }
189  return worked;
190  }
191 
192  bool aesActive () {
193  if( getList0().aesActive() == true ) {
194  return true;
195  }
196  for( uint8_t i=1; i<=this->channels(); ++i) {
197  if( channel(i).aesActive() == true ) {
198  return true;
199  }
200  }
201  return false;
202  }
203 
204  bool validSignature(Message& msg) {
205 #ifdef USE_AES
206  if( aesActive() == true ) {
207  return this->requestSignature(msg);
208  }
209 #endif
210  return true;
211  }
212 
213  bool validSignature(uint8_t ch,Message& msg) {
214 #ifdef USE_AES
215  if( (ch==0 && aesActive()) || (this->hasChannel(ch)==true && channel(ch).aesActive()==true) ) {
216  return this->requestSignature(msg);
217  }
218 #endif
219  return true;
220  }
221 
222  bool process(Message& msg) {
223  uint8_t answer = REPLAY_NO;
224  ChannelType* ch = 0;
225  HMID devid;
226  this->getDeviceID(devid);
227  if( msg.to() == devid || this->isBroadcastMsg(msg) ) {
228  // we got a message - we do not answer before 100ms
229  this->radio().setSendTimeout(); // use default value from radio
230  DPRINT(F("-> "));
231  msg.dump();
232  // ignore repeated messages
233  if( this->isRepeat(msg) == true ) {
234  return false;
235  }
236  // start processing the message
237  uint8_t mtype = msg.type();
238  uint8_t mcomm = msg.command();
239  uint8_t msubc = msg.subcommand();
240  if( mtype == AS_MESSAGE_CONFIG ) {
241  // PAIR_SERIAL
242  if( msubc == AS_CONFIG_PAIR_SERIAL && this->isDeviceSerial(msg.data())==true ) {
243  this->led().set(LedStates::pairing);
244  this->activity().stayAwake( seconds2ticks(20) ); // 20 seconds
245  this->sendDeviceInfo(msg.from(),msg.length());
246  }
247  // CONFIG_PEER_ADD
248  else if ( msubc == AS_CONFIG_PEER_ADD ) {
249  const ConfigPeerAddMsg& pm = msg.configPeerAdd();
250  bool success = false;
251  if( this->hasChannel(pm.channel()) == true ) {
252  if( validSignature(pm.channel(),msg) == true ) {
253  ch = &channel(pm.channel());
254  if( pm.peers() == 1 ) {
255  success = ch->peer(pm.peer1());
256  }
257  else {
258  success = ch->peer(pm.peer1(),pm.peer2());
259  }
260  }
261  }
262  if( success == true ) {
263  ch->configChanged();
264  storage().store();
265  answer = REPLAY_ACK;
266  }
267  else {
268  answer = REPLAY_NACK;
269  }
270  }
271  // CONFIG_PEER_REMOVE
272  else if ( msubc == AS_CONFIG_PEER_REMOVE ) {
273  const ConfigPeerRemoveMsg& pm = msg.configPeerRemove();
274  bool success = false;
275  if( this->hasChannel(pm.channel()) == true ) {
276  if( validSignature(pm.channel(),msg) == true ) {
277  ch = &channel(pm.channel());
278  success = ch->deletepeer(pm.peer1());
279  if( pm.peers() == 2 ) {
280  success &= ch->deletepeer(pm.peer2());
281  }
282  }
283  }
284  if( success == true ) {
285  ch->configChanged();
286  storage().store();
287  answer = REPLAY_ACK;
288  }
289  else {
290  answer = REPLAY_NACK;
291  }
292  }
293  // CONFIG_PEER_LIST_REQ
294  else if( msubc == AS_CONFIG_PEER_LIST_REQ ) {
295  const ConfigPeerListReqMsg& pm = msg.configPeerListReq();
296  if( this->hasChannel(pm.channel()) == true ) {
297  this->sendInfoPeerList(msg.from(),msg.count(),channel(pm.channel()));
298  }
299  }
300  // CONFIG_PARAM_REQ
301  else if (msubc == AS_CONFIG_PARAM_REQ ) {
302  const ConfigParamReqMsg& pm = msg.configParamReq();
303  GenericList gl = findList(pm.channel(),pm.peer(),pm.list());
304  if( gl.valid() == true ) {
305  this->sendInfoParamResponsePairs(msg.from(),msg.count(),gl);
306  }
307  else {
308  answer = REPLAY_NACK;
309  }
310  }
311  // CONFIG_STATUS_REQUEST
312  else if (msubc == AS_CONFIG_STATUS_REQUEST ) {
313  // this is an answer to a request - so we need no ack
314  this->sendInfoActuatorStatus(msg.from(),msg.count(),channel(msg.command()),false);
315  }
316  // CONFIG_START
317  else if( msubc == AS_CONFIG_START ) {
318  const ConfigStartMsg& pm = msg.configStart();
319  if( validSignature(pm.channel(),msg) == true ) {
320  cfgChannel = pm.channel();
321  cfgList = findList(cfgChannel,pm.peer(),pm.list());
322  // TODO setup alarm to disable after 2000ms
323  answer = REPLAY_ACK;
324  }
325  else {
326  answer = REPLAY_NACK;
327  }
328  }
329  // CONFIG_END
330  else if( msubc == AS_CONFIG_END ) {
331  if( cfgList.address() == list0.address() ) {
332  this->led().set(LedStates::nothing);
333  this->configChanged();
334  }
335  else {
336  // signal list update to channel
337  channel(cfgChannel).configChanged();
338  }
339  cfgChannel = 0xff;
340  storage().store();
341  // TODO cancel alarm
342  this->sendAck(msg,Message::WKMEUP);
343  }
344  else if( msubc == AS_CONFIG_WRITE_INDEX ) {
345  const ConfigWriteIndexMsg& pm = msg.configWriteIndex();
346  if( validSignature(pm.channel(),msg)==true ) {
347  if( cfgChannel == pm.channel() && cfgList.valid() == true ) {
348  this->writeList(cfgList,pm.data(),pm.datasize());
349  }
350  answer = REPLAY_ACK;
351  }
352  else {
353  answer = REPLAY_NACK;
354  }
355  }
356  else if( msubc == AS_CONFIG_SERIAL_REQ ) {
357  this->sendSerialInfo(msg.from(),msg.count());
358  }
359  else {
360  answer = REPLAY_NACK;
361  }
362  }
363  else if( mtype == AS_MESSAGE_ACTION ) {
364  if ( mcomm == AS_ACTION_RESET || mcomm == AS_ACTION_ENTER_BOOTLOADER ) {
365  if( validSignature(msg) == true ) {
366  this->sendAck(msg);
367  if( mcomm == AS_ACTION_ENTER_BOOTLOADER ) {
368  bootloader();
369  }
370  else {
371  reset();
372  }
373  }
374  }
375 #ifndef SENSOR_ONLY
376  else {
377  const ActionMsg& pm = msg.action();
378  if( this->hasChannel(pm.channel())==true ) {
379  ch = &channel(pm.channel());
380  if( validSignature(pm.channel(),msg)==true ) {
381  switch( mcomm ) {
382  case AS_ACTION_INHIBIT_OFF:
383  ch->inhibit(false);
384  answer = REPLAY_ACK;
385  break;
386  case AS_ACTION_INHIBIT_ON:
387  ch->inhibit(true);
388  answer = REPLAY_ACK;
389  break;
390  case AS_ACTION_STOP_CHANGE:
391  ch->stop();
392  answer = REPLAY_ACK;
393  break;
394  case AS_ACTION_SET:
395  if( ch->inhibit() == false ) {
396  answer = ch->process(msg.actionSet()) ? REPLAY_ACK : REPLAY_NACK;
397  }
398  break;
399  case AS_ACTION_COMMAND:
400  if( ch->inhibit() == false ) {
401  answer = ch->process(msg.actionCommand()) ? REPLAY_ACK : REPLAY_NACK;
402  }
403  break;
404  }
405  }
406  }
407  }
408 #endif
409  }
410  else if( mtype == AS_MESSAGE_HAVE_DATA ) {
411  DPRINTLN(F("HAVE DATA"));
412  answer = REPLAY_ACK;
413  }
414 #ifndef SENSOR_ONLY
415  else if (mtype == AS_MESSAGE_REMOTE_EVENT || mtype == AS_MESSAGE_SENSOR_EVENT) {
416  answer = REPLAY_NACK;
417  const RemoteEventMsg& pm = msg.remoteEvent();
418  uint8_t processed = 0;
419  for( uint8_t cdx=1; cdx<=this->channels(); ++cdx ) {
420  ChannelType* c = &channel(cdx);
421  if( c->inhibit() == false && c->has(pm.peer()) == true ) {
422  if( processed > 0 || validSignature(cdx,msg) == true ) {
423  ++processed;
424  ch = c;
425  switch( mtype ) {
426  case AS_MESSAGE_REMOTE_EVENT:
427  ch->process(pm);
428  break;
429  case AS_MESSAGE_SENSOR_EVENT:
430  ch->process(msg.sensorEvent());
431  break;
432  }
433  answer = REPLAY_ACK;
434  }
435  }
436  }
437  if( processed > 1 ) {
438  // we had more than one channel processed
439  // clear channel, so we only send an ACK
440  ch = 0;
441  }
442  }
443 #endif
444 #ifdef USE_AES
445  else if (mtype == AS_MESSAGE_KEY_EXCHANGE ) {
446  if( validSignature(msg) == true ) {
447  if( this->keystore().exchange(msg.aesExchange())==true ) answer = REPLAY_ACK;
448  else answer = REPLAY_NACK;
449  }
450  }
451 #endif
452  // default - send Nack if answer is requested
453  else {
454  answer = REPLAY_NACK;
455  }
456  }
457  else {
458  DPRINT(F("ignore "));
459  msg.dump();
460  return false;
461  }
462  // send ack/nack
463  if( msg.ackRequired() == true && msg.to() == devid ) {
464  if( answer == REPLAY_ACK ) {
465  if( ch != 0 ) this->sendAck(msg, *ch);
466  else this->sendAck(msg);
467  }
468  else if( answer == REPLAY_NACK ) {
469  this->sendNack(msg);
470  }
471  }
472  // we always stay awake after valid communication
473  this->activity().stayAwake(millis2ticks(500));
474  return true;
475  }
476 
477  GenericList findList(uint8_t ch,const Peer& peer,uint8_t numlist) {
478  if (numlist == 0) {
479  return list0;
480  } else if (this->hasChannel(ch) == true) {
481  ChannelType& c = channel(ch);
482  if (numlist == 1) {
483  return c.getList1();
484  } else if (numlist == 2) {
485  return c.getList2();
486  } else if (c.hasList3() && numlist == 3) {
487  return c.getList3(peer);
488  } else if (c.hasList4() && numlist == 4) {
489  return c.getList4(peer);
490  }
491  }
492  return GenericList();
493  }
494 
495  void sendPeerEvent (Message& msg,const ChannelType& ch) {
496  // we send only to peers if there is no config message pending
497  if( cfgChannel != 0xff ) {
498  DeviceType::sendPeerEvent(msg,ch);
499  }
500  }
501 };
502 
503 
504 template <class HalType,class ChannelType,int ChannelCount,class List0Type=List0>
505 class MultiChannelDevice : public ChannelDevice<HalType,ChannelType,ChannelCount,List0Type> {
506 
507  ChannelType cdata[ChannelCount];
508 
509 public:
510 
512 
514  for( uint8_t i=0; i<ChannelCount; ++i ) {
515  this->registerChannel(cdata[i], i+1);
516  }
517  }
518 
519  virtual ~MultiChannelDevice () {}
520 };
521 
522 }
523 
524 #endif
as::UserStorage
Definition: Storage.h:505
as::ConfigStartMsg
Definition: Message.h:448
as::ConfigWriteIndexMsg
Definition: Message.h:458
as::Message
Definition: Message.h:51
as::Peer
Definition: Peer.h:14
as::ActionMsg
Definition: Message.h:498
as::MultiChannelDevice
Definition: MultiChannelDevice.h:505
as::ConfigPeerListReqMsg
Definition: Message.h:435
as::ConfigParamReqMsg
Definition: Message.h:440
as::Device
Definition: Device.h:80
as::DeviceInfo
Definition: Device.h:70
as::ConfigPeerRemoveMsg
Definition: Message.h:430
as::ChannelDevice
Definition: MultiChannelDevice.h:28
as::RemoteEventMsg
Definition: Message.h:463
as::GenericList
Definition: ChannelList.h:72
as::ConfigPeerAddMsg
Definition: Message.h:416
as::HMID
Definition: HMID.h:14