-
Notifications
You must be signed in to change notification settings - Fork 12
/
Asksin_HM_LC_Sw1PBU_FM.ino
369 lines (326 loc) · 13.7 KB
/
Asksin_HM_LC_Sw1PBU_FM.ino
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
//- load library's --------------------------------------------------------------------------------------------------------
#include "AskSin.h"
//- serial communication --------------------------------------------------------------------------------------------------
const char helptext1[] PROGMEM = { // help text for serial console
"\n"
"Available commands:" "\n"
" p - start pairing with master" "\n"
" b[0] b[n] s - send a string, b[0] is length (50 bytes max)" "\n"
"\n"
" i[0]. i[1]. e - show eeprom content, i[0]. start address, i[1]. length" "\n"
" i[0]. b[1] f - write content to eeprom, i[0]. address, i[1] byte" "\n"
" c - clear eeprom complete, write 0 from start to end" "\n"
"\n"
" b[c] b[l] b - send button event, b[c] channel, b[l] short 0 or long 1" "\n"
" a - stay awake for TRX module (valid if power mode = 2)" "\n"
" t - gives an overview of the device configuration" "\n"
"\n"
" $nn for HEX input (e.g. $AB,$AC ); b[] = byte, i[]. = integer " "\n"
};
#if defined(USE_SERIAL)
const InputParser::Commands cmdTab[] PROGMEM = {
{ 'h', 0, showHelp },
{ 'p', 0, sendPairing },
{ 's', 1, sendCmdStr },
{ 'e', 0, showEEprom },
{ 'f', 2, writeEEprom },
{ 'c', 0, clearEEprom },
{ 't', 0, testConfig },
{ 'b', 1, buttonSend },
{ 'a', 0, stayAwake },
{ 'r', 0, resetDevice },
{ 0 }
};
InputParser parser (50, (InputParser::Commands*) cmdTab);
#endif
//- homematic communication -----------------------------------------------------------------------------------------------
const s_jumptable jumptable[] PROGMEM = { // jump table for HM communication
{ 0x01, 0x0E, HM_Status_Request },
{ 0x11, 0x02, HM_Set_Cmd },
{ 0x11, 0x04, HM_Reset_Cmd },
{ 0x3E, 0x00, HM_Switch_Event },
{ 0x40, 0x00, HM_Remote_Event },
{ 0xFF, 0xFF, HM_Config_Changed },
{ 0x0 }
};
HM hm ((s_jumptable*) jumptable, regMcPtr); // declare class for handling HM communication
BK bk[3]; // declare 1 inxtStatnces of the button key handler
RL rl[2]; // declare one inxtStatnce of relay class
//- current sensor
unsigned long lastCurrentInfoSentTime = 0;
unsigned long lastCurrentSenseTime = 0;
unsigned long currentImpulsStart = 0;
unsigned long lastSensorImpulsLength = 0;
unsigned long lastCurrentSenseImpulsLength = 0;
boolean lastCurrentSense = false;
boolean lastCurrentPin = false;
const uint8_t pinCurrent = 31;
const uint8_t pinRelay = 12;
const unsigned long minImpulsLength = 5000;
const uint8_t sendSensorIntervalSec = 150;
ISR(PCINT0_vect) {
currentImpuls();
}
boolean isInitialized = false;
//- main functions --------------------------------------------------------------------------------------------------------
void setup() {
#if defined(USE_SERIAL)
Serial.begin(57600); // starting serial messages
#else
Serial.end();
#endif
// some power savings
ADCSRA = 0; // disable ADC
power_all_disable(); // all devices off
power_timer0_enable(); // we need timer0 for delay function
//power_timer2_enable(); // we need timer2 for PWM
power_usart0_enable(); // it's the serial console
//power_twi_enable(); // i2c interface, not needed yet
power_spi_enable(); // enables SPI master
//power_adc_enable();
// init HM module
hm.init(); // initialize HM module
hm.ld.config(0); // configure the status led pin
hm.setPowerMode(0); // power mode for HM device
hm.setConfigEvent(); // reread config
// configure some buttons - config(tIdx, tPin, tTimeOutShortDbl, tLongKeyTime, tTimeOutLongDdbl, tCallBack)
bk[0].config(0,15,0,5000,15000,buttonState); // button 0 for channel 0 for send pairing string, and double press for reseting device config
bk[1].config(1,14,0,1000,5000,buttonState); // channel 1 to 2 as push button
bk[2].config(2,8,0,1000,5000,buttonState);
// init relay stuff
pinMode(pinRelay, OUTPUT);
rl[0].config(3,&relayState,&setInternalRelay,&hm,1,1);
// fake relay channel for current sensor
rl[1].config(4,&relayState,&setVirtualRelay,&hm,1,1);
#if defined(USE_SERIAL)
// show help screen and config
showHelp(); // shows help screen on serial console
showSettings(); // show device settings
Serial << F("version 025") << '\n'; // show device settings
#endif
// Enable interrupt on PA0
pinMode(pinCurrent, INPUT);
PCMSK0 |= (1<<PCINT0);
PCICR |= (1<<PCIE0);
isInitialized = true;
}
void loop() {
// poll functions for serial console, HM module, button key handler and relay handler
#if defined(USE_SERIAL)
parser.poll(); // handle serial input from console
#endif
hm.poll(); // HOMEMATIC task scheduler
bk->poll(); // key handler poll
rl->poll(); // relay handler poll
if (millis() - lastCurrentInfoSentTime > sendSensorIntervalSec * 1000) {
lastCurrentInfoSentTime = millis();
hm.sendSensorData(0, 0, lastSensorImpulsLength/(50*sendSensorIntervalSec), 0, 0); // send message
lastSensorImpulsLength = 0;
}
if (millis() - lastCurrentSenseTime > 500) {
cli();
lastCurrentSenseTime = millis();
// Calculate current sense boolean: 500ms*50Hz = 25 Impulses
boolean currentSense = lastCurrentSenseImpulsLength > (25 * minImpulsLength);
lastCurrentSenseImpulsLength = 0;
// Act on changes
if (currentSense != lastCurrentSense)
{
rl[1].setCurStat(currentSense?3:6);
// Serial << F("New Powersense: ") << currentSense << '\n';
// hm.sendInfoActuatorStatus(4,currentSense?0xC8:0x00,0);
lastCurrentSense = currentSense;
}
sei();
}
}
void currentImpuls()
{
cli();
boolean actualCurrentPin = digitalRead(pinCurrent);
if (lastCurrentPin == actualCurrentPin) {
sei();
return;
}
lastCurrentPin = actualCurrentPin;
if (actualCurrentPin) { // Impuls start
currentImpulsStart = micros();
} else { // Impuls end
unsigned long impulsLength = micros() - currentImpulsStart;
lastSensorImpulsLength += impulsLength;
lastCurrentSenseImpulsLength += impulsLength;
currentImpulsStart = 0;
}
sei();
return;
}
//- key handler functions -------------------------------------------------------------------------------------------------
void buttonState(uint8_t idx, uint8_t state) {
// possible events of this function:
// 0 - short key press
// 1 - double short key press
// 2 - long key press
// 3 - repeated long key press
// 4 - end of long key press
// 5 - double long key press
// 6 - time out for a double long
#if defined(RL_DBG)
Serial << "i:" << idx << ", s:" << state << '\n'; // some debug message
#endif
// channel device
if (idx == 0) {
if (state == 0) hm.ld.shortBlink();
if (state == 6) hm.startPairing(); // long key press, start pairing
if (state == 5) hm.reset(); // double long key press, reset the device
}
// channel 1 - 2
if ((idx >= 1) && (idx <= 2)) {
if ((state == 0) || (state == 1)) hm.sendPeerREMOTE(idx,0,0); // short key or double short key press detected
if ((state == 2) || (state == 3)) hm.sendPeerREMOTE(idx,1,0); // long or repeated long key press detected
if (state == 4) hm.sendPeerREMOTE(idx,2,0); // end of long or repeated long key press detected
}
}
//- relay handler functions -----------------------------------------------------------------------------------------------
void relayState(uint8_t cnl, uint8_t curStat, uint8_t nxtStat) {
#if defined(RL_DBG)
Serial << "c:" << cnl << " cS:" << curStat << " nS:" << nxtStat << '\n'; // some debug message
#endif
if (cnl == 3) { // cnl 3 => switch, cnl 4 => wechselschalter
if (curStat == 3)
{
hm.ld.set(1);
} else {
hm.ld.set(0);
}
}
}
void setInternalRelay(uint8_t cnl, uint8_t tValue) {
digitalWrite(pinRelay,tValue);
}
void setVirtualRelay(uint8_t cnl, uint8_t tValue) {
if (! isInitialized) return;
if (rl[0].getCurStat() == 3) {
rl[0].setNxtStat(6);
} else {
rl[0].setNxtStat(3);
}
}
//- HM functions ----------------------------------------------------------------------------------------------------------
void HM_Status_Request(uint8_t cnl, uint8_t *data, uint8_t len) {
// message from master to client while requesting the channel specific status
// client has to send an INFO_ACTUATOR_MESSAGE with the current status of the requested channel
// there is no payload; data[0] could be ignored
#if defined(RL_DBG)
Serial << F("\nxtStattus_Request; cnl: ") << pHex(cnl) << F(", data: ") << pHex(data,len) << "\n\n";
#endif
//if (cnl == 3) rl[0].sendStatus(); // send the current status
}
void HM_Set_Cmd(uint8_t cnl, uint8_t *data, uint8_t len) {
// message from master to client for setting a defined value to client channel
// client has to send an ACK with the current status; payload is typical 3 bytes
// data[0] = status message; data[1] = down,up,low battery; data[2] = rssi (signal quality)
#if defined(RL_DBG)
Serial << F("\nSet_Cmd; cnl: ") << pHex(cnl) << F(", data: ") << pHex(data,len) << "\n\n";
#endif
if (cnl == 3) rl[0].trigger11(data[0], data+1, (len>4)?data+3:NULL);
if (cnl == 4) rl[1].trigger11(data[0], data+1, (len>4)?data+3:NULL);
}
void HM_Reset_Cmd(uint8_t cnl, uint8_t *data, uint8_t len) {
#if defined(RL_DBG)
Serial << F("\nReset_Cmd; cnl: ") << pHex(cnl) << F(", data: ") << pHex(data,len) << "\n\n";
#endif
hm.send_ACK(); // send an ACK
if (cnl == 0) hm.reset(); // do a reset only if channel is 0
}
void HM_Switch_Event(uint8_t cnl, uint8_t *data, uint8_t len) {
// sample needed!
// ACK is requested but will send automatically
#if defined(RL_DBG)
Serial << F("\nSwitch_Event; cnl: ") << pHex(cnl) << F(", data: ") << pHex(data,len) << "\n\n";
#endif
}
void HM_Remote_Event(uint8_t cnl, uint8_t *data, uint8_t len) {
// message from a remote to the client device; this event pop's up if the remote is peered
// cnl = indicates client device channel
// data[0] the remote channel, but also the information for long key press - ((data[0] & 0x40)>>6) extracts the long key press
// data[1] = typically the key counter of the remote
#if defined(USE_SERIAL)
Serial << F("\nRemote_Event; cnl: ") << pHex(cnl) << F(", data: ") << pHex(data,len) << "\n\n";
#endif
if (cnl == 3) rl[0].trigger40(((data[0] & 0x40)>>6),data[1],(void*)®MC.ch3.l3);
if (cnl == 4) rl[1].trigger40(((data[0] & 0x40)>>6),data[1],(void*)®MC.ch4.l3);
}
void HM_Sensor_Event(uint8_t cnl, uint8_t *data, uint8_t len) {
// sample needed!
// ACK is requested but will send automatically
#if defined(USE_SERIAL)
Serial << F("\nSensor_Event; cnl: ") << pHex(cnl) << F(", data: ") << pHex(data,len) << "\n\n";
#endif
}
void HM_Config_Changed(uint8_t cnl, uint8_t *data, uint8_t len) {
#if defined(USE_SERIAL)
Serial << F("config changed\n");
#endif
}
#if defined(USE_SERIAL)
//- config functions ------------------------------------------------------------------------------------------------------
void sendCmdStr() { // reads a sndStr from console and put it in the send queue
memcpy(hm.send.data,parser.buffer,parser.count()); // take over the parsed byte data
Serial << F("s: ") << pHexL(hm.send.data, hm.send.data[0]+1) << '\n'; // some debug string
hm.send_out(); // fire to send routine
}
void sendPairing() { // send the first pairing request
hm.startPairing();
}
void showEEprom() {
uint16_t start, len;
uint8_t buf[32];
parser >> start >> len;
if (len == 0) len = E2END - start;
Serial << F("EEPROM listing, start: ") << start << F(", len: ") << len << '\n';
for (uint16_t i = start; i < len; i+=32) {
eeprom_read_block(buf,(void*)i,32);
Serial << pHex(i>>8) << pHex(i&0xFF) << F(" ") << pHex(buf,32) << '\n';
}
}
void writeEEprom() {
uint16_t addr;
uint8_t data;
for (uint8_t i = 0; i < parser.count(); i+=3) {
parser >> addr >> data;
eeprom_write_byte((uint8_t*)addr,data);
Serial << F("Write EEprom, Address: ") << pHex(addr>>8) << pHex(addr&0xFF) << F(", Data: ") << pHex(data) << '\n';
}
}
void clearEEprom() { // clear settings
Serial << F("Clear EEprom, size: ") << E2END+1 << F(" bytes") << '\n';
for (uint16_t i = 0; i <= E2END; i++) {
eeprom_write_byte((uint8_t*)i,0);
}
Serial << F("done") << '\n';
}
void showHelp() { // display help on serial console
showPGMText(helptext1);
}
void showSettings() { // shows device settings on serial console
hm.printSettings(); // print settings of own HM device
Serial << F("FreeMem: ") << freeMemory() << F(" byte's\n"); // displays the free memory
}
void testConfig() { // shows the complete configuration of slice table and peer database
hm.printConfig(); // prints register and peer config
}
void buttonSend() {
uint8_t cnl, lpr;
parser >> cnl >> lpr;
Serial << "button press, cnl: " << cnl << ", long press: " << lpr << '\n'; // some debug message
hm.sendPeerREMOTE(cnl,lpr,0); // parameter: button/channel, long press, battery
}
void stayAwake() {
hm.stayAwake(30000); // stay awake for 30 seconds
}
void resetDevice() {
Serial << F("reset device, clear eeprom...\n");
hm.reset();
Serial << F("reset done\n");
}
#endif