Tasmota/sonoff/support_rotary.ino

169 lines
4.9 KiB
C++

/*
support_rotary.ino - rotary switch support for Sonoff-Tasmota
Copyright (C) 2019 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_LIGHT
//#define ROTARY_V1
#ifdef ROTARY_V1
/*********************************************************************************************\
* Rotary support
\*********************************************************************************************/
struct ROTARY {
unsigned long debounce = 0; // Rotary debounce timer
uint8_t present = 0;
uint8_t state = 0;
uint8_t position = 128;
uint8_t last_position = 128;
uint8_t interrupts_in_use_count = 0;
uint8_t changed = 0;
} Rotary;
/********************************************************************************************/
void update_position(void)
{
uint8_t s;
/*
* https://github.com/PaulStoffregen/Encoder/blob/master/Encoder.h
*/
s = Rotary.state & 3;
if (digitalRead(pin[GPIO_ROT1A])) s |= 4;
if (digitalRead(pin[GPIO_ROT1B])) s |= 8;
switch (s) {
case 0: case 5: case 10: case 15:
break;
case 1: case 7: case 8: case 14:
Rotary.position++; break;
case 2: case 4: case 11: case 13:
Rotary.position--; break;
case 3: case 12:
Rotary.position = Rotary.position + 2; break;
default:
Rotary.position = Rotary.position - 2; break;
}
Rotary.state = (s >> 2);
}
#ifndef ARDUINO_ESP8266_RELEASE_2_3_0 // Fix core 2.5.x ISR not in IRAM Exception
void update_rotary(void) ICACHE_RAM_ATTR;
#endif // ARDUINO_ESP8266_RELEASE_2_3_0
void update_rotary(void)
{
if (MI_DESK_LAMP == my_module_type){
if (LightPower()) {
update_position();
}
}
}
bool RotaryButtonPressed(void)
{
if ((MI_DESK_LAMP == my_module_type) && (Rotary.changed) && LightPower()) {
Rotary.changed = 0; // Color temp changed, no need to turn of the light
return true;
}
return false;
}
void RotaryInit(void)
{
Rotary.present = 0;
if ((pin[GPIO_ROT1A] < 99) && (pin[GPIO_ROT1B] < 99)) {
Rotary.present++;
pinMode(pin[GPIO_ROT1A], INPUT_PULLUP);
pinMode(pin[GPIO_ROT1B], INPUT_PULLUP);
// GPIO6-GPIO11 are typically used to interface with the flash memory IC on
// most esp8266 modules, so we should avoid adding interrupts to these pins.
if ((pin[GPIO_ROT1A] < 6) || (pin[GPIO_ROT1A] > 11)) {
attachInterrupt(digitalPinToInterrupt(pin[GPIO_ROT1A]), update_rotary, CHANGE);
Rotary.interrupts_in_use_count++;
}
if ((pin[GPIO_ROT1B] < 6) || (pin[GPIO_ROT1B] > 11)) {
attachInterrupt(digitalPinToInterrupt(pin[GPIO_ROT1B]), update_rotary, CHANGE);
Rotary.interrupts_in_use_count++;
}
}
}
/*********************************************************************************************\
* Rotary handler
\*********************************************************************************************/
void RotaryHandler(void)
{
if (Rotary.interrupts_in_use_count < 2) {
noInterrupts();
update_rotary();
} else {
noInterrupts();
}
if (Rotary.last_position != Rotary.position) {
if (MI_DESK_LAMP == my_module_type) { // Mi Desk lamp
if (Button.hold_timer[0]) {
Rotary.changed = 1;
// button1 is pressed: set color temperature
int16_t t = LightGetColorTemp();
t = t + (Rotary.position - Rotary.last_position);
if (t < 153) {
t = 153;
}
if (t > 500) {
t = 500;
}
DEBUG_CORE_LOG(PSTR("ROT: " D_CMND_COLORTEMPERATURE " %d"), Rotary.position - Rotary.last_position);
LightSetColorTemp((uint16_t)t);
} else {
int8_t d = Settings.light_dimmer;
d = d + (Rotary.position - Rotary.last_position);
if (d < 1) {
d = 1;
}
if (d > 100) {
d = 100;
}
DEBUG_CORE_LOG(PSTR("ROT: " D_CMND_DIMMER " %d"), Rotary.position - Rotary.last_position);
LightSetDimmer((uint8_t)d);
Settings.light_dimmer = d;
}
}
Rotary.last_position = 128;
Rotary.position = 128;
}
interrupts();
}
void RotaryLoop(void)
{
if (Rotary.present) {
if (TimeReached(Rotary.debounce)) {
SetNextTimeInterval(Rotary.debounce, Settings.button_debounce); // Using button_debounce setting for this as well
RotaryHandler();
}
}
}
#endif // ROTARY_V1
#endif // USE_LIGHT