forked from pa-pa/AskSinPP
-
Notifications
You must be signed in to change notification settings - Fork 0
/
AskSinPP.h
370 lines (316 loc) · 10.4 KB
/
AskSinPP.h
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
//- -----------------------------------------------------------------------------------------------------------------------
// AskSin++
// 2016-10-31 papa Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
//- -----------------------------------------------------------------------------------------------------------------------
#ifndef __ASKSINPP_h__
#define __ASKSINPP_h__
#define ASKSIN_PLUS_PLUS_MAJOR 5
#define ASKSIN_PLUS_PLUS_MINOR 0
#define ASKSIN_PLUS_PLUS_SERVICE 3
#define ASKSIN_PLUS_PLUS_VERSION (ASKSIN_PLUS_PLUS_MAJOR * 10000 \
+ ASKSIN_PLUS_PLUS_MINOR * 100 + ASKSIN_PLUS_PLUS_SERVICE)
#define STRINGIZE2(s) #s
#ifndef STRINGIZE
#define STRINGIZE(s) STRINGIZE2(s)
#endif
#define ASKSIN_PLUS_PLUS_VERSION_STR STRINGIZE(ASKSIN_PLUS_PLUS_MAJOR) \
"." STRINGIZE(ASKSIN_PLUS_PLUS_MINOR) "." STRINGIZE(ASKSIN_PLUS_PLUS_SERVICE)
#define ASKSIN_PLUS_PLUS_IDENTIFIER F("AskSin++ v" ASKSIN_PLUS_PLUS_VERSION_STR " (" __DATE__ " " __TIME__ ")")
#define CONFIG_FREQ1 0
#define CONFIG_FREQ2 1
#define CONFIG_BOOTSTATE 2 //location of current boot state for ResetOnBoot
#include "Debug.h"
#include <stdint.h>
#ifdef ARDUINO_ARCH_EFM32
#define NOT_AN_INTERRUPT 0
#define enableInterrupt(pin,handler,mode) attachInterrupt(pin,handler,mode)
typedef unsigned long WiringPinMode;
typedef uint8_t uint8;
#elif ARDUINO_ARCH_STM32F1
#define _delay_us(us) delayMicroseconds(us)
inline void _delay_ms(uint32_t ms) { do { delayMicroseconds(1000); } while( (--ms) > 0); }
#define NOT_AN_INTERRUPT -1
#define digitalPinToInterrupt(pin) (pin)
#define enableInterrupt(pin,handler,mode) attachInterrupt(pin,handler,mode)
#define disableInterrupt(pin) detachInterrupt(pin)
#define memcmp_P(src,dst,count) memcmp((src),(dst),(count))
#elif (defined ARDUINO_ARCH_STM32) || (defined ARDUINO_ARCH_ESP32) || (defined ARDUINO_ARCH_RP2040)
#define _delay_us(us) delayMicroseconds(us)
//inline void _delay_ms(uint32_t ms) { do { delayMicroseconds(1000); } while ((--ms) > 0); }
inline void _delay_ms(uint32_t ms) { delay(ms); }
typedef uint32_t WiringPinMode;
typedef uint8_t uint8;
#define enableInterrupt(pin,handler,mode) attachInterrupt(pin,handler,mode)
#define disableInterrupt(pin) detachInterrupt(pin)
#else
typedef uint8_t uint8;
typedef uint16_t uint16;
// if we have no EnableInterrupt Library - and also no PCINT - use polling
#ifndef EnableInterrupt_h
#define enableInterrupt(pin,handler,mode) pinpolling##pin().enable(pin,handler,mode)
#define disableInterrupt(pin) pinpolling##pin().disable()
#endif
#endif
#include <Storage.h>
#include <Pins.h>
#include "Debug.h"
#include <Activity.h>
#include <Led.h>
#include <Buzzer.h>
#include <AlarmClock.h>
#include <Message.h>
#include <Button.h>
#include <Radio.h>
#include <BatterySensor.h>
namespace as {
extern const char* __gb_chartable;
/**
* AskSinBase provides basic methods for general use.
*/
class AskSinBase {
Storage storage;
public:
/**
* Read data from program space (AVR).
* \param dest pointer to destiantion in RAM
* \param adr address of program space to read data from
* \param size of data to read
*/
static void pgm_read(uint8_t* dest,uint16_t adr,uint8_t size) {
for( int i=0; i<size; ++i, ++dest ) {
#ifdef ARDUINO_ARCH_RP2040
*dest = pgm_read_byte((const void*)(adr + i));
#else
*dest = pgm_read_byte(adr + i);
#endif
}
}
/**
* Convert numeric value to character for printing.
* \param c numeric value to convert
* \return character for output
*/
static uint8_t toChar (uint8_t c) {
return *(__gb_chartable+c);
}
/**
* Update CRC16 value
* \param crc current checksum value
* \param data to add
* \return new crc checksum value
*/
static uint16_t crc16 (uint16_t crc,uint8_t d) {
crc ^= d;
for( uint8_t i = 8; i != 0; --i ) {
crc = (crc >> 1) ^ ((crc & 1) ? 0xA001 : 0 );
}
return crc;
}
/**
* Calculate CRC24 value from data
* \param data pointer to data to calculate CRC checksum
* \param len number of bytes to use for calculation
* \return CRC24 checksum
*/
static uint32_t crc24(const uint8_t* data,int len) {
uint32_t crc = 0xB704CEL;
for( uint8_t i=0; i<len; ++i) {
crc = crc24(crc,data[i]);
}
return crc;
}
/**
* Update CRC24 value
* \param crc current checksum value
* \param data to add
* \return new crc checksum value
*/
static uint32_t crc24(uint32_t crc,uint8_t d) {
crc ^= ((uint32_t)d) << 16;
for( uint8_t i = 0; i < 8; i++) {
crc <<= 1;
if (crc & 0x1000000) {
crc ^= 0x1864CFBL;
}
}
return crc & 0xFFFFFFL;
}
// get timer count in ticks
static uint32_t byteTimeCvt(uint8_t tTime) {
if( tTime == 0xff ) return 0xffffffff;
const uint16_t c[8] = {1,10,50,100,600,3000,6000,36000};
return decis2ticks( (uint32_t)(tTime & 0x1F) * c[tTime >> 5] );
}
static uint32_t byteTimeCvtSeconds (uint8_t tTime) {
const uint8_t c[2] = {1,60};
return seconds2ticks( (uint32_t)(tTime & 0x7F) * c[tTime >> 7] );
}
// get timer count in ticks
static uint32_t intTimeCvt(uint16_t iTime) {
if (iTime == 0x00) return 0x00;
if (iTime == 0xffff) return 0xffffffff;
uint32_t tByte;
if ((iTime & 0x1F) != 0) {
tByte = 2;
for (uint8_t i = 1; i < (iTime & 0x1F); i++) tByte *= 2;
} else tByte = 1;
return decis2ticks( (uint32_t)tByte*(iTime>>5) );
}
/** Calculate time until next send slot
* \param id Homematic ID of the device
* \param msgcnt current message counter
* \return next send slot in sysclock ticks
*/
static uint32_t nextSendSlot (const HMID& id,uint8_t msgcnt) {
uint32_t value = ((uint32_t)id) << 8 | msgcnt;
value = (value * 1103515245 + 12345) >> 16;
value = (value & 0xFF) + 480;
value *= 250;
DDEC(value / 1000);DPRINT(".");DDECLN(value % 1000);
return value;
}
/**
* Read status of pin.
* \param pinnr the number of the pin to read
* \param enablenr pin to set to high for enabling the read
* \param ms milli seconds to wait between enablement and pin read
* \return status of read pin
*/
static uint8_t readPin(uint8_t pinnr,uint8_t enablenr=0,uint8_t ms=0) {
uint8_t value=0;
pinMode(pinnr,INPUT_PULLUP);
if( enablenr != 0 && enablenr != 0xff) {
digitalWrite(enablenr,HIGH);
if( ms != 0 ) {
_delay_ms(ms);
}
}
value = digitalRead(pinnr);
// pinMode(pinnr,OUTPUT);
// digitalWrite(pinnr,LOW);
// 01/2022 TM: Ruhezustand des Eingangs ist nicht mehr Output/Low sondern High-Z
// https://homematic-forum.de/forum/viewtopic.php?f=76&t=71854
pinMode(pinnr,INPUT);
if( enablenr != 0 && enablenr != 0xff) {
digitalWrite(enablenr,LOW);
}
return value;
}
};
template <class StatusLed,class Battery,class Radio,class Buzzer=NoBuzzer>
class AskSin : public AskSinBase {
public:
typedef StatusLed LedType;
typedef Battery BatteryType;
typedef Radio RadioType;
typedef Buzzer BuzzerType;
LedType led;
BatteryType battery;
Activity activity;
RadioType radio;
BuzzerType buzzer;
void init (const HMID& id) {
led.init();
buzzer.init();
bool ccinitOK = radio.init();
radio.enable();
// start the system timer
sysclock.init();
// signal start to user
led.set(ccinitOK ? LedStates::welcome : LedStates::failure);
// delay first send by 'random' time
radio.setSendTimeout((id.id2()*10)+10);
}
void initBattery(uint16_t interval,uint8_t low,uint8_t critical) {
battery.init(seconds2ticks(interval),sysclock);
battery.low(low);
battery.critical(critical);
}
void config (const StorageConfig& sc) {
if( sc.valid() == true ) {
uint8_t f1 = sc.getByte(CONFIG_FREQ1);
uint8_t f2 = sc.getByte(CONFIG_FREQ2);
// check if CONFIG_FREQ1 is in range 0x60...0x6A -> 868,3MHz -550kHz/+567kHz
if( f1 >= 0x60 && f1 <= 0x6A ) {
DPRINT(F("Config Freq: 0x21"));DHEX(f1);DHEXLN(f2);
radio.tuneFreq(0x21, f1, f2);
}
}
}
bool runready () {
return sysclock.runready();
}
// call by the device before the device sends a message
// can be used to update data by the device sketch
void prepareSend (__attribute__((unused)) Message& msg) {}
// call after send a message to a peer
void sendPeer () {}
// use radio timer to wait given millis
void waitTimeout(uint16_t millis) {
radio.waitTimeout(millis);
}
void setIdle () {
radio.setIdle();
battery.setIdle();
}
void unsetIdle () {
battery.unsetIdle();
}
void wakeup () {
radio.wakeup();
}
#if defined(ARDUINO_ARCH_AVR) && ! ( defined(ARDUINO_AVR_ATmega32) || defined(__AVR_ATmega644__) || defined(__AVR_ATmega128__))
template <bool ENABLETIMER2=false, bool ENABLEADC=false>
void idle () { activity.savePower< Idle<ENABLETIMER2,ENABLEADC> >(*this); }
template <bool ENABLETIMER2=false>
void sleep () { activity.savePower< Sleep<ENABLETIMER2> >(*this); }
void sleepForever () { activity.sleepForever(*this); }
#endif
#if defined(ARDUINO_ARCH_RP2040)
void sleep () { activity.savePower<Sleep>(*this); }
#endif
};
#ifndef NORTC
template <class StatusLed,class Battery,class Radio,class Buzzer=NoBuzzer>
class AskSinRTC : public AskSin<StatusLed,Battery,Radio,Buzzer> {
public:
void init (const HMID& id) {
AskSin<StatusLed,Battery,Radio,Buzzer>::init(id);
// init real time clock - 1 tick per second
rtc.init();
}
void initBattery(uint16_t interval,uint8_t low,uint8_t critical) {
this->battery.init(interval,rtc);
this->battery.low(low);
this->battery.critical(critical);
}
bool runready () {
return rtc.runready() || AskSin<StatusLed,Battery,Radio,Buzzer>::runready();
}
#if defined(ARDUINO_ARCH_AVR) && ! ( defined(ARDUINO_AVR_ATmega32) || defined(__AVR_ATmega644__) || defined(__AVR_ATmega128__))
template <bool ENABLETIMER2=false>
void sleep () { this->activity.template savePower< SleepRTC >(*this); }
#endif
};
#endif
template <class HAL,int TIMEOUT=60>
class RadioWatchdog : public Alarm {
public:
RadioWatchdog () : Alarm(0,false) {}
virtual ~RadioWatchdog () {}
virtual void trigger (AlarmClock& clock) {
typename HAL::RadioType& radio = HAL::RadioType::instance();
bool alive = radio.clearAlive();
if( alive == false ) {
radio.init();
radio.enable();
}
set(seconds2ticks(TIMEOUT));
clock.add(*this);
}
void start (AlarmClock& clock) {
trigger(clock);
}
};
}
#endif