mirror of https://github.com/arendst/Tasmota.git
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:
parent
3c98acea65
commit
49022d0320
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
\*********************************************************************************************/
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue