-
Notifications
You must be signed in to change notification settings - Fork 4.9k
/
Copy pathxdrv_24_buzzer.ino
254 lines (223 loc) · 8.24 KB
/
xdrv_24_buzzer.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
/*
xdrv_24_Buzzer.ino - buzzer support for Tasmota
Copyright (C) 2021 Theo Arends
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef USE_BUZZER
/*********************************************************************************************\
* Buzzer support
\*********************************************************************************************/
#define XDRV_24 24
struct BUZZER {
uint32_t tune = 0;
uint32_t tune_reload = 0;
bool active = true;
bool enable = false;
uint8_t inverted = 0; // Buzzer inverted flag (1 = (0 = On, 1 = Off))
uint8_t count = 0; // Number of buzzes
uint8_t mode = 0; // Buzzer mode (0 = regular, 1 = infinite, 2 = follow LED)
uint8_t set[2];
uint8_t duration;
uint8_t state = 0;
uint8_t tune_size = 0;
uint8_t size = 0;
uint8_t sleep; // Current copy of TasmotaGlobal.sleep
} Buzzer;
/*********************************************************************************************/
void BuzzerSet(uint32_t state) {
if (Buzzer.inverted) {
state = !state;
}
if (Settings->flag4.buzzer_freq_mode) { // SetOption111 - Enable frequency output mode for buzzer
static uint8_t last_state = 0;
if (last_state != state) {
// Set 50% duty cycle for frequency output
// Set 0% (or 100% for inverted PWM) duty cycle which turns off frequency output either way
analogWrite(Pin(GPIO_BUZZER), (state) ? Settings->pwm_range / 2 : 0); // set duty cycle for frequency output
last_state = state;
}
} else {
DigitalWrite(GPIO_BUZZER, 0, state); // Buzzer On/Off
}
}
//void BuzzerBeep(uint32_t count = 1, uint32_t on = 1, uint32_t off = 1, uint32_t tune = 0, uint32_t mode = 0);
void BuzzerBeep(uint32_t count, uint32_t on, uint32_t off, uint32_t tune, uint32_t mode) {
Buzzer.set[0] = off; // Off duration in 100 mSec steps
Buzzer.set[1] = on; // On duration in 100 mSec steps
Buzzer.duration = 1; // Start buzzer on first step
Buzzer.size = 0;
Buzzer.tune_size = 0;
Buzzer.tune = 0;
Buzzer.tune_reload = 0;
Buzzer.mode = mode;
if (tune) {
uint32_t tune1 = tune;
uint32_t tune2 = tune;
for (uint32_t i = 0; i < 32; i++) {
if (!(tune2 & 0x80000000)) {
tune2 <<= 1; // Skip leading silence
} else {
Buzzer.tune_size++; // Allow trailing silence
Buzzer.tune_reload <<= 1; // Add swapped tune
Buzzer.tune_reload |= tune1 & 1;
tune1 >>= 1;
}
}
Buzzer.size = Buzzer.tune_size;
Buzzer.tune = Buzzer.tune_reload;
}
Buzzer.count = count * 2; // Start buzzer
AddLog(LOG_LEVEL_DEBUG, PSTR("BUZ: Count %d(%d), Time %d/%d, Tune 0x%08X(0x%08X), Size %d, Mode %d"),
count, Buzzer.count, on, off, tune, Buzzer.tune, Buzzer.tune_size, Settings->flag4.buzzer_freq_mode);
Buzzer.enable = (Buzzer.count > 0);
if (Buzzer.enable) {
Buzzer.sleep = TasmotaGlobal.sleep;
if (Settings->sleep > PWM_MAX_SLEEP) {
TasmotaGlobal.sleep = PWM_MAX_SLEEP; // Set a maxumum value of 10 milliseconds to ensure that buzzer periods are a bit more accurate
} else {
TasmotaGlobal.sleep = Settings->sleep; // Or keep the current sleep if it's lower than 10
}
} else {
TasmotaGlobal.sleep = Buzzer.sleep; // Restore original sleep
BuzzerSet(0);
}
}
void BuzzerSetStateToLed(uint32_t state) {
if (Buzzer.enable && (2 == Buzzer.mode)) {
Buzzer.state = (state != 0);
BuzzerSet(Buzzer.state);
}
}
void BuzzerBeep(uint32_t count) {
BuzzerBeep(count, 1, 1, 0, 0);
}
void BuzzerEnabledBeep(uint32_t count, uint32_t duration) {
if (Settings->flag3.buzzer_enable) { // SetOption67 - Enable buzzer when available
BuzzerBeep(count, duration, 1, 0, 0);
}
}
/*********************************************************************************************/
bool BuzzerPinState(void) {
if (XdrvMailbox.index == AGPIO(GPIO_BUZZER_INV)) {
Buzzer.inverted = 1;
XdrvMailbox.index -= (AGPIO(GPIO_BUZZER_INV) - AGPIO(GPIO_BUZZER));
return true;
}
return false;
}
void BuzzerInit(void) {
if (PinUsed(GPIO_BUZZER)) {
pinMode(Pin(GPIO_BUZZER), OUTPUT);
BuzzerSet(0);
} else {
Buzzer.active = false;
}
}
void BuzzerEvery100mSec(void) {
if (Buzzer.enable && (Buzzer.mode != 2)) {
if (Buzzer.count) {
if (Buzzer.duration) {
Buzzer.duration--;
if (!Buzzer.duration) {
if (Buzzer.size) {
Buzzer.size--;
Buzzer.state = Buzzer.tune & 1;
Buzzer.tune >>= 1;
} else {
Buzzer.size = Buzzer.tune_size;
Buzzer.tune = Buzzer.tune_reload;
Buzzer.count -= (Buzzer.tune_reload) ? 2 : 1;
Buzzer.state = Buzzer.count & 1;
if (Buzzer.mode) {
Buzzer.count |= 2;
}
}
Buzzer.duration = Buzzer.set[Buzzer.state];
}
}
BuzzerSet(Buzzer.state);
} else {
TasmotaGlobal.sleep = Buzzer.sleep; // Restore original sleep
Buzzer.enable = false;
}
}
}
/*********************************************************************************************\
* Commands
\*********************************************************************************************/
const char kBuzzerCommands[] PROGMEM = "Buzzer|" // Prefix
"Active|Pwm||" ;
SO_SYNONYMS(kBuzzerSynonyms,
67, 111
);
void (* const BuzzerCommand[])(void) PROGMEM = {
&CmndBuzzer };
void CmndBuzzer(void) {
// Buzzer <number of beeps>,<duration of beep in 100mS steps>,<duration of silence in 100mS steps>,<tune>
// All parameters are optional
//
// Buzzer = Buzzer 1,1,1 = Beep once with both duration and pause set to 100mS
// Buzzer 0 = Stop active beep cycle
// Buzzer 2 = Beep twice with duration 200mS and pause 100mS
// Buzzer 2,3 = Beep twice with duration 300mS and pause 100mS
// Buzzer 2,3,4 = Beep twice with duration 300mS and pause 400mS
// Buzzer 2,3,4,0x0F54 = Beep a sequence twice indicated by 0x0F54 = 1111 0101 0100 with duration 300mS and pause 400mS
// Notice skipped leading zeroes but valid trailing zeroes
// Buzzer -1 = Beep infinite
// Buzzer -2 = Beep following link led
if (XdrvMailbox.data_len > 0) {
if (XdrvMailbox.payload != 0) {
uint32_t parm[4] = { 0 };
ParseParameters(4, parm);
uint32_t mode = 0;
if (XdrvMailbox.payload < 0) {
parm[0] = 1; // Default Count
if (XdrvMailbox.payload > -3) {
mode = -XdrvMailbox.payload; // 0, 1 or 2
}
}
for (uint32_t i = 1; i < 3; i++) {
if (parm[i] < 1) { parm[i] = 1; } // Default On time, Off time
}
BuzzerBeep(parm[0], parm[1], parm[2], parm[3], mode);
} else {
BuzzerBeep(0);
}
} else {
BuzzerBeep(1);
}
ResponseCmndDone();
}
/*********************************************************************************************\
* Interface
\*********************************************************************************************/
bool Xdrv24(uint8_t function) {
bool result = false;
if (Buzzer.active) {
switch (function) {
case FUNC_EVERY_100_MSECOND:
BuzzerEvery100mSec();
break;
case FUNC_COMMAND:
result = DecodeCommand(kBuzzerCommands, BuzzerCommand, kBuzzerSynonyms);
break;
case FUNC_PRE_INIT:
BuzzerInit();
break;
case FUNC_PIN_STATE:
result = BuzzerPinState();
break;
}
}
return result;
}
#endif // USE_BUZZER