Browse Source

fan: common types and actions

update custom action callback to also get the event
buttons fan actions
ifan buttons are handled through action instead of manually
fan settings and api are (sort-of) generic
mcspr-patch-1
Maxim Prokhorov 3 years ago
parent
commit
972188c83b
11 changed files with 137 additions and 112 deletions
  1. +3
    -0
      code/espurna/board.cpp
  2. +25
    -3
      code/espurna/button.cpp
  3. +5
    -3
      code/espurna/button.h
  4. +2
    -0
      code/espurna/config/dependencies.h
  5. +9
    -0
      code/espurna/config/general.h
  6. +5
    -5
      code/espurna/config/hardware.h
  7. +3
    -0
      code/espurna/config/types.h
  8. +21
    -0
      code/espurna/fan.h
  9. +61
    -87
      code/espurna/ifan.cpp
  10. +0
    -11
      code/espurna/ifan.h
  11. +3
    -3
      code/espurna/main.cpp

+ 3
- 0
code/espurna/board.cpp View File

@ -41,6 +41,9 @@ PROGMEM const char espurna_modules[] =
#if ENCODER_SUPPORT #if ENCODER_SUPPORT
"ENCODER " "ENCODER "
#endif #endif
#if FAN_SUPPORT
"FAN "
#endif
#if HOMEASSISTANT_SUPPORT #if HOMEASSISTANT_SUPPORT
"HOMEASSISTANT " "HOMEASSISTANT "
#endif #endif


+ 25
- 3
code/espurna/button.cpp View File

@ -15,11 +15,12 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
#include <vector> #include <vector>
#include "compat.h" #include "compat.h"
#include "fan.h"
#include "gpio.h" #include "gpio.h"
#include "system.h"
#include "light.h"
#include "mqtt.h" #include "mqtt.h"
#include "relay.h" #include "relay.h"
#include "light.h"
#include "system.h"
#include "ws.h" #include "ws.h"
#include "libs/BasePin.h" #include "libs/BasePin.h"
@ -156,6 +157,9 @@ ButtonAction convert(const String& value) {
case ButtonAction::BrightnessDecrease: case ButtonAction::BrightnessDecrease:
case ButtonAction::DisplayOn: case ButtonAction::DisplayOn:
case ButtonAction::Custom: case ButtonAction::Custom:
case ButtonAction::FanLow:
case ButtonAction::FanMedium:
case ButtonAction::FanHigh:
return action; return action;
} }
} }
@ -588,10 +592,28 @@ void buttonEvent(unsigned char id, button_event_t event) {
case ButtonAction::Custom: case ButtonAction::Custom:
if (_button_custom_action) { if (_button_custom_action) {
_button_custom_action(id);
_button_custom_action(id, event);
} }
break; break;
case ButtonAction::FanLow:
#if FAN_SUPPORT
fanSpeed(FanSpeed::Low);
#endif
break;
case ButtonAction::FanMedium:
#if FAN_SUPPORT
fanSpeed(FanSpeed::Medium);
#endif
break;
case ButtonAction::FanHigh:
#if FAN_SUPPORT
fanSpeed(FanSpeed::High);
#endif
break;
case ButtonAction::None: case ButtonAction::None:
break; break;


+ 5
- 3
code/espurna/button.h View File

@ -22,8 +22,6 @@ constexpr size_t ButtonsActionMax { 255ul };
constexpr size_t ButtonsPresetMax { 8ul }; constexpr size_t ButtonsPresetMax { 8ul };
constexpr size_t ButtonsMax { 32ul }; constexpr size_t ButtonsMax { 32ul };
using ButtonCustomAction = void(*)(unsigned char id);
enum class ButtonProvider : int { enum class ButtonProvider : int {
None, None,
Gpio, Gpio,
@ -57,7 +55,10 @@ enum class ButtonAction : uint8_t {
BrightnessIncrease, BrightnessIncrease,
BrightnessDecrease, BrightnessDecrease,
DisplayOn, DisplayOn,
Custom
Custom,
FanLow,
FanMedium,
FanHigh
}; };
struct ButtonActions { struct ButtonActions {
@ -96,6 +97,7 @@ struct button_t {
BrokerDeclare(ButtonBroker, void(unsigned char id, button_event_t event)); BrokerDeclare(ButtonBroker, void(unsigned char id, button_event_t event));
using ButtonCustomAction = void(*)(unsigned char id, button_event_t event);
void buttonSetCustomAction(ButtonCustomAction); void buttonSetCustomAction(ButtonCustomAction);
bool buttonState(unsigned char id); bool buttonState(unsigned char id);


+ 2
- 0
code/espurna/config/dependencies.h View File

@ -135,6 +135,8 @@
#if IFAN_SUPPORT #if IFAN_SUPPORT
#undef RELAY_SUPPORT #undef RELAY_SUPPORT
#define RELAY_SUPPORT 1 // Need relays to manage general state #define RELAY_SUPPORT 1 // Need relays to manage general state
#undef FAN_SUPPORT
#define FAN_SUPPORT 1
#endif #endif
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------


+ 9
- 0
code/espurna/config/general.h View File

@ -1838,10 +1838,19 @@
#define PROMETHEUS_SUPPORT 0 #define PROMETHEUS_SUPPORT 0
#endif #endif
//--------------------------------------------------------------------------------
// Generic Fan support
//--------------------------------------------------------------------------------
#ifndef FAN_SUPPORT
#define FAN_SUPPORT 0
#endif
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
// ITEAD iFan support // ITEAD iFan support
//-------------------------------------------------------------------------------- //--------------------------------------------------------------------------------
// Note: enabling this will also implicitly enable FAN_SUPPORT
#ifndef IFAN_SUPPORT #ifndef IFAN_SUPPORT
#define IFAN_SUPPORT 0 #define IFAN_SUPPORT 0
#endif #endif


+ 5
- 5
code/espurna/config/hardware.h View File

@ -1018,21 +1018,21 @@
#define DEVICE "SONOFF_IFAN02" #define DEVICE "SONOFF_IFAN02"
// Base module // Base module
#define IFAN_SUPPORT 1
#define FAN_SUPPORT 1
// These buttons are triggered by the remote // These buttons are triggered by the remote
// iFan module adds a custom button handler and a special relay controlling the speed
// Fan module adds a custom button handler and a special relay controlling the speed
#define BUTTON1_PIN 0 #define BUTTON1_PIN 0
#define BUTTON1_CLICK BUTTON_ACTION_TOGGLE #define BUTTON1_CLICK BUTTON_ACTION_TOGGLE
#define BUTTON2_PIN 9 #define BUTTON2_PIN 9
#define BUTTON2_CLICK BUTTON_ACTION_CUSTOM
#define BUTTON2_CLICK BUTTON_ACTION_FAN_LOW
#define BUTTON3_PIN 10 #define BUTTON3_PIN 10
#define BUTTON3_CLICK BUTTON_ACTION_CUSTOM
#define BUTTON3_CLICK BUTTON_ACTION_FAN_MEDIUM
#define BUTTON4_PIN 14 #define BUTTON4_PIN 14
#define BUTTON4_CLICK BUTTON_ACTION_CUSTOM
#define BUTTON4_CLICK BUTTON_ACTION_FAN_HIGH
// LEDs // LEDs
#define LED1_PIN 13 #define LED1_PIN 13


+ 3
- 0
code/espurna/config/types.h View File

@ -43,6 +43,9 @@
#define BUTTON_ACTION_DIM_DOWN ButtonAction::BrightnessDecrease #define BUTTON_ACTION_DIM_DOWN ButtonAction::BrightnessDecrease
#define BUTTON_ACTION_DISPLAY_ON ButtonAction::DisplayOn #define BUTTON_ACTION_DISPLAY_ON ButtonAction::DisplayOn
#define BUTTON_ACTION_CUSTOM ButtonAction::Custom #define BUTTON_ACTION_CUSTOM ButtonAction::Custom
#define BUTTON_ACTION_FAN_LOW ButtonAction::FanLow
#define BUTTON_ACTION_FAN_MEDIUM ButtonAction::FanMedium
#define BUTTON_ACTION_FAN_HIGH ButtonAction::FanHigh
#define BUTTON_ACTION_MAX ButtonsActionMax #define BUTTON_ACTION_MAX ButtonsActionMax


+ 21
- 0
code/espurna/fan.h View File

@ -0,0 +1,21 @@
/*
Fan MODULE
Copyright (C) 2021 by Maxim Prokhorov <prokhorov dot max at outlook dot com>
*/
#pragma once
enum class FanSpeed {
Off,
Low,
Medium,
High
};
void fanSpeed(FanSpeed);
FanSpeed fanSpeed();
void fanSetup();

+ 61
- 87
code/espurna/ifan.cpp View File

@ -14,7 +14,7 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
#if IFAN_SUPPORT #if IFAN_SUPPORT
#include "api.h" #include "api.h"
#include "button.h"
#include "fan.h"
#include "mqtt.h" #include "mqtt.h"
#include "relay.h" #include "relay.h"
#include "terminal.h" #include "terminal.h"
@ -22,60 +22,55 @@ Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
#include <array> #include <array>
#include <utility> #include <utility>
namespace ifan02 {
// TODO: in case there are more FANs, move externally
enum class Speed {
Off,
Low,
Medium,
High
};
namespace ifan02 {
const char* speedToPayload(Speed value) {
const char* speedToPayload(FanSpeed value) {
switch (value) { switch (value) {
case Speed::Off:
case FanSpeed::Off:
return "off"; return "off";
case Speed::Low:
case FanSpeed::Low:
return "low"; return "low";
case Speed::Medium:
case FanSpeed::Medium:
return "medium"; return "medium";
case Speed::High:
case FanSpeed::High:
return "high"; return "high";
} }
return ""; return "";
} }
Speed payloadToSpeed(const char* payload) {
FanSpeed payloadToSpeed(const char* payload) {
auto len = strlen(payload); auto len = strlen(payload);
if (len == 1) { if (len == 1) {
switch (payload[0]) { switch (payload[0]) {
case '0': case '0':
return Speed::Off;
return FanSpeed::Off;
case '1': case '1':
return Speed::Low;
return FanSpeed::Low;
case '2': case '2':
return Speed::Medium;
return FanSpeed::Medium;
case '3': case '3':
return Speed::High;
return FanSpeed::High;
} }
} else if (len > 1) { } else if (len > 1) {
String cmp(payload); String cmp(payload);
if (cmp == "off") { if (cmp == "off") {
return Speed::Off;
return FanSpeed::Off;
} else if (cmp == "low") { } else if (cmp == "low") {
return Speed::Low;
return FanSpeed::Low;
} else if (cmp == "medium") { } else if (cmp == "medium") {
return Speed::Medium;
return FanSpeed::Medium;
} else if (cmp == "high") { } else if (cmp == "high") {
return Speed::High;
return FanSpeed::High;
} }
} }
return Speed::Off;
return FanSpeed::Off;
} }
Speed payloadToSpeed(const String& string) {
FanSpeed payloadToSpeed(const String& string) {
return payloadToSpeed(string.c_str()); return payloadToSpeed(string.c_str());
} }
@ -85,7 +80,7 @@ namespace settings {
namespace internal { namespace internal {
template <> template <>
ifan02::Speed convert(const String& value) {
FanSpeed convert(const String& value) {
return ifan02::payloadToSpeed(value); return ifan02::payloadToSpeed(value);
} }
@ -96,13 +91,6 @@ namespace ifan02 {
constexpr unsigned long DefaultSaveDelay { 1000ul }; constexpr unsigned long DefaultSaveDelay { 1000ul };
// Remote presses trigger GPIO pushbutton events
// Attach to a specific ID to trigger an action
constexpr unsigned char DefaultLowButtonId { 1u };
constexpr unsigned char DefaultMediumButtonId { 2u };
constexpr unsigned char DefaultHighButtonId { 3u };
// We expect to write a specific 'mask' via GPIO LOW & HIGH to set the speed // We expect to write a specific 'mask' via GPIO LOW & HIGH to set the speed
// Sync up with the relay and write it on ON / OFF status events // Sync up with the relay and write it on ON / OFF status events
@ -129,30 +117,20 @@ constexpr int controlPin() {
struct Config { struct Config {
Config() = default; Config() = default;
explicit Config(unsigned long save_, unsigned char buttonLowId_,
unsigned char buttonMediumId_, unsigned char buttonHighId_, Speed speed_) :
explicit Config(unsigned long save_, FanSpeed speed_) :
save(save_), save(save_),
buttonLowId(buttonLowId_),
buttonMediumId(buttonMediumId_),
buttonHighId(buttonHighId_),
speed(speed_) speed(speed_)
{} {}
unsigned long save { DefaultSaveDelay }; unsigned long save { DefaultSaveDelay };
unsigned char buttonLowId { DefaultLowButtonId };
unsigned char buttonMediumId { DefaultMediumButtonId };
unsigned char buttonHighId { DefaultHighButtonId };
Speed speed { Speed::Off };
FanSpeed speed { FanSpeed::Off };
StatePins state_pins; StatePins state_pins;
}; };
Config readSettings() { Config readSettings() {
return Config( return Config(
getSetting("ifanSave", DefaultSaveDelay),
getSetting("ifanBtnLowId", DefaultLowButtonId),
getSetting("ifanBtnMediumId", DefaultMediumButtonId),
getSetting("ifanBtnHighId", DefaultHighButtonId),
getSetting("ifanSpeed", Speed::Medium)
getSetting("fanSave", DefaultSaveDelay),
getSetting("fanSpeed", FanSpeed::Medium)
); );
} }
@ -162,18 +140,18 @@ void configure() {
config = readSettings(); config = readSettings();
} }
void report(Speed speed [[gnu::unused]]) {
void report(FanSpeed speed [[gnu::unused]]) {
#if MQTT_SUPPORT #if MQTT_SUPPORT
mqttSend(MQTT_TOPIC_SPEED, speedToPayload(speed)); mqttSend(MQTT_TOPIC_SPEED, speedToPayload(speed));
#endif #endif
} }
void save(Speed speed) {
void save(FanSpeed speed) {
static Ticker ticker; static Ticker ticker;
config.speed = speed; config.speed = speed;
ticker.once_ms(config.save, []() { ticker.once_ms(config.save, []() {
const char* value = speedToPayload(config.speed); const char* value = speedToPayload(config.speed);
setSetting("ifanSpeed", value);
setSetting("fanSpeed", value);
DEBUG_MSG_P(PSTR("[IFAN] Saved speed setting \"%s\"\n"), value); DEBUG_MSG_P(PSTR("[IFAN] Saved speed setting \"%s\"\n"), value);
}); });
} }
@ -203,30 +181,30 @@ StatePins setupStatePins() {
return pins; return pins;
} }
State stateFromSpeed(Speed speed) {
State stateFromSpeed(FanSpeed speed) {
switch (speed) { switch (speed) {
case Speed::Low:
case FanSpeed::Low:
return {HIGH, LOW, LOW}; return {HIGH, LOW, LOW};
case Speed::Medium:
case FanSpeed::Medium:
return {HIGH, HIGH, LOW}; return {HIGH, HIGH, LOW};
case Speed::High:
case FanSpeed::High:
return {HIGH, LOW, HIGH}; return {HIGH, LOW, HIGH};
case Speed::Off:
case FanSpeed::Off:
break; break;
} }
return {LOW, LOW, LOW}; return {LOW, LOW, LOW};
} }
const char* maskFromSpeed(Speed speed) {
const char* maskFromSpeed(FanSpeed speed) {
switch (speed) { switch (speed) {
case Speed::Low:
case FanSpeed::Low:
return "0b100"; return "0b100";
case Speed::Medium:
case FanSpeed::Medium:
return "0b110"; return "0b110";
case Speed::High:
case FanSpeed::High:
return "0b101"; return "0b101";
case Speed::Off:
case FanSpeed::Off:
return "0b000"; return "0b000";
} }
@ -236,26 +214,26 @@ const char* maskFromSpeed(Speed speed) {
// Note that we use API speed endpoint strictly for the setting // Note that we use API speed endpoint strictly for the setting
// (which also allows to pre-set the speed without turning the relay ON) // (which also allows to pre-set the speed without turning the relay ON)
using FanSpeedUpdate = std::function<void(Speed)>;
using FanSpeedUpdate = std::function<void(FanSpeed)>;
FanSpeedUpdate onSpeedUpdate = [](Speed) {
FanSpeedUpdate onFanSpeedUpdate = [](FanSpeed) {
}; };
void updateSpeed(Config& config, Speed speed) {
void updateSpeed(Config& config, FanSpeed speed) {
switch (speed) { switch (speed) {
case Speed::Low:
case Speed::Medium:
case Speed::High:
case FanSpeed::Low:
case FanSpeed::Medium:
case FanSpeed::High:
save(speed); save(speed);
report(speed); report(speed);
onSpeedUpdate(speed);
onFanSpeedUpdate(speed);
break; break;
case Speed::Off:
case FanSpeed::Off:
break; break;
} }
} }
void updateSpeed(Speed speed) {
void updateSpeed(FanSpeed speed) {
updateSpeed(config, speed); updateSpeed(config, speed);
} }
@ -295,17 +273,18 @@ public:
_pin(std::move(pin)), _pin(std::move(pin)),
_config(config) _config(config)
{ {
callback = [this](Speed speed) {
callback = [this](FanSpeed speed) {
change(speed); change(speed);
}; };
_pin->pinMode(OUTPUT);
} }
const char* id() const override { const char* id() const override {
return "fan"; return "fan";
} }
void change(Speed speed) {
_pin->digitalWrite((Speed::Off != speed) ? HIGH : LOW);
void change(FanSpeed speed) {
_pin->digitalWrite((FanSpeed::Off != speed) ? HIGH : LOW);
auto state = stateFromSpeed(speed); auto state = stateFromSpeed(speed);
DEBUG_MSG_P(PSTR("[IFAN] State mask: %s\n"), maskFromSpeed(speed)); DEBUG_MSG_P(PSTR("[IFAN] State mask: %s\n"), maskFromSpeed(speed));
@ -321,7 +300,7 @@ public:
} }
void change(bool status) override { void change(bool status) override {
change(status ? _config.speed : Speed::Off);
change(status ? _config.speed : FanSpeed::Off);
} }
private: private:
@ -342,26 +321,13 @@ void setup() {
auto relay_pin = gpioRegister(controlPin()); auto relay_pin = gpioRegister(controlPin());
if (relay_pin) { if (relay_pin) {
relay_pin->pinMode(OUTPUT);
auto provider = std::make_unique<FanProvider>(std::move(relay_pin), config, onSpeedUpdate);
auto provider = std::make_unique<FanProvider>(std::move(relay_pin), config, onFanSpeedUpdate);
if (!relayAdd(std::move(provider))) { if (!relayAdd(std::move(provider))) {
DEBUG_MSG_P(PSTR("[IFAN] Could not add relay provider for GPIO%d\n"), controlPin()); DEBUG_MSG_P(PSTR("[IFAN] Could not add relay provider for GPIO%d\n"), controlPin());
gpioUnlock(controlPin()); gpioUnlock(controlPin());
} }
} }
#if BUTTON_SUPPORT
buttonSetCustomAction([](unsigned char id) {
if (config.buttonLowId == id) {
updateSpeed(Speed::Low);
} else if (config.buttonMediumId == id) {
updateSpeed(Speed::Medium);
} else if (config.buttonHighId == id) {
updateSpeed(Speed::High);
}
});
#endif
#if MQTT_SUPPORT #if MQTT_SUPPORT
mqttRegister(onMqttEvent); mqttRegister(onMqttEvent);
#endif #endif
@ -394,7 +360,15 @@ void setup() {
} // namespace ifan } // namespace ifan
void ifanSetup() {
FanSpeed fanSpeed() {
return ifan02::config.speed;
}
void fanSpeed(FanSpeed speed) {
ifan02::updateSpeed(FanSpeed::Low);
}
void fanSetup() {
ifan02::setup(); ifan02::setup();
} }


+ 0
- 11
code/espurna/ifan.h View File

@ -1,11 +0,0 @@
/*
iFan MODULE
Copyright (C) 2016-2019 by Xose Pérez <xose dot perez at gmail dot com>
*/
#pragma once
void ifanSetup();

+ 3
- 3
code/espurna/main.cpp View File

@ -34,7 +34,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "garland.h" #include "garland.h"
#include "i2c.h" #include "i2c.h"
#include "influxdb.h" #include "influxdb.h"
#include "ifan.h"
#include "fan.h"
#include "ir.h" #include "ir.h"
#include "led.h" #include "led.h"
#include "light.h" #include "light.h"
@ -305,8 +305,8 @@ void setup() {
#if KINGART_CURTAIN_SUPPORT #if KINGART_CURTAIN_SUPPORT
kingartCurtainSetup(); kingartCurtainSetup();
#endif #endif
#if IFAN_SUPPORT
ifanSetup();
#if FAN_SUPPORT
fanSetup();
#endif #endif
// 3rd party code hook // 3rd party code hook


Loading…
Cancel
Save