Add Sonoff iFan02 support

Add support for Sonoff iFan02 as module 44 introducing command FanSpeed
0..3 (#2839)
This commit is contained in:
Theo Arends 2018-07-01 15:06:44 +02:00
parent 8b7645b05d
commit d3d876f030
5 changed files with 97 additions and 49 deletions

View File

@ -1,5 +1,5 @@
/* 6.0.0b
* Add initial support for Sonoff iFan02 - Module 44 - Command FanSpeed 0..3 - Webpage will only allow Toggle1 (#2839)
* Add support for Sonoff iFan02 as module 44 introducing command FanSpeed 0..3 (#2839)
* Add support for Sonoff S26 Smart Socket (#2808)
* Add command SetOption30 to enforce Hass discovery as light group (#1784)
* Add decimal values support for commands ADD, SUB, MULT and SCALE (#3083, #3089)

View File

@ -96,9 +96,7 @@ const char kTasmotaCommands[] PROGMEM =
D_CMND_TELEPERIOD "|" D_CMND_RESTART "|" D_CMND_RESET "|" D_CMND_TIMEZONE "|" D_CMND_TIMESTD "|" D_CMND_TIMEDST "|" D_CMND_ALTITUDE "|" D_CMND_LEDPOWER "|" D_CMND_LEDSTATE "|"
D_CMND_I2CSCAN "|" D_CMND_SERIALSEND "|" D_CMND_BAUDRATE "|" D_CMND_SERIALDELIMITER;
//const uint8_t kIFan02Speed[4][3] = {{0,0,0}, {1,0,0}, {1,1,0}, {1,0,1}};
const uint8_t kIFan02Speed[4][3] = {{6,6,6}, {7,6,6}, {7,7,6}, {7,6,7}};
//const uint8_t kIFan02Speed[4][3] = {{16,16,16}, {17,16,16}, {17,17,16}, {17,16,17}};
// Global variables
unsigned long feature_drv1; // Compiled driver feature map
@ -363,6 +361,23 @@ void SetLedPower(uint8_t state)
digitalWrite(pin[GPIO_LED1], (bitRead(led_inverted, 0)) ? !state : state);
}
uint8_t GetFanspeed()
{
uint8_t fanspeed = 0;
// if (SONOFF_IFAN02 == Settings.module) {
/* 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 MqttDataHandler(char* topic, byte* data, unsigned int data_len)
@ -507,17 +522,14 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len)
return;
}
else if ((CMND_FANSPEED == command_code) && (SONOFF_IFAN02 == Settings.module)) {
uint8_t fanspeed = (uint8_t)(power &0xF) >> 1;
if (fanspeed) { fanspeed = (fanspeed >> 1) +1; }
if ((payload >= 0) && (payload <= 3) && (payload != fanspeed)) {
fanspeed = payload;
if ((payload >= 0) && (payload <= 3) && (payload != GetFanspeed())) {
for (byte i = 0; i < 3; i++) {
uint8_t state = kIFan02Speed[fanspeed][i];
// uint8_t state = pgm_read_byte(kIFan02Speed +(fanspeed *3) +i);
ExecuteCommandPower(i +2, state, SRC_IGNORE);
uint8_t state = kIFan02Speed[payload][i];
// uint8_t state = pgm_read_byte(kIFan02Speed +(payload *3) +i);
ExecuteCommandPower(i +2, state, SRC_IGNORE); // Use relay 2, 3 and 4
}
}
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, fanspeed);
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, GetFanspeed());
}
else if (CMND_STATUS == command_code) {
if ((payload < 0) || (payload > MAX_STATUS)) payload = 99;
@ -1207,20 +1219,14 @@ void ExecuteCommandPower(byte device, byte state, int source)
// ShowSource(source);
/*
if (SONOFF_IFAN02 == Settings.module) {
if (state > 15) { // Only allow Fanspeed control over relay 2..4
state -= 10;
blink_mask &= 1;
Settings.flag.interlock = 0;
Settings.pulse_timer[1] = 0;
Settings.pulse_timer[2] = 0;
Settings.pulse_timer[3] = 0;
} else {
device = 1; // Only allow user control over light
}
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
Settings.pulse_timer[2] = 0;
Settings.pulse_timer[3] = 0;
}
*/
uint8_t publish_power = 1;
if ((POWER_OFF_NO_STATE == state) || (POWER_ON_NO_STATE == state)) {
state &= 1;
@ -1447,6 +1453,10 @@ void MqttShowState()
LightState(1);
} else {
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"%s\":\"%s\""), mqtt_data, GetPowerDevice(stemp1, i +1, sizeof(stemp1), Settings.flag.device_index_enable), GetStateText(bitRead(power, i)));
if (SONOFF_IFAN02 == Settings.module) {
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"" D_CMND_FANSPEED "\":%d"), mqtt_data, GetFanspeed());
break;
}
}
}
@ -1521,6 +1531,7 @@ void PerformEverySecond()
if (!status_update_timer) {
for (byte i = 1; i <= devices_present; i++) {
MqttPublishPowerState(i);
if (SONOFF_IFAN02 == Settings.module) { break; } // Only report status of light relay
}
}
}

