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
"ENCODER "
#endif
#if FAN_SUPPORT
"FAN "
#endif
#if HOMEASSISTANT_SUPPORT
"HOMEASSISTANT "
#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 "compat.h"
#include "fan.h"
#include "gpio.h"
#include "system.h"
#include "light.h"
#include "mqtt.h"
#include "relay.h"
#include "light.h"
#include "system.h"
#include "ws.h"
#include "libs/BasePin.h"
@ -156,6 +157,9 @@ ButtonAction convert(const String& value) {
case ButtonAction::BrightnessDecrease:
case ButtonAction::DisplayOn:
case ButtonAction::Custom:
case ButtonAction::FanLow:
case ButtonAction::FanMedium:
case ButtonAction::FanHigh:
return action;
}
}
@ -588,10 +592,28 @@ void buttonEvent(unsigned char id, button_event_t event) {
case ButtonAction::Custom:
if (_button_custom_action) {
_button_custom_action(id);
_button_custom_action(id, event);
}
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:
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 ButtonsMax { 32ul };
using ButtonCustomAction = void(*)(unsigned char id);
enum class ButtonProvider : int {
None,
Gpio,
@ -57,7 +55,10 @@ enum class ButtonAction : uint8_t {
BrightnessIncrease,
BrightnessDecrease,
DisplayOn,
Custom
Custom,
FanLow,
FanMedium,
FanHigh
};
struct ButtonActions {
@ -96,6 +97,7 @@ struct button_t {
BrokerDeclare(ButtonBroker, void(unsigned char id, button_event_t event));
using ButtonCustomAction = void(*)(unsigned char id, button_event_t event);
void buttonSetCustomAction(ButtonCustomAction);
bool buttonState(unsigned char id);


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

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


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

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


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

@ -1018,21 +1018,21 @@
#define DEVICE "SONOFF_IFAN02"
// Base module
#define IFAN_SUPPORT 1
#define FAN_SUPPORT 1
// 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_CLICK BUTTON_ACTION_TOGGLE
#define BUTTON2_PIN 9
#define BUTTON2_CLICK BUTTON_ACTION_CUSTOM
#define BUTTON2_CLICK BUTTON_ACTION_FAN_LOW
#define BUTTON3_PIN 10
#define BUTTON3_CLICK BUTTON_ACTION_CUSTOM
#define BUTTON3_CLICK BUTTON_ACTION_FAN_MEDIUM
#define BUTTON4_PIN 14
#define BUTTON4_CLICK BUTTON_ACTION_CUSTOM
#define BUTTON4_CLICK BUTTON_ACTION_FAN_HIGH
// LEDs
#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_DISPLAY_ON ButtonAction::DisplayOn
#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


+ 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
#include "api.h"
#include "button.h"
#include "fan.h"
#include "mqtt.h"
#include "relay.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 <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) {
case Speed::Off:
case FanSpeed::Off:
return "off";
case Speed::Low:
case FanSpeed::Low:
return "low";
case Speed::Medium:
case FanSpeed::Medium:
return "medium";
case Speed::High:
case FanSpeed::High:
return "high";
}
return "";
}
Speed payloadToSpeed(const char* payload) {
FanSpeed payloadToSpeed(const char* payload) {
auto len = strlen(payload);
if (len == 1) {
switch (payload[0]) {
case '0':
return Speed::Off;
return FanSpeed::Off;
case '1':
return Speed::Low;
return FanSpeed::Low;
case '2':
return Speed::Medium;
return FanSpeed::Medium;
case '3':
return Speed::High;
return FanSpeed::High;
}
} else if (len > 1) {
String cmp(payload);
if (cmp == "off") {
return Speed::Off;
return FanSpeed::Off;
} else if (cmp == "low") {
return Speed::Low;
return FanSpeed::Low;
} else if (cmp == "medium") {
return Speed::Medium;
return FanSpeed::Medium;
} 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());
}
@ -85,7 +80,7 @@ namespace settings {
namespace internal {
template <>
ifan02::Speed convert(const String& value) {
FanSpeed convert(const String& value) {
return ifan02::payloadToSpeed(value);
}
@ -96,13 +91,6 @@ namespace ifan02 {
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
// Sync up with the relay and write it on ON / OFF status events
@ -129,30 +117,20 @@ constexpr int controlPin() {
struct Config {
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_),
buttonLowId(buttonLowId_),
buttonMediumId(buttonMediumId_),
buttonHighId(buttonHighId_),
speed(speed_)
{}
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;
};
Config readSettings() {
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();
}
void report(Speed speed [[gnu::unused]]) {
void report(FanSpeed speed [[gnu::unused]]) {
#if MQTT_SUPPORT
mqttSend(MQTT_TOPIC_SPEED, speedToPayload(speed));
#endif
}
void save(Speed speed) {
void save(FanSpeed speed) {
static Ticker ticker;
config.speed = speed;
ticker.once_ms(config.save, []() {
const char* value = speedToPayload(config.speed);
setSetting("ifanSpeed", value);
setSetting("fanSpeed", value);
DEBUG_MSG_P(PSTR("[IFAN] Saved speed setting \"%s\"\n"), value);
});
}
@ -203,30 +181,30 @@ StatePins setupStatePins() {
return pins;
}
State stateFromSpeed(Speed speed) {
State stateFromSpeed(FanSpeed speed) {
switch (speed) {
case Speed::Low:
case FanSpeed::Low:
return {HIGH, LOW, LOW};
case Speed::Medium:
case FanSpeed::Medium:
return {HIGH, HIGH, LOW};
case Speed::High:
case FanSpeed::High:
return {HIGH, LOW, HIGH};
case Speed::Off:
case FanSpeed::Off:
break;
}
return {LOW, LOW, LOW};
}
const char* maskFromSpeed(Speed speed) {
const char* maskFromSpeed(FanSpeed speed) {
switch (speed) {
case Speed::Low:
case FanSpeed::Low:
return "0b100";
case Speed::Medium:
case FanSpeed::Medium:
return "0b110";
case Speed::High:
case FanSpeed::High:
return "0b101";
case Speed::Off:
case FanSpeed::Off:
return "0b000";
}
@ -236,26 +214,26 @@ const char* maskFromSpeed(Speed speed) {
// Note that we use API speed endpoint strictly for the setting
// (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) {
case Speed::Low:
case Speed::Medium:
case Speed::High:
case FanSpeed::Low:
case FanSpeed::Medium:
case FanSpeed::High:
save(speed);
report(speed);
onSpeedUpdate(speed);
onFanSpeedUpdate(speed);
break;
case Speed::Off:
case FanSpeed::Off:
break;
}
}
void updateSpeed(Speed speed) {
void updateSpeed(FanSpeed speed) {
updateSpeed(config, speed);
}
@ -295,17 +273,18 @@ public:
_pin(std::move(pin)),
_config(config)
{
callback = [this](Speed speed) {
callback = [this](FanSpeed speed) {
change(speed);
};
_pin->pinMode(OUTPUT);
}
const char* id() const override {
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);
DEBUG_MSG_P(PSTR("[IFAN] State mask: %s\n"), maskFromSpeed(speed));
@ -321,7 +300,7 @@ public:
}
void change(bool status) override {
change(status ? _config.speed : Speed::Off);
change(status ? _config.speed : FanSpeed::Off);
}
private:
@ -342,26 +321,13 @@ void setup() {
auto relay_pin = gpioRegister(controlPin());
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))) {
DEBUG_MSG_P(PSTR("[IFAN] Could not add relay provider for GPIO%d\n"), 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
mqttRegister(onMqttEvent);
#endif
@ -394,7 +360,15 @@ void setup() {
} // namespace ifan
void ifanSetup() {
FanSpeed fanSpeed() {
return ifan02::config.speed;
}
void fanSpeed(FanSpeed speed) {
ifan02::updateSpeed(FanSpeed::Low);
}
void fanSetup() {
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 "i2c.h"
#include "influxdb.h"
#include "ifan.h"
#include "fan.h"
#include "ir.h"
#include "led.h"
#include "light.h"
@ -305,8 +305,8 @@ void setup() {
#if KINGART_CURTAIN_SUPPORT
kingartCurtainSetup();
#endif
#if IFAN_SUPPORT
ifanSetup();
#if FAN_SUPPORT
fanSetup();
#endif
// 3rd party code hook


Loading…
Cancel
Save