6.6.0.2 Add support for Sonoff iFan03 as module 71

Add support for Sonoff iFan03 as module 71 (#5988)
This commit is contained in:
Theo Arends 2019-07-14 15:23:02 +02:00
parent 3c98acea65
commit 49022d0320
10 changed files with 406 additions and 75 deletions

View File

@ -1,7 +1,14 @@
/*********************************************************************************************\
* 6.6.0.2 20190714
* Add support for Sonoff iFan03 as module 71 (#5988)
* Add support for a buzzer
* Add command SetOption67 0/1 to disable or enable a buzzer as used in iFan03
*
* 6.6.0.1 20190708
* Fix Domoticz battery level set to 100 if define USE_ADC_VCC is not used (#6033)
* Fix Force Elliptic Curve for Letsencrypt TLS #6042
* Fix WeMo emulation for 1G echo and 2G echo dot (#6086)
* Fix Xiaomi Philips brightness (#6091)
* Change defines USE_TX20_WIND_SENSOR and USE_RC_SWITCH in my_user_config.h to disable to lower iram usage enabling latest core compilation (#6060, #6062)
* Add blend RGB leds with White leds for better whites (#5895, #5704)
* Add command SetOption41 0..8 to control number of Tuya switches (#6039)
@ -11,8 +18,6 @@
* Add AZ7798 automatic setting of clock display (#6034)
* Add Epoch and UptimeSec to JSON messages (#6068)
* Add support for up to 4 INA219 sensors (#6046)
* Fix WeMo emulation for 1G echo and 2G echo dot (#6086)
* Fix Xiaomi Philips brightness (#6091)
*
* 6.6.0 20190707
* Remove support of TLS on core 2.3.0 and extent support on core 2.4.2 and up

View File

@ -78,9 +78,9 @@ typedef union { // Restricted by MISRA-C Rule 18.4 bu
uint32_t no_hold_retain : 1; // bit 12 (v6.4.1.19) - SetOption62 - Don't use retain flag on HOLD messages
uint32_t no_power_feedback : 1; // bit 13 (v6.5.0.9) - SetOption63 - Don't scan relay power state at restart
uint32_t use_underscore : 1; // bit 14 (v6.5.0.12) - SetOption64 - Enable "_" instead of "-" as sensor index separator
uint32_t tuya_show_dimmer : 1; // bit 15 (v6.5.0.15) - SetOption65 - Enable or Disable Dimmer slider control
uint32_t tuya_dimmer_range_255 : 1; // bit 16 (v6.6.0.2) - SetOption66 - Enable or Disable Dimmer range 255 slider control
uint32_t spare17 : 1;
uint32_t tuya_show_dimmer : 1; // bit 15 (v6.5.0.15) - SetOption65 - Enable or Disable Dimmer slider control
uint32_t tuya_dimmer_range_255 : 1; // bit 16 (v6.6.0.1) - SetOption66 - Enable or Disable Dimmer range 255 slider control
uint32_t buzzer_enable : 1; // bit 17 (v6.6.0.1) - SetOption67 - Enable buzzer when available
uint32_t spare18 : 1;
uint32_t spare19 : 1;
uint32_t spare20 : 1;

View File

@ -71,8 +71,6 @@ const uint8_t MAX_RULE_MEMS = 5; // Max number of saved vars
const uint8_t MAX_RULE_SETS = 3; // Max number of rule sets of size 512 characters
const uint16_t MAX_RULE_SIZE = 512; // Max number of characters in rules
const uint8_t MAX_FAN_SPEED = 4; // Max number of iFan02 fan speeds (0 .. 3)
const char MQTT_TOKEN_PREFIX[] PROGMEM = "%prefix%"; // To be substituted by mqtt_prefix[x]
const char MQTT_TOKEN_TOPIC[] PROGMEM = "%topic%"; // To be substituted by mqtt_topic, mqtt_grptopic, mqtt_buttontopic, mqtt_switchtopic
const char WIFI_HOSTNAME[] = "%s-%04d"; // Expands to <MQTT_TOPIC>-<last 4 decimal chars of MAC address>
@ -256,13 +254,11 @@ enum XsnsFunctions {FUNC_SETTINGS_OVERRIDE, FUNC_MODULE_INIT, FUNC_PRE_INIT, FUN
FUNC_WEB_ADD_BUTTON, FUNC_WEB_ADD_MAIN_BUTTON, FUNC_WEB_ADD_HANDLER, FUNC_SET_CHANNELS};
enum CommandSource { SRC_IGNORE, SRC_MQTT, SRC_RESTART, SRC_BUTTON, SRC_SWITCH, SRC_BACKLOG, SRC_SERIAL, SRC_WEBGUI, SRC_WEBCOMMAND, SRC_WEBCONSOLE, SRC_PULSETIMER,
SRC_TIMER, SRC_RULE, SRC_MAXPOWER, SRC_MAXENERGY, SRC_OVERTEMP, SRC_LIGHT, SRC_KNX, SRC_DISPLAY, SRC_WEMO, SRC_HUE, SRC_RETRY, SRC_MAX };
const char kCommandSource[] PROGMEM = "I|MQTT|Restart|Button|Switch|Backlog|Serial|WebGui|WebCommand|WebConsole|PulseTimer|Timer|Rule|MaxPower|MaxEnergy|Overtemp|Light|Knx|Display|Wemo|Hue|Retry";
SRC_TIMER, SRC_RULE, SRC_MAXPOWER, SRC_MAXENERGY, SRC_OVERTEMP, SRC_LIGHT, SRC_KNX, SRC_DISPLAY, SRC_WEMO, SRC_HUE, SRC_RETRY, SRC_REMOTE, SRC_MAX };
const char kCommandSource[] PROGMEM = "I|MQTT|Restart|Button|Switch|Backlog|Serial|WebGui|WebCommand|WebConsole|PulseTimer|Timer|Rule|MaxPower|MaxEnergy|Overtemp|Light|Knx|Display|Wemo|Hue|Retry|Remote";
const uint8_t kDefaultRfCode[9] PROGMEM = { 0x21, 0x16, 0x01, 0x0E, 0x03, 0x48, 0x2E, 0x1A, 0x00 };
const uint8_t kIFan02Speed[MAX_FAN_SPEED][3] = {{6,6,6}, {7,6,6}, {7,7,6}, {7,6,7}}; // Do not use PROGMEM as it fails
/*********************************************************************************************\
* Extern global variables
\*********************************************************************************************/

View File

@ -73,7 +73,7 @@
#include "settings.h"
enum TasmotaCommands {
CMND_BACKLOG, CMND_DELAY, CMND_POWER, CMND_FANSPEED, CMND_STATUS, CMND_STATE, CMND_POWERONSTATE, CMND_PULSETIME,
CMND_BACKLOG, CMND_DELAY, CMND_POWER, CMND_STATUS, CMND_STATE, CMND_POWERONSTATE, CMND_PULSETIME,
CMND_BLINKTIME, CMND_BLINKCOUNT, CMND_SENSOR, CMND_SAVEDATA, CMND_SETOPTION, CMND_TEMPERATURE_RESOLUTION, CMND_HUMIDITY_RESOLUTION,
CMND_PRESSURE_RESOLUTION, CMND_POWER_RESOLUTION, CMND_VOLTAGE_RESOLUTION, CMND_FREQUENCY_RESOLUTION, CMND_CURRENT_RESOLUTION, CMND_ENERGY_RESOLUTION, CMND_WEIGHT_RESOLUTION,
CMND_MODULE, CMND_MODULES, CMND_ADC, CMND_ADCS, CMND_GPIO, CMND_GPIOS, CMND_PWM, CMND_PWMFREQUENCY, CMND_PWMRANGE, CMND_COUNTER, CMND_COUNTERTYPE,
@ -83,7 +83,7 @@ enum TasmotaCommands {
CMND_TELEPERIOD, CMND_RESTART, CMND_RESET, CMND_TIME, CMND_TIMEZONE, CMND_TIMESTD, CMND_TIMEDST, CMND_ALTITUDE, CMND_LEDPOWER, CMND_LEDSTATE, CMND_LEDMASK,
CMND_I2CSCAN, CMND_SERIALSEND, CMND_BAUDRATE, CMND_SERIALDELIMITER, CMND_DRIVER };
const char kTasmotaCommands[] PROGMEM =
D_CMND_BACKLOG "|" D_CMND_DELAY "|" D_CMND_POWER "|" D_CMND_FANSPEED "|" D_CMND_STATUS "|" D_CMND_STATE "|" D_CMND_POWERONSTATE "|" D_CMND_PULSETIME "|"
D_CMND_BACKLOG "|" D_CMND_DELAY "|" D_CMND_POWER "|" D_CMND_STATUS "|" D_CMND_STATE "|" D_CMND_POWERONSTATE "|" D_CMND_PULSETIME "|"
D_CMND_BLINKTIME "|" D_CMND_BLINKCOUNT "|" D_CMND_SENSOR "|" D_CMND_SAVEDATA "|" D_CMND_SETOPTION "|" D_CMND_TEMPERATURE_RESOLUTION "|" D_CMND_HUMIDITY_RESOLUTION "|"
D_CMND_PRESSURE_RESOLUTION "|" D_CMND_POWER_RESOLUTION "|" D_CMND_VOLTAGE_RESOLUTION "|" D_CMND_FREQUENCY_RESOLUTION "|" D_CMND_CURRENT_RESOLUTION "|" D_CMND_ENERGY_RESOLUTION "|" D_CMND_WEIGHT_RESOLUTION "|"
D_CMND_MODULE "|" D_CMND_MODULES "|" D_CMND_ADC "|" D_CMND_ADCS "|" D_CMND_GPIO "|" D_CMND_GPIOS "|" D_CMND_PWM "|" D_CMND_PWMFREQUENCY "|" D_CMND_PWMRANGE "|" D_CMND_COUNTER "|" D_CMND_COUNTERTYPE "|"
@ -151,6 +151,7 @@ uint8_t leds_present = 0; // Max number of LED supported
uint8_t led_inverted = 0; // LED inverted flag (1 = (0 = On, 1 = Off))
uint8_t led_power = 0; // LED power state
uint8_t ledlnk_inverted = 0; // Link LED inverted flag (1 = (0 = On, 1 = Off))
uint8_t buzzer_inverted = 0; // Buzzer inverted flag (1 = (0 = On, 1 = Off))
uint8_t pwm_inverted = 0; // PWM inverted flag (1 = inverted)
uint8_t counter_no_pullup = 0; // Counter input pullup flag (1 = No pullup)
uint8_t energy_flg = 0; // Energy monitor configured
@ -163,6 +164,7 @@ uint8_t seriallog_level; // Current copy of Settings.seriallo
uint8_t syslog_level; // Current copy of Settings.syslog_level
uint8_t my_module_type; // Current copy of Settings.module or user template type
uint8_t my_adc0; // Active copy of Module ADC0
uint8_t buzzer_count = 0; // Number of buzzes
//uint8_t mdns_delayed_start = 0; // mDNS delayed start
bool serial_local = false; // Handle serial locally;
bool fallback_topic_flag = false; // Use Topic or FallbackTopic
@ -430,35 +432,6 @@ void SetLedLink(uint8_t state)
}
}
uint8_t GetFanspeed(void)
{
uint8_t fanspeed = 0;
// if (SONOFF_IFAN02 == my_module_type) {
/* Fanspeed is controlled by relay 2, 3 and 4 as in Sonoff 4CH.
000x = 0
001x = 1
011x = 2
101x = 3
*/
fanspeed = (uint8_t)(power &0xF) >> 1;
if (fanspeed) { fanspeed = (fanspeed >> 1) +1; }
// }
return fanspeed;
}
void SetFanspeed(uint8_t fanspeed)
{
for (uint32_t i = 0; i < MAX_FAN_SPEED -1; i++) {
uint8_t state = kIFan02Speed[fanspeed][i];
// uint8_t state = pgm_read_byte(kIFan02Speed +(speed *3) +i);
ExecuteCommandPower(i +2, state, SRC_IGNORE); // Use relay 2, 3 and 4
}
#ifdef USE_DOMOTICZ
DomoticzUpdateFanState(); // Command FanSpeed feedback
#endif // USE_DOMOTICZ
}
void SetPulseTimer(uint8_t index, uint16_t time)
{
pulse_timer[index] = (time > 111) ? millis() + (1000 * (time - 100)) : (time > 0) ? millis() + (100 * time) : 0L;
@ -636,22 +609,6 @@ void MqttDataHandler(char* topic, uint8_t* data, unsigned int data_len)
fallback_topic_flag = false;
return;
}
else if ((CMND_FANSPEED == command_code) && (SONOFF_IFAN02 == my_module_type)) {
if (data_len > 0) {
if ('-' == dataBuf[0]) {
payload = (int16_t)GetFanspeed() -1;
if (payload < 0) { payload = MAX_FAN_SPEED -1; }
}
else if ('+' == dataBuf[0]) {
payload = GetFanspeed() +1;
if (payload > MAX_FAN_SPEED -1) { payload = 0; }
}
}
if ((payload >= 0) && (payload < MAX_FAN_SPEED) && (payload != GetFanspeed())) {
SetFanspeed(payload);
}
Response_P(S_JSON_COMMAND_NVALUE, command, GetFanspeed());
}
else if (CMND_STATUS == command_code) {
if ((payload < 0) || (payload > MAX_STATUS)) payload = 99;
PublishStatus(payload);
@ -1675,7 +1632,7 @@ void ExecuteCommandPower(uint8_t device, uint8_t state, int source)
// ShowSource(source);
if (SONOFF_IFAN02 == my_module_type) {
if (IsModuleIfan()) {
blink_mask &= 1; // No blinking on the fan relays
Settings.flag.interlock = 0; // No interlock mode as it is already done by the microcontroller
Settings.pulse_timer[1] = 0; // No pulsetimers on the fan relays
@ -1826,7 +1783,7 @@ void PublishStatus(uint8_t payload)
if ((0 == payload) || (99 == payload)) {
uint8_t maxfn = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : (!devices_present) ? 1 : devices_present;
if (SONOFF_IFAN02 == my_module_type) { maxfn = 1; }
if (IsModuleIfan()) { maxfn = 1; }
stemp[0] = '\0';
for (uint32_t i = 0; i < maxfn; i++) {
snprintf_P(stemp, sizeof(stemp), PSTR("%s%s\"%s\"" ), stemp, (i > 0 ? "," : ""), Settings.friendlyname[i]);
@ -1965,7 +1922,7 @@ void MqttShowState(void)
} else {
#endif
ResponseAppend_P(PSTR(",\"%s\":\"%s\""), GetPowerDevice(stemp1, i +1, sizeof(stemp1), Settings.flag.device_index_enable), GetStateText(bitRead(power, i)));
if (SONOFF_IFAN02 == my_module_type) {
if (IsModuleIfan()) {
ResponseAppend_P(PSTR(",\"" D_CMND_FANSPEED "\":%d"), GetFanspeed());
break;
}
@ -2144,6 +2101,16 @@ void Every100mSeconds(void)
if (backlog_pointer >= MAX_BACKLOG) { backlog_pointer = 0; }
}
}
if ((pin[GPIO_BUZZER] < 99) && (Settings.flag3.buzzer_enable)) {
if (buzzer_count) {
buzzer_count--;
uint8_t state = buzzer_count & 1;
digitalWrite(pin[GPIO_BUZZER], (buzzer_inverted) ? !state : state);
}
} else {
buzzer_count = 0;
}
}
/*-------------------------------------------------------------------------------------------*\
@ -2609,6 +2576,10 @@ void GpioInit(void)
ledlnk_inverted = 1;
mpin -= (GPIO_LEDLNK_INV - GPIO_LEDLNK);
}
else if (mpin == GPIO_BUZZER_INV) {
buzzer_inverted = 1;
mpin -= (GPIO_BUZZER_INV - GPIO_BUZZER);
}
else if ((mpin >= GPIO_PWM1_INV) && (mpin < (GPIO_PWM1_INV + MAX_PWMS))) {
bitSet(pwm_inverted, mpin - GPIO_PWM1_INV);
mpin -= (GPIO_PWM1_INV - GPIO_PWM1);
@ -2742,6 +2713,10 @@ void GpioInit(void)
pinMode(pin[GPIO_LEDLNK], OUTPUT);
digitalWrite(pin[GPIO_LEDLNK], ledlnk_inverted);
}
if (pin[GPIO_BUZZER] < 99) {
pinMode(pin[GPIO_BUZZER], OUTPUT);
digitalWrite(pin[GPIO_BUZZER], buzzer_inverted); // Buzzer Off
}
ButtonInit();
SwitchInit();

View File

@ -184,6 +184,8 @@ enum UserSelectablePins {
GPIO_LEDLNK, // Link led
GPIO_LEDLNK_INV, // Inverted link led
GPIO_ARIRFSEL, // Arilux RF Receive input selected
GPIO_BUZZER, // Buzzer
GPIO_BUZZER_INV, // Inverted buzzer
GPIO_SENSOR_END };
// Programmer selectable GPIO functionality
@ -251,6 +253,7 @@ const char kSensorNames[] PROGMEM =
D_SENSOR_ADE7953_IRQ "|"
D_SENSOR_LED_LINK "|" D_SENSOR_LED_LINK "i|"
D_SENSOR_ARIRFSEL "|"
D_SENSOR_BUZZER "|" D_SENSOR_BUZZER "i|"
;
// User selectable ADC0 functionality
@ -353,6 +356,7 @@ enum SupportedModules {
WAGA,
SYF05,
SONOFF_L1,
SONOFF_IFAN03,
MAXMODULE};
#define USER_MODULE 255
@ -476,6 +480,8 @@ const uint8_t kGpioNiceList[] PROGMEM = {
GPIO_CNTR4,
GPIO_CNTR4_NP,
#endif
GPIO_BUZZER, // Buzzer
GPIO_BUZZER_INV, // Inverted buzzer
GPIO_TXD, // Serial interface
GPIO_RXD, // Serial interface
#ifdef USE_I2C
@ -664,6 +670,7 @@ const uint8_t kModuleNiceList[] PROGMEM = {
SLAMPHER,
SONOFF_SC, // Sonoff Environmemtal Sensor
SONOFF_IFAN02, // Sonoff Fan
SONOFF_IFAN03,
SONOFF_BRIDGE, // Sonoff Bridge
SONOFF_SV, // Sonoff Development Devices
SONOFF_DEV,
@ -2002,6 +2009,25 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
GPIO_USER,
GPIO_USER,
0
},
{ "Sonoff iFan03", // Sonoff iFan03 (ESP8285)
GPIO_KEY1, // GPIO00 WIFI_KEY0 Virtual button 1 as feedback from RC
GPIO_TXD, // GPIO01 ESP_TXD Serial RXD and Optional sensor
0, // GPIO02 ESP_LOG
GPIO_RXD, // GPIO03 ESP_RXD Serial TXD and Optional sensor
0, // GPIO04 DEBUG_RX
0, // GPIO05 DEBUG_TX
// GPIO06 (SD_CLK Flash)
// GPIO07 (SD_DATA0 Flash QIO/DIO/DOUT)
// GPIO08 (SD_DATA1 Flash QIO/DIO/DOUT)
GPIO_REL1_INV, // GPIO09 WIFI_O0 Relay 1 (0 = Off, 1 = On) controlling the light
GPIO_BUZZER_INV, // GPIO10 WIFI_O4 Buzzer (0 = Off, 1 = On)
// GPIO11 (SD_CMD Flash)
GPIO_REL3, // GPIO12 WIFI_O2 Relay 3 (0 = Off, 1 = On) controlling the fan
GPIO_LED1_INV, // GPIO13 WIFI_CHK Blue Led on PCA (0 = On, 1 = Off) - Link and Power status
GPIO_REL2, // GPIO14 WIFI_O1 Relay 2 (0 = Off, 1 = On) controlling the fan
GPIO_REL4, // GPIO15 WIFI_O3 Relay 4 (0 = Off, 1 = On) controlling the fan
0, 0
}
};

View File

@ -20,6 +20,6 @@
#ifndef _SONOFF_VERSION_H_
#define _SONOFF_VERSION_H_
const uint32_t VERSION = 0x06060001;
const uint32_t VERSION = 0x06060002;
#endif // _SONOFF_VERSION_H_

View File

@ -942,9 +942,9 @@ void HandleRoot(void)
#endif
WSContentSend_P(HTTP_TABLE100);
WSContentSend_P(PSTR("<tr>"));
if (SONOFF_IFAN02 == my_module_type) {
if (IsModuleIfan()) {
WSContentSend_P(HTTP_DEVICE_CONTROL, 36, 1, D_BUTTON_TOGGLE, "");
for (uint32_t i = 0; i < MAX_FAN_SPEED; i++) {
for (uint32_t i = 0; i < MaxFanspeed(); i++) {
snprintf_P(stemp, sizeof(stemp), PSTR("%d"), i);
WSContentSend_P(HTTP_DEVICE_CONTROL, 16, i +2, stemp, "");
}
@ -1007,7 +1007,7 @@ bool HandleRootStatusRefresh(void)
if (strlen(tmp)) {
ShowWebSource(SRC_WEBGUI);
uint8_t device = atoi(tmp);
if (SONOFF_IFAN02 == my_module_type) {
if (IsModuleIfan()) {
if (device < 2) {
ExecuteCommandPower(1, POWER_TOGGLE, SRC_IGNORE);
} else {
@ -1042,7 +1042,7 @@ bool HandleRootStatusRefresh(void)
if (devices_present) {
WSContentSend_P(PSTR("{t}<tr>"));
uint8_t fsize = (devices_present < 5) ? 70 - (devices_present * 8) : 32;
if (SONOFF_IFAN02 == my_module_type) {
if (IsModuleIfan()) {
WSContentSend_P(HTTP_DEVICE_STATE, 36, (bitRead(power, 0)) ? "bold" : "normal", 54, GetStateText(bitRead(power, 0)));
uint8_t fanspeed = GetFanspeed();
snprintf_P(svalue, sizeof(svalue), PSTR("%d"), fanspeed);
@ -1573,7 +1573,7 @@ void HandleOtherConfiguration(void)
WSContentSend_P(HTTP_FORM_OTHER, stemp, (USER_MODULE == Settings.module) ? " checked disabled" : "", (Settings.flag.mqtt_enabled) ? " checked" : "");
uint8_t maxfn = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : (!devices_present) ? 1 : devices_present;
if (SONOFF_IFAN02 == my_module_type) { maxfn = 1; }
if (IsModuleIfan()) { maxfn = 1; }
for (uint32_t i = 0; i < maxfn; i++) {
snprintf_P(stemp, sizeof(stemp), PSTR("%d"), i +1);
WSContentSend_P(PSTR("<b>" D_FRIENDLY_NAME " %d</b> (" FRIENDLY_NAME "%s)<br><input id='a%d' placeholder='" FRIENDLY_NAME "%s' value='%s'><p></p>"),
@ -1752,7 +1752,7 @@ void HandleInformation(void)
WSContentSend_P(PSTR("}1" D_BOOT_COUNT "}2%d"), Settings.bootcount);
WSContentSend_P(PSTR("}1" D_RESTART_REASON "}2%s"), GetResetReason().c_str());
uint8_t maxfn = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : devices_present;
if (SONOFF_IFAN02 == my_module_type) { maxfn = 1; }
if (IsModuleIfan()) { maxfn = 1; }
for (uint32_t i = 0; i < maxfn; i++) {
WSContentSend_P(PSTR("}1" D_FRIENDLY_NAME " %d}2%s"), i +1, Settings.friendlyname[i]);
}

View File

@ -309,8 +309,8 @@ void MqttPublishPowerState(uint8_t device)
if ((device < 1) || (device > devices_present)) { device = 1; }
if ((SONOFF_IFAN02 == my_module_type) && (device > 1)) {
if (GetFanspeed() < MAX_FAN_SPEED) { // 4 occurs when fanspeed is 3 and RC button 2 is pressed
if (IsModuleIfan() && (device > 1)) {
if (GetFanspeed() < MaxFanspeed()) { // 4 occurs when fanspeed is 3 and RC button 2 is pressed
#ifdef USE_DOMOTICZ
DomoticzUpdateFanState(); // RC Button feedback
#endif // USE_DOMOTICZ
@ -335,7 +335,7 @@ void MqttPublishAllPowerState()
{
for (uint32_t i = 1; i <= devices_present; i++) {
MqttPublishPowerState(i);
if (SONOFF_IFAN02 == my_module_type) { break; } // Report status of light relay only
if (IsModuleIfan()) { break; } // Report status of light relay only
}
}

View File

@ -101,7 +101,7 @@ void MqttPublishDomoticzPowerState(uint8_t device)
if (Settings.flag.mqtt_enabled) {
if ((device < 1) || (device > devices_present)) { device = 1; }
if (Settings.domoticz_relay_idx[device -1]) {
if ((SONOFF_IFAN02 == my_module_type) && (device > 1)) {
if (IsModuleIfan() && (device > 1)) {
// Fan handled by MqttPublishDomoticzFanState
} else {
char svalue[8]; // Dimmer value
@ -129,7 +129,7 @@ void DomoticzMqttUpdate(void)
if (domoticz_update_timer <= 0) {
domoticz_update_timer = Settings.domoticz_update_timer;
for (uint32_t i = 1; i <= devices_present; i++) {
if ((SONOFF_IFAN02 == my_module_type) && (i > 1)) {
if (IsModuleIfan() && (i > 1)) {
MqttPublishDomoticzFanState();
break;
} else {
@ -214,7 +214,7 @@ bool DomoticzMqttData(void)
if (idx == Settings.domoticz_relay_idx[i]) {
bool iscolordimmer = strcmp_P(domoticz["dtype"],PSTR("Color Switch")) == 0;
snprintf_P(stemp1, sizeof(stemp1), PSTR("%d"), i +1);
if ((SONOFF_IFAN02 == my_module_type) && (1 == i)) { // Idx 2 is fanspeed
if (IsModuleIfan() && (1 == i)) { // Idx 2 is fanspeed
uint8_t svalue = 0;
if (domoticz.containsKey("svalue1")) {
svalue = domoticz["svalue1"];
@ -472,7 +472,7 @@ void HandleDomoticzConfiguration(void)
WSContentSend_P(HTTP_FORM_DOMOTICZ_SWITCH,
i +1, i, Settings.domoticz_switch_idx[i]);
}
if ((SONOFF_IFAN02 == my_module_type) && (1 == i)) { break; }
if (IsModuleIfan() && (1 == i)) { break; }
}
for (uint32_t i = 0; i < DZ_MAX_SENSORS; i++) {
WSContentSend_P(HTTP_FORM_DOMOTICZ_SENSOR,

View File

@ -0,0 +1,329 @@
/*
xdrv_22_sonoff_ifan.ino - sonoff iFan02 and iFan03 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/>.
*/
/*********************************************************************************************\
Sonoff iFan02 and iFan03
\*********************************************************************************************/
#define XDRV_22 22
const uint8_t MAX_FAN_SPEED = 4; // Max number of iFan02 fan speeds (0 .. 3)
const uint8_t kIFan02Speed[MAX_FAN_SPEED][3] = {{6,6,6}, {7,6,6}, {7,7,6}, {7,6,7}}; // Do not use PROGMEM as it fails
const uint8_t kIFan03Speed[MAX_FAN_SPEED +2][3] = {{6,6,6}, {7,6,6}, {7,7,6}, {6,6,7}, {7,6,7}, {6,7,7}}; // Do not use PROGMEM as it fails
uint8_t ifan_fanspeed_timer = 0;
uint8_t ifan_fanspeed_goal = 0;
bool ifan_receive_flag = false;
/*********************************************************************************************/
bool IsModuleIfan()
{
return ((SONOFF_IFAN02 == my_module_type) || (SONOFF_IFAN03 == my_module_type));
}
uint8_t MaxFanspeed(void)
{
return MAX_FAN_SPEED;
}
uint8_t GetFanspeed(void)
{
if (ifan_fanspeed_timer) {
return ifan_fanspeed_goal;
} else {
uint8_t fanspeed = 0;
// if (SONOFF_IFAN02 == my_module_type) {
/* Fanspeed is controlled by relay 2, 3 and 4 as in Sonoff 4CH.
000x = 0
001x = 1
011x = 2
101x = 3 (ifan02) or 100x = 3 (ifan03)
*/
fanspeed = (uint8_t)(power &0xF) >> 1;
if (fanspeed) { fanspeed = (fanspeed >> 1) +1; } // 0, 1, 2, 3
// }
return fanspeed;
}
}
void SetFanspeed(uint8_t fanspeed)
{
if (SONOFF_IFAN02 == my_module_type) {
for (uint32_t i = 0; i < 3; i++) {
uint8_t state = kIFan02Speed[fanspeed][i];
ExecuteCommandPower(i +2, state, SRC_IGNORE); // Use relay 2, 3 and 4
}
#ifdef USE_DOMOTICZ
DomoticzUpdateFanState(); // Command FanSpeed feedback
#endif // USE_DOMOTICZ
}
else if (SONOFF_IFAN03 == my_module_type) {
SonoffIFanSetFanspeed(fanspeed, true);
}
}
/*********************************************************************************************/
void SonoffIFanSetFanspeed(uint8_t fanspeed, bool sequence)
{
ifan_fanspeed_timer = 0; // Stop any sequence
ifan_fanspeed_goal = fanspeed;
uint8_t fanspeed_now = GetFanspeed();
if (fanspeed == fanspeed_now) { return; }
// Change to lookup table
if (sequence) {
if (0 == fanspeed_now) {
switch (fanspeed) {
case 1:
case 3:
fanspeed = 2;
ifan_fanspeed_timer = 20;
break;
}
}
if (1 == fanspeed_now) {
switch (fanspeed) {
case 3:
fanspeed = 4;
ifan_fanspeed_timer = 2;
break;
}
}
if (2 == fanspeed_now) {
switch (fanspeed) {
case 0:
fanspeed = 1;
ifan_fanspeed_timer = 2;
break;
case 3:
fanspeed = 5;
ifan_fanspeed_timer = 2;
break;
}
}
if (3 == fanspeed_now) {
switch (fanspeed) {
case 0:
case 1:
fanspeed = 4;
ifan_fanspeed_timer = 2;
break;
case 2:
fanspeed = 5;
ifan_fanspeed_timer = 2;
break;
}
}
// if (fanspeed == ifan_fanspeed_goal) {
// sequence = false;
// }
}
for (uint32_t i = 0; i < 3; i++) {
uint8_t state = kIFan03Speed[fanspeed][i];
ExecuteCommandPower(i +2, state, SRC_IGNORE); // Use relay 2, 3 and 4
}
#ifdef USE_DOMOTICZ
if (sequence) { DomoticzUpdateFanState(); } // Command FanSpeed feedback
#endif // USE_DOMOTICZ
}
/*********************************************************************************************/
void SonoffIfanSendAck()
{
// AA 55 01 04 00 00 05
serial_in_buffer[5] = 0;
uint8_t crc = 0;
for (uint32_t i = 2; i < 5; i++) {
crc += serial_in_buffer[i];
}
serial_in_buffer[6] = crc;
for (uint32_t i = 0; i < 7; i++) {
Serial.write(serial_in_buffer[i]);
}
}
void SonoffIfanReceived(void)
{
char svalue[32];
uint8_t mode = serial_in_buffer[3];
uint8_t action = serial_in_buffer[6];
if (4 == mode) {
if (action < 4) {
// AA 55 01 04 00 01 00 06 - Fan 0
// AA 55 01 04 00 01 01 07 - Fan 1
// AA 55 01 04 00 01 02 08 - Fan 2
// AA 55 01 04 00 01 03 09 - Fan 3
if (action != GetFanspeed()) {
snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_FANSPEED " %d"), action);
ExecuteCommand(svalue, SRC_REMOTE);
buzzer_count = 2; // Beep once
}
} else {
// AA 55 01 04 00 01 04 0A - Light
ExecuteCommandPower(1, POWER_TOGGLE, SRC_REMOTE);
}
}
if (6 == mode) {
// AA 55 01 06 00 01 01 09 - Buzzer
Settings.flag3.buzzer_enable = !Settings.flag3.buzzer_enable; // SetOption67
}
if (7 == mode) {
// AA 55 01 07 00 01 01 0A - Rf long press - forget RF codes
buzzer_count = 6; // Beep three times
}
SonoffIfanSendAck();
}
bool SonoffIfanSerialInput(void)
{
if (SONOFF_IFAN03 == my_module_type) {
if (0xAA == serial_in_byte) { // 0xAA - Start of text
serial_in_byte_counter = 0;
ifan_receive_flag = true;
}
if (ifan_receive_flag) {
// AA 55 01 01 00 01 01 04 - Wifi long press - start wifi setup
// AA 55 01 01 00 01 02 05 - Rf and Wifi short press
// AA 55 01 04 00 01 00 06 - Fan 0
// AA 55 01 04 00 01 01 07 - Fan 1
// AA 55 01 04 00 01 02 08 - Fan 2
// AA 55 01 04 00 01 03 09 - Fan 3
// AA 55 01 04 00 01 04 0A - Light
// AA 55 01 06 00 01 01 09 - Buzzer
// AA 55 01 07 00 01 01 0A - Rf long press - forget RF codes
serial_in_buffer[serial_in_byte_counter++] = serial_in_byte;
if (serial_in_byte_counter == 8) {
AddLogSerial(LOG_LEVEL_DEBUG);
uint8_t crc = 0;
for (uint32_t i = 2; i < 7; i++) {
crc += serial_in_buffer[i];
}
if (crc == serial_in_buffer[7]) {
SonoffIfanReceived();
ifan_receive_flag = false;
return true;
}
}
serial_in_byte = 0;
}
return false;
}
}
/*********************************************************************************************\
* Commands
\*********************************************************************************************/
enum SonoffIfanCommands { CMND_FANSPEED };
const char kSonoffIfanCommands[] PROGMEM = D_CMND_FANSPEED;
bool SonoffIfanCommand(void)
{
char command [CMDSZ];
bool serviced = true;
int command_code = GetCommandCode(command, sizeof(command), XdrvMailbox.topic, kSonoffIfanCommands);
if (-1 == command_code) {
serviced = false; // Unknown command
}
else if (CMND_FANSPEED == command_code) {
if (XdrvMailbox.data_len > 0) {
if ('-' == XdrvMailbox.data[0]) {
XdrvMailbox.payload = (int16_t)GetFanspeed() -1;
if (XdrvMailbox.payload < 0) { XdrvMailbox.payload = MAX_FAN_SPEED -1; }
}
else if ('+' == XdrvMailbox.data[0]) {
XdrvMailbox.payload = GetFanspeed() +1;
if (XdrvMailbox.payload > MAX_FAN_SPEED -1) { XdrvMailbox.payload = 0; }
}
}
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < MAX_FAN_SPEED)) {
SetFanspeed(XdrvMailbox.payload);
}
Response_P(S_JSON_COMMAND_NVALUE, command, GetFanspeed());
} else serviced = false; // Unknown command
return serviced;
}
/*********************************************************************************************/
bool SonoffIfanInit(void)
{
if (SONOFF_IFAN03 == my_module_type) {
Settings.flag.mqtt_serial = 0;
baudrate = 9600;
SetSeriallog(LOG_LEVEL_NONE);
}
return false; // Continue init chain
}
void SonoffIfanUpdate(void)
{
if (SONOFF_IFAN03 == my_module_type) {
if (ifan_fanspeed_timer) {
ifan_fanspeed_timer--;
if (!ifan_fanspeed_timer) {
SonoffIFanSetFanspeed(ifan_fanspeed_goal, false);
}
}
}
}
/*********************************************************************************************\
* Interface
\*********************************************************************************************/
bool Xdrv22(uint8_t function)
{
bool result = false;
if (IsModuleIfan()) {
switch (function) {
case FUNC_MODULE_INIT:
result = SonoffIfanInit();
break;
case FUNC_EVERY_250_MSECOND:
SonoffIfanUpdate();
break;
case FUNC_COMMAND:
result = SonoffIfanCommand();
break;
case FUNC_SERIAL:
result = SonoffIfanSerialInput();
break;
}
}
return result;
}