View File

@ -838,20 +838,20 @@ const mytmplt kModules[MAXMODULE] PROGMEM = {
0, 0, 0, 0
},
{ "Sonoff iFan02", // Sonoff iFan02 (ESP8285)
GPIO_KEY1, // GPIO00 Virtual button 1
GPIO_KEY1, // GPIO00 Virtual button 1 as feedback from RC
GPIO_USER, // GPIO01 Serial RXD and Optional sensor
0, // GPIO02 Optional sensor
GPIO_USER, // GPIO03 Serial TXD and Optional sensor
GPIO_REL3, // GPIO04 Relay 3 (0 = Off, 1 = On)
GPIO_REL2, // GPIO05 Relay 2 (0 = Off, 1 = On)
GPIO_REL3, // GPIO04 Relay 3 (0 = Off, 1 = On) controlling the fan
GPIO_REL2, // GPIO05 Relay 2 (0 = Off, 1 = On) controlling the fan
0, 0, 0, // Flash connection
GPIO_KEY2, // GPIO09 Virtual button 2
GPIO_KEY3, // GPIO10 Virtual button 3
GPIO_KEY2, // GPIO09 Virtual button 2 as feedback from RC
GPIO_KEY3, // GPIO10 Virtual button 3 as feedback from RC
0, // Flash connection
GPIO_REL1, // GPIO12 Relay 1 (0 = Off, 1 = On)
GPIO_REL1, // GPIO12 Relay 1 (0 = Off, 1 = On) controlling the light
GPIO_LED1_INV, // GPIO13 Blue Led on PCA (0 = On, 1 = Off)
GPIO_KEY4, // GPIO14 Virtual button 4
GPIO_REL4, // GPIO15 Relay 4 (0 = Off, 1 = On)
GPIO_KEY4, // GPIO14 Virtual button 4 as feedback from RC
GPIO_REL4, // GPIO15 Relay 4 (0 = Off, 1 = On) controlling the fan
0, 0
}
};

View File

@ -283,15 +283,24 @@ void MqttPublishPowerState(byte device)
char scommand[33];
if ((device < 1) || (device > devices_present)) { device = 1; }
GetPowerDevice(scommand, device, sizeof(scommand), Settings.flag.device_index_enable);
GetTopic_P(stopic, STAT, mqtt_topic, (Settings.flag.mqtt_response) ? scommand : S_RSLT_RESULT);
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, scommand, GetStateText(bitRead(power, device -1)));
MqttPublish(stopic);
if ((SONOFF_IFAN02 == Settings.module) && (device > 1)) { // Do not report status of fan relays
if (GetFanspeed() < 4) {
snprintf_P(scommand, sizeof(scommand), PSTR(D_CMND_FANSPEED));
GetTopic_P(stopic, STAT, mqtt_topic, (Settings.flag.mqtt_response) ? scommand : S_RSLT_RESULT);
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, scommand, GetFanspeed());
MqttPublish(stopic);
}
} else {
GetPowerDevice(scommand, device, sizeof(scommand), Settings.flag.device_index_enable);
GetTopic_P(stopic, STAT, mqtt_topic, (Settings.flag.mqtt_response) ? scommand : S_RSLT_RESULT);
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, scommand, GetStateText(bitRead(power, device -1)));
MqttPublish(stopic);
GetTopic_P(stopic, STAT, mqtt_topic, scommand);
snprintf_P(mqtt_data, sizeof(mqtt_data), GetStateText(bitRead(power, device -1)));
MqttPublish(stopic, Settings.flag.mqtt_power_retain);
GetTopic_P(stopic, STAT, mqtt_topic, scommand);
snprintf_P(mqtt_data, sizeof(mqtt_data), GetStateText(bitRead(power, device -1)));
MqttPublish(stopic, Settings.flag.mqtt_power_retain);
}
}
void MqttPublishPowerBlinkState(byte device)

View File

@ -313,6 +313,9 @@ const char HTTP_END[] PROGMEM =
"</body>"
"</html>";
const char HTTP_DEVICE_CONTROL[] PROGMEM = "<td style='width:%d%%'><button onclick='la(\"?o=%d\");'>%s%s</button></td>";
const char HTTP_DEVICE_STATE[] PROGMEM = "%s<td style='width:%d{c}%s;font-size:%dpx'>%s</div></td>"; // {c} = %'><div style='text-align:center;font-weight:
const char HDR_CTYPE_PLAIN[] PROGMEM = "text/plain";
const char HDR_CTYPE_HTML[] PROGMEM = "text/html";
const char HDR_CTYPE_XML[] PROGMEM = "text/xml";
@ -553,11 +556,21 @@ void HandleRoot()
}
page += FPSTR(HTTP_TABLE100);
page += F("<tr>");
for (byte idx = 1; idx <= devices_present; idx++) {
snprintf_P(stemp, sizeof(stemp), PSTR(" %d"), idx);
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("<td style='width:%d%'><button onclick='la(\"?o=%d\");'>%s%s</button></td>"),
100 / devices_present, idx, (devices_present < 5) ? D_BUTTON_TOGGLE : "", (devices_present > 1) ? stemp : "");
if (SONOFF_IFAN02 == Settings.module) {
snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_DEVICE_CONTROL, 36, 1, D_BUTTON_TOGGLE, "");
page += mqtt_data;
for (byte i = 0; i < 4; i++) {
snprintf_P(stemp, sizeof(stemp), PSTR("%d"), i);
snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_DEVICE_CONTROL, 16, i +2, stemp, "");
page += mqtt_data;
}
} else {
for (byte idx = 1; idx <= devices_present; idx++) {
snprintf_P(stemp, sizeof(stemp), PSTR(" %d"), idx);
snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_DEVICE_CONTROL,
100 / devices_present, idx, (devices_present < 5) ? D_BUTTON_TOGGLE : "", (devices_present > 1) ? stemp : "");
page += mqtt_data;
}
}
page += F("</tr></table>");
}
@ -592,10 +605,16 @@ void HandleAjaxStatusRefresh()
WebGetArg("o", tmp, sizeof(tmp));
if (strlen(tmp)) {
ShowWebSource(SRC_WEBGUI);
if (SONOFF_IFAN02 == Settings.module) { // QandD
ExecuteCommandPower(1, POWER_TOGGLE, SRC_IGNORE);
uint8_t device = atoi(tmp);
if (SONOFF_IFAN02 == Settings.module) {
if (device < 2) {
ExecuteCommandPower(1, POWER_TOGGLE, SRC_IGNORE);
} else {
snprintf_P(svalue, sizeof(svalue), PSTR(D_CMND_FANSPEED " %d"), device -2);
ExecuteCommand(svalue, SRC_WEBGUI);
}
} else {
ExecuteCommandPower(atoi(tmp), POWER_TOGGLE, SRC_IGNORE);
ExecuteCommandPower(device, POWER_TOGGLE, SRC_IGNORE);
}
}
WebGetArg("d", tmp, sizeof(tmp));
@ -627,10 +646,19 @@ void HandleAjaxStatusRefresh()
if (devices_present) {
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s{t}<tr>"), mqtt_data);
uint8_t fsize = (devices_present < 5) ? 70 - (devices_present * 8) : 32;
for (byte idx = 1; idx <= devices_present; idx++) {
snprintf_P(svalue, sizeof(svalue), PSTR("%d"), bitRead(power, idx -1));
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s<td style='width:%d{c}%s;font-size:%dpx'>%s</div></td>"), // {c} = %'><div style='text-align:center;font-weight:
mqtt_data, 100 / devices_present, (bitRead(power, idx -1)) ? "bold" : "normal", fsize, (devices_present < 5) ? GetStateText(bitRead(power, idx -1)) : svalue);
if (SONOFF_IFAN02 == Settings.module) {
snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_DEVICE_STATE,
mqtt_data, 36, (bitRead(power, 0)) ? "bold" : "normal", 54, GetStateText(bitRead(power, 0)));
uint8_t fanspeed = GetFanspeed();
snprintf_P(svalue, sizeof(svalue), PSTR("%d"), fanspeed);
snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_DEVICE_STATE,
mqtt_data, 64, (fanspeed) ? "bold" : "normal", 54, (fanspeed) ? svalue : GetStateText(0));
} else {
for (byte idx = 1; idx <= devices_present; idx++) {
snprintf_P(svalue, sizeof(svalue), PSTR("%d"), bitRead(power, idx -1));
snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_DEVICE_STATE,
mqtt_data, 100 / devices_present, (bitRead(power, idx -1)) ? "bold" : "normal", fsize, (devices_present < 5) ? GetStateText(bitRead(power, idx -1)) : svalue);
}
}
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s</tr></table>"), mqtt_data);
}