2019-08-12 16:18:08 +01:00
|
|
|
/*
|
2019-10-27 10:13:24 +00:00
|
|
|
xdrv_24_Buzzer.ino - buzzer support for Tasmota
|
2019-08-12 16:18:08 +01:00
|
|
|
|
2021-01-01 12:44:04 +00:00
|
|
|
Copyright (C) 2021 Theo Arends
|
2019-08-12 16:18:08 +01:00
|
|
|
|
|
|
|
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
|
|
|
|
\*********************************************************************************************/
|
2019-08-13 15:10:47 +01:00
|
|
|
|
2019-08-12 16:18:08 +01:00
|
|
|
#define XDRV_24 24
|
|
|
|
|
|
|
|
struct BUZZER {
|
|
|
|
uint32_t tune = 0;
|
2020-01-28 10:22:36 +00:00
|
|
|
uint32_t tune_reload = 0;
|
2019-08-12 16:18:08 +01:00
|
|
|
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
|
2020-02-06 15:25:37 +00:00
|
|
|
uint8_t mode = 0; // Buzzer mode (0 = regular, 1 = infinite, 2 = follow LED)
|
2019-08-12 16:18:08 +01:00
|
|
|
uint8_t set[2];
|
|
|
|
uint8_t duration;
|
|
|
|
uint8_t state = 0;
|
2021-01-27 15:09:56 +00:00
|
|
|
uint8_t tune_size = 0;
|
|
|
|
uint8_t size = 0;
|
2021-05-04 11:31:35 +01:00
|
|
|
uint8_t sleep; // Current copy of TasmotaGlobal.sleep
|
2019-08-13 15:10:47 +01:00
|
|
|
} Buzzer;
|
2019-08-12 16:18:08 +01:00
|
|
|
|
|
|
|
/*********************************************************************************************/
|
|
|
|
|
2021-01-27 15:09:56 +00:00
|
|
|
void BuzzerSet(uint32_t state) {
|
2020-09-09 19:00:55 +01:00
|
|
|
if (Buzzer.inverted) {
|
|
|
|
state = !state;
|
|
|
|
}
|
|
|
|
|
2021-06-11 17:14:12 +01:00
|
|
|
if (Settings->flag4.buzzer_freq_mode) { // SetOption111 - Enable frequency output mode for buzzer
|
2020-09-09 19:00:55 +01:00
|
|
|
static uint8_t last_state = 0;
|
|
|
|
if (last_state != state) {
|
2022-01-27 20:30:05 +00:00
|
|
|
// Set 50% duty cycle for frequency output
|
|
|
|
// Set 0% (or 100% for inverted PWM) duty cycle which turns off frequency output either way
|
2023-12-19 14:38:12 +00:00
|
|
|
#ifdef ESP8266
|
2022-01-27 20:30:05 +00:00
|
|
|
analogWrite(Pin(GPIO_BUZZER), (state) ? Settings->pwm_range / 2 : 0); // set duty cycle for frequency output
|
2023-12-19 14:38:12 +00:00
|
|
|
#else
|
|
|
|
int32_t pin = Pin(GPIO_BUZZER);
|
|
|
|
if (analogAttach(pin, Buzzer.inverted) >= 0) {
|
|
|
|
analogWritePhase(pin, (state) ? Settings->pwm_range / 2 : 0, 0);
|
|
|
|
}
|
|
|
|
#endif
|
2020-09-09 19:00:55 +01:00
|
|
|
last_state = state;
|
|
|
|
}
|
2021-01-27 15:09:56 +00:00
|
|
|
} else {
|
|
|
|
DigitalWrite(GPIO_BUZZER, 0, state); // Buzzer On/Off
|
2020-09-09 19:00:55 +01:00
|
|
|
}
|
2019-10-12 10:06:09 +01:00
|
|
|
}
|
|
|
|
|
2020-01-28 10:22:36 +00:00
|
|
|
//void BuzzerBeep(uint32_t count = 1, uint32_t on = 1, uint32_t off = 1, uint32_t tune = 0, uint32_t mode = 0);
|
2021-01-27 15:09:56 +00:00
|
|
|
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
|
2021-01-27 15:31:15 +00:00
|
|
|
Buzzer.size = 0;
|
2021-01-27 15:09:56 +00:00
|
|
|
Buzzer.tune_size = 0;
|
2021-01-27 15:31:15 +00:00
|
|
|
Buzzer.tune = 0;
|
2020-01-28 10:22:36 +00:00
|
|
|
Buzzer.tune_reload = 0;
|
|
|
|
Buzzer.mode = mode;
|
|
|
|
|
2019-08-13 10:40:34 +01:00
|
|
|
if (tune) {
|
|
|
|
uint32_t tune1 = tune;
|
|
|
|
uint32_t tune2 = tune;
|
|
|
|
for (uint32_t i = 0; i < 32; i++) {
|
|
|
|
if (!(tune2 & 0x80000000)) {
|
2021-01-27 15:09:56 +00:00
|
|
|
tune2 <<= 1; // Skip leading silence
|
2019-08-13 10:40:34 +01:00
|
|
|
} else {
|
2021-01-27 15:09:56 +00:00
|
|
|
Buzzer.tune_size++; // Allow trailing silence
|
|
|
|
Buzzer.tune_reload <<= 1; // Add swapped tune
|
2020-01-28 10:22:36 +00:00
|
|
|
Buzzer.tune_reload |= tune1 & 1;
|
2019-08-13 10:40:34 +01:00
|
|
|
tune1 >>= 1;
|
|
|
|
}
|
|
|
|
}
|
2021-01-27 15:09:56 +00:00
|
|
|
Buzzer.size = Buzzer.tune_size;
|
2020-01-28 10:22:36 +00:00
|
|
|
Buzzer.tune = Buzzer.tune_reload;
|
2019-08-12 17:20:32 +01:00
|
|
|
}
|
2021-01-27 15:09:56 +00:00
|
|
|
Buzzer.count = count * 2; // Start buzzer
|
2019-08-12 16:18:08 +01:00
|
|
|
|
2021-01-27 15:09:56 +00:00
|
|
|
AddLog(LOG_LEVEL_DEBUG, PSTR("BUZ: Count %d(%d), Time %d/%d, Tune 0x%08X(0x%08X), Size %d, Mode %d"),
|
2021-06-11 17:14:12 +01:00
|
|
|
count, Buzzer.count, on, off, tune, Buzzer.tune, Buzzer.tune_size, Settings->flag4.buzzer_freq_mode);
|
2019-08-12 16:18:08 +01:00
|
|
|
|
2019-10-12 10:06:09 +01:00
|
|
|
Buzzer.enable = (Buzzer.count > 0);
|
2020-10-13 11:02:44 +01:00
|
|
|
if (Buzzer.enable) {
|
2021-05-04 11:31:35 +01:00
|
|
|
Buzzer.sleep = TasmotaGlobal.sleep;
|
2021-06-11 17:14:12 +01:00
|
|
|
if (Settings->sleep > PWM_MAX_SLEEP) {
|
2021-01-27 15:09:56 +00:00
|
|
|
TasmotaGlobal.sleep = PWM_MAX_SLEEP; // Set a maxumum value of 10 milliseconds to ensure that buzzer periods are a bit more accurate
|
2020-10-29 15:16:34 +00:00
|
|
|
} else {
|
2021-06-11 17:14:12 +01:00
|
|
|
TasmotaGlobal.sleep = Settings->sleep; // Or keep the current sleep if it's lower than 10
|
2020-10-29 15:16:34 +00:00
|
|
|
}
|
2021-01-27 15:09:56 +00:00
|
|
|
} else {
|
2021-05-04 11:31:35 +01:00
|
|
|
TasmotaGlobal.sleep = Buzzer.sleep; // Restore original sleep
|
2020-09-09 19:00:55 +01:00
|
|
|
BuzzerSet(0);
|
2019-10-12 10:06:09 +01:00
|
|
|
}
|
2019-08-12 16:18:08 +01:00
|
|
|
}
|
|
|
|
|
2021-01-27 15:09:56 +00:00
|
|
|
void BuzzerSetStateToLed(uint32_t state) {
|
2020-02-06 15:25:37 +00:00
|
|
|
if (Buzzer.enable && (2 == Buzzer.mode)) {
|
|
|
|
Buzzer.state = (state != 0);
|
2020-09-09 19:00:55 +01:00
|
|
|
BuzzerSet(Buzzer.state);
|
2020-01-28 10:22:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-27 15:09:56 +00:00
|
|
|
void BuzzerBeep(uint32_t count) {
|
2020-01-28 10:22:36 +00:00
|
|
|
BuzzerBeep(count, 1, 1, 0, 0);
|
2019-08-12 16:18:08 +01:00
|
|
|
}
|
|
|
|
|
2021-01-27 15:09:56 +00:00
|
|
|
void BuzzerEnabledBeep(uint32_t count, uint32_t duration) {
|
2021-06-11 17:14:12 +01:00
|
|
|
if (Settings->flag3.buzzer_enable) { // SetOption67 - Enable buzzer when available
|
2020-01-28 10:22:36 +00:00
|
|
|
BuzzerBeep(count, duration, 1, 0, 0);
|
2019-08-12 16:18:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************************************/
|
|
|
|
|
2021-01-27 15:09:56 +00:00
|
|
|
bool BuzzerPinState(void) {
|
2020-06-24 14:49:18 +01:00
|
|
|
if (XdrvMailbox.index == AGPIO(GPIO_BUZZER_INV)) {
|
2019-08-13 15:10:47 +01:00
|
|
|
Buzzer.inverted = 1;
|
2020-06-24 14:49:18 +01:00
|
|
|
XdrvMailbox.index -= (AGPIO(GPIO_BUZZER_INV) - AGPIO(GPIO_BUZZER));
|
2019-08-12 16:18:08 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-01-27 15:09:56 +00:00
|
|
|
void BuzzerInit(void) {
|
2020-04-27 11:54:07 +01:00
|
|
|
if (PinUsed(GPIO_BUZZER)) {
|
2020-04-26 16:33:27 +01:00
|
|
|
pinMode(Pin(GPIO_BUZZER), OUTPUT);
|
2020-09-09 19:00:55 +01:00
|
|
|
BuzzerSet(0);
|
2019-08-12 16:18:08 +01:00
|
|
|
} else {
|
2019-08-13 15:10:47 +01:00
|
|
|
Buzzer.active = false;
|
2019-08-12 16:18:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-27 15:09:56 +00:00
|
|
|
void BuzzerEvery100mSec(void) {
|
2020-02-06 15:25:37 +00:00
|
|
|
if (Buzzer.enable && (Buzzer.mode != 2)) {
|
2019-08-13 15:10:47 +01:00
|
|
|
if (Buzzer.count) {
|
|
|
|
if (Buzzer.duration) {
|
|
|
|
Buzzer.duration--;
|
|
|
|
if (!Buzzer.duration) {
|
2021-01-27 15:09:56 +00:00
|
|
|
if (Buzzer.size) {
|
|
|
|
Buzzer.size--;
|
2019-08-13 15:10:47 +01:00
|
|
|
Buzzer.state = Buzzer.tune & 1;
|
|
|
|
Buzzer.tune >>= 1;
|
2019-08-12 17:20:32 +01:00
|
|
|
} else {
|
2021-01-27 15:09:56 +00:00
|
|
|
Buzzer.size = Buzzer.tune_size;
|
2020-01-28 10:22:36 +00:00
|
|
|
Buzzer.tune = Buzzer.tune_reload;
|
2020-02-06 15:25:37 +00:00
|
|
|
Buzzer.count -= (Buzzer.tune_reload) ? 2 : 1;
|
2019-08-13 15:10:47 +01:00
|
|
|
Buzzer.state = Buzzer.count & 1;
|
2020-02-06 15:25:37 +00:00
|
|
|
if (Buzzer.mode) {
|
|
|
|
Buzzer.count |= 2;
|
|
|
|
}
|
2019-08-12 17:20:32 +01:00
|
|
|
}
|
2019-08-13 15:10:47 +01:00
|
|
|
Buzzer.duration = Buzzer.set[Buzzer.state];
|
2019-08-12 16:18:08 +01:00
|
|
|
}
|
|
|
|
}
|
2020-09-09 19:00:55 +01:00
|
|
|
BuzzerSet(Buzzer.state);
|
2019-08-12 16:18:08 +01:00
|
|
|
} else {
|
2021-05-04 11:31:35 +01:00
|
|
|
TasmotaGlobal.sleep = Buzzer.sleep; // Restore original sleep
|
2019-08-13 15:10:47 +01:00
|
|
|
Buzzer.enable = false;
|
2019-08-12 16:18:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************************************\
|
|
|
|
* Commands
|
|
|
|
\*********************************************************************************************/
|
|
|
|
|
2021-01-27 15:58:33 +00:00
|
|
|
const char kBuzzerCommands[] PROGMEM = "Buzzer|" // Prefix
|
|
|
|
"Active|Pwm||" ;
|
|
|
|
|
2021-01-29 10:14:00 +00:00
|
|
|
SO_SYNONYMS(kBuzzerSynonyms,
|
2021-01-27 15:58:33 +00:00
|
|
|
67, 111
|
2021-01-29 10:14:00 +00:00
|
|
|
);
|
2019-08-12 16:18:08 +01:00
|
|
|
|
|
|
|
void (* const BuzzerCommand[])(void) PROGMEM = {
|
|
|
|
&CmndBuzzer };
|
|
|
|
|
2021-01-27 15:09:56 +00:00
|
|
|
void CmndBuzzer(void) {
|
2019-08-13 13:52:46 +01:00
|
|
|
// Buzzer <number of beeps>,<duration of beep in 100mS steps>,<duration of silence in 100mS steps>,<tune>
|
2019-08-12 16:18:08 +01:00
|
|
|
// All parameters are optional
|
|
|
|
//
|
2021-01-27 15:31:15 +00:00
|
|
|
// 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
|
2019-08-12 16:18:08 +01:00
|
|
|
|
|
|
|
if (XdrvMailbox.data_len > 0) {
|
2020-01-28 10:22:36 +00:00
|
|
|
if (XdrvMailbox.payload != 0) {
|
2020-02-06 15:25:37 +00:00
|
|
|
uint32_t parm[4] = { 0 };
|
2020-01-22 10:55:48 +00:00
|
|
|
ParseParameters(4, parm);
|
2021-01-30 16:00:50 +00:00
|
|
|
uint32_t mode = 0;
|
|
|
|
if (XdrvMailbox.payload < 0) {
|
2021-01-27 15:31:15 +00:00
|
|
|
parm[0] = 1; // Default Count
|
2021-01-30 16:00:50 +00:00
|
|
|
if (XdrvMailbox.payload > -3) {
|
|
|
|
mode = -XdrvMailbox.payload; // 0, 1 or 2
|
|
|
|
}
|
2020-01-28 10:22:36 +00:00
|
|
|
}
|
|
|
|
for (uint32_t i = 1; i < 3; i++) {
|
2021-01-27 15:31:15 +00:00
|
|
|
if (parm[i] < 1) { parm[i] = 1; } // Default On time, Off time
|
2019-10-12 10:06:09 +01:00
|
|
|
}
|
2020-01-28 10:22:36 +00:00
|
|
|
BuzzerBeep(parm[0], parm[1], parm[2], parm[3], mode);
|
2019-10-12 10:06:09 +01:00
|
|
|
} else {
|
|
|
|
BuzzerBeep(0);
|
2019-08-12 16:18:08 +01:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
BuzzerBeep(1);
|
|
|
|
}
|
|
|
|
ResponseCmndDone();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************************************\
|
|
|
|
* Interface
|
|
|
|
\*********************************************************************************************/
|
|
|
|
|
2022-11-11 09:44:56 +00:00
|
|
|
bool Xdrv24(uint32_t function) {
|
2019-08-12 16:18:08 +01:00
|
|
|
bool result = false;
|
|
|
|
|
2019-08-13 15:10:47 +01:00
|
|
|
if (Buzzer.active) {
|
2019-08-12 16:18:08 +01:00
|
|
|
switch (function) {
|
|
|
|
case FUNC_EVERY_100_MSECOND:
|
|
|
|
BuzzerEvery100mSec();
|
|
|
|
break;
|
|
|
|
case FUNC_COMMAND:
|
2021-01-27 15:58:33 +00:00
|
|
|
result = DecodeCommand(kBuzzerCommands, BuzzerCommand, kBuzzerSynonyms);
|
2019-08-12 16:18:08 +01:00
|
|
|
break;
|
|
|
|
case FUNC_PRE_INIT:
|
|
|
|
BuzzerInit();
|
|
|
|
break;
|
|
|
|
case FUNC_PIN_STATE:
|
|
|
|
result = BuzzerPinState();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2020-01-28 10:22:36 +00:00
|
|
|
#endif // USE_BUZZER
|