mirror of
https://github.com/arendst/Tasmota.git
synced 2025-04-22 05:47:17 +00:00

5.9.0 20171030 * Rewrite code (partly) using Google C++ Style Guide (https://google.github.io/styleguide/cppguide.html) * Rewrite code by using command lookup tables and javascript (client side) web page expansions * Change HTML/CSS to enable nicer form field entry * Change default PWM assignments for H801 RGB(CW) led controller to support optional Color/Dimmer control * GPIO04 (W2) from GPIO_PWM2 to GPIO_USER to be user configurable for GPIO_PWM5 (second White - Warm if W1 is Cold) * GPIO12 (Blue) GPIO_PWM3 no change * GPIO13 (Green) from GPIO_PWM4 to GPIO_PWM2 * GPIO14 (W1) from GPIO_PWM1 to GPIO_USER to be user configurable for GPIO_PWM4 (first White - Cold or Warm) * GPIO15 (Red) from GPIO_PWM5 to GPIO_PWM1 * Change default PWM assignments for MagicHome RGB(W) led controller to support optional Color/Dimmer control * GPIO05 (Green) from GPIO_PWM4 to GPIO_PWM2 * GPIO12 (Blue) from GPIO_PWM5 to GPIO_PWM3 * GPIO13 (White) GPIO_USER to be user configurable for GPIO_PWM4 (White - Cold or Warm) * GPIO14 (Red) from GPIO_PWM3 to GPIO_PWM1 * Change default PWM assignment for Witty Cloud to support optional Color/Dimmer control (#976) * GPIO12 (Green) from GPIO_PWM4 to GPIO_PWM2 * GPIO13 (Blue) from GPIO_PWM5 to GPIO_PWM3 * GPIO15 (Red) from GPIO_PWM3 to GPIO_PWM1 * Change when another module is selected now all GPIO user configuration is removed * Change command name IRRemote to IRSend (#956) * Remove Arduino IDE version too low warning as it interferes with platformio.ini platform = espressif8266_stage * Fix command FullTopic entry when using serial or console interface * Fix possible UDP syslog blocking * Fix minimum TelePeriod of 10 seconds set by web page * Fix command GPIOx JSON response (#897) * Fix inverted relay power on state (#909) * Fix compile error when DOMOTICZ_UPDATE_TIMER is not defined (#930) * Fix alignment of web page items in some browsers (#935) * Fix setting all saved power settings to Off when SetOption0 (SaveState) = 0 (#955) * Fix timezone range from -12/12 to -13/13 (#968) * Fix Southern Hemisphere TIME_STD/TIME_DST (#968) * Fix TLS MQTT SSL fingerprint test (#970, #808) * Fix virtual relay status message used with Color/Dimmer control (#989) * Fix command IRSend and IRHvac case sensitive parameter regression introduced with version 5.8.0 (#993) * Fix pressure calculation for some BMP versions regression introduced with version 5.8.0i (#974) * Fix Domoticz Dimmer set to same level not powering on (#945) * Fix Blocked Loop when erasing large flash using command reset 2 (#1002) * Fix relay power control when light power control is also configured as regression from 5.8.0 (#1016) * Fix Mqtt server mDNS lookup only when MqttHost name is empty (#1026) * Add debug information to MQTT subscribe * Add translations to I2Cscan * Add translation to BH1750 unit lx * Add light scheme options (Color cycle Up, Down, Random) and moving WS2812 schemes up by 3 * Add Domoticz counter sensor to IrReceive representing Received IR Protocol and Data * Add option 0 to MqttHost to allow empty Mqtt host name * Add support for Arilux AL-LC01 RGB Led controller (#370) * Add esp8266 de-blocking to PubSubClient library (#790) * Add Domoticz sensors for Voltage and Current (#903) * Add platformio OTA upload support (#928, #934) * Add warning to webpage when USE_MINIMAL is selected (#929) * Add smoother movement of hour hand in WS2812 led clock (#936) * Add support for Magic Home RGBW and some Arilux Led controllers (#940) * Add command SetOption15 0 (default) for command PWM control or SetOption15 1 for commands Color/Dimmer control to PWM RGB(CW) leds (#941) * Add Domoticz counter sensor to Sonoff Bridge representing Received RF code (#943) * Add support for Luani HVIO board (https://luani.de/projekte/esp8266-hvio/) (#953) * Add PWM initialization after restart (#955) * Add IR Receiver support. Disable in user_config.h (#956) * Add support for inverted PWM (#960) * Add Sea level pressure calculation and Provide command Altitude (#974) * Add support for up to 8 relays (#995) * Add commands RfSync, RfLow, RfHigh, RfHost and RfCode to allow sending custom RF codes (#1001) * Add retain to ENERGY messages controlled by command SensorRetain (#1013) * Add commands Color2, Color3, Color4, Width2, Width3, Width4 and SetOption16 to set Ws2812 Clock parameters (#1019) * Add German language file (#1022) * Add support for connecting to MQTT brokers without userid and/or password (#1023) * Add support for esp8266 core v2.4.0-rc2 (#1024) * Add commands PwmRange 1,255..1023 and PwmFrequency 1,100..4000 (#1025) * Add Polish language file (#1044, #1047) * Add support for KMC 70011 Power Monitoring Smart Plug (#1045) * Add support for VEML6070 I2C Ultra Violet level sensor (#1053) * Add light turn Off Fade (#925) * Add IrSend command option Panasonic as IrSend {"Protocol":"Panasonic", "Bits":16388, "Data":<Panasonic data>} * where 16388 is 0x4004 hexadecimal (#1014) * Add retry counter to DHT11/21/22 sensors (#1082)
269 lines
10 KiB
C++
269 lines
10 KiB
C++
/*
|
|
xdrv_snfbridge.ino - sonoff RF bridge 433 support for Sonoff-Tasmota
|
|
|
|
Copyright (C) 2017 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 RF Bridge 433
|
|
\*********************************************************************************************/
|
|
|
|
#define SFB_TIME_AVOID_DUPLICATE 2000 // Milliseconds
|
|
|
|
enum SonoffBridgeCommands {
|
|
CMND_RFSYNC, CMND_RFLOW, CMND_RFHIGH, CMND_RFHOST, CMND_RFCODE, CMND_RFKEY };
|
|
const char kSonoffBridgeCommands[] PROGMEM =
|
|
D_CMND_RFSYNC "|" D_CMND_RFLOW "|" D_CMND_RFHIGH "|" D_CMND_RFHOST "|" D_CMND_RFCODE "|" D_CMND_RFKEY ;
|
|
|
|
uint8_t sonoff_bridge_receive_flag = 0;
|
|
uint8_t sonoff_bridge_learn_key = 1;
|
|
uint8_t sonoff_bridge_learn_active = 0;
|
|
uint32_t sonoff_bridge_last_received_id = 0;
|
|
uint32_t sonoff_bridge_last_send_code = 0;
|
|
unsigned long sonoff_bridge_last_time = 0;
|
|
|
|
void SonoffBridgeReceived()
|
|
{
|
|
uint16_t sync_time = 0;
|
|
uint16_t low_time = 0;
|
|
uint16_t high_time = 0;
|
|
uint32_t received_id = 0;
|
|
char svalue[90];
|
|
char rfkey[8];
|
|
|
|
svalue[0] = '\0';
|
|
for (byte i = 0; i < serial_in_byte_counter; i++) {
|
|
snprintf_P(svalue, sizeof(svalue), PSTR("%s%02X "), svalue, serial_in_buffer[i]);
|
|
}
|
|
snprintf_P(log_data, sizeof(log_data), PSTR(D_LOG_BRIDGE D_RECEIVED " %s"), svalue);
|
|
AddLog(LOG_LEVEL_DEBUG);
|
|
|
|
if (0xA2 == serial_in_buffer[0]) { // Learn timeout
|
|
sonoff_bridge_learn_active = 0;
|
|
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, D_CMND_RFKEY, sonoff_bridge_learn_key, D_LEARN_FAILED);
|
|
MqttPublishPrefixTopic_P(5, PSTR(D_CMND_RFKEY));
|
|
}
|
|
else if (0xA3 == serial_in_buffer[0]) { // Learned A3 20 F8 01 18 03 3E 2E 1A 22 55
|
|
sonoff_bridge_learn_active = 0;
|
|
low_time = serial_in_buffer[3] << 8 | serial_in_buffer[4]; // Low time in uSec
|
|
high_time = serial_in_buffer[5] << 8 | serial_in_buffer[6]; // High time in uSec
|
|
if (low_time && high_time) {
|
|
for (byte i = 0; i < 9; i++) {
|
|
Settings.rf_code[sonoff_bridge_learn_key][i] = serial_in_buffer[i +1];
|
|
}
|
|
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, D_CMND_RFKEY, sonoff_bridge_learn_key, D_LEARNED);
|
|
} else {
|
|
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, D_CMND_RFKEY, sonoff_bridge_learn_key, D_LEARN_FAILED);
|
|
}
|
|
MqttPublishPrefixTopic_P(5, PSTR(D_CMND_RFKEY));
|
|
}
|
|
else if (0xA4 == serial_in_buffer[0]) { // Received RF data A4 20 EE 01 18 03 3E 2E 1A 22 55
|
|
sync_time = serial_in_buffer[1] << 8 | serial_in_buffer[2]; // Sync time in uSec
|
|
low_time = serial_in_buffer[3] << 8 | serial_in_buffer[4]; // Low time in uSec
|
|
high_time = serial_in_buffer[5] << 8 | serial_in_buffer[6]; // High time in uSec
|
|
received_id = serial_in_buffer[7] << 16 | serial_in_buffer[8] << 8 | serial_in_buffer[9];
|
|
|
|
unsigned long now = millis();
|
|
if (!((received_id == sonoff_bridge_last_received_id) && (now - sonoff_bridge_last_time < SFB_TIME_AVOID_DUPLICATE))) {
|
|
sonoff_bridge_last_received_id = received_id;
|
|
sonoff_bridge_last_time = now;
|
|
strncpy_P(rfkey, PSTR("\"" D_NONE "\""), sizeof(rfkey));
|
|
for (byte i = 1; i <= 16; i++) {
|
|
if (Settings.rf_code[i][0]) {
|
|
uint32_t send_id = Settings.rf_code[i][6] << 16 | Settings.rf_code[i][7] << 8 | Settings.rf_code[i][8];
|
|
if (send_id == received_id) {
|
|
snprintf_P(rfkey, sizeof(rfkey), PSTR("%d"), i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_RFRECEIVED "\":{\"" D_SYNC "\":%d, \"" D_LOW "\":%d, \"" D_HIGH "\":%d, \"" D_DATA "\":\"%06X\", \"" D_CMND_RFKEY "\":%s}}"),
|
|
sync_time, low_time, high_time, received_id, rfkey);
|
|
MqttPublishPrefixTopic_P(6, PSTR(D_RFRECEIVED));
|
|
#ifdef USE_DOMOTICZ
|
|
DomoticzSensor(DZ_COUNT, received_id); // Send rid as Domoticz Counter value
|
|
#endif // USE_DOMOTICZ
|
|
}
|
|
}
|
|
}
|
|
|
|
boolean SonoffBridgeSerialInput()
|
|
{
|
|
if (sonoff_bridge_receive_flag) {
|
|
if (!((serial_in_byte_counter == 0) && (serial_in_byte == 0))) { // Skip leading 0
|
|
serial_in_buffer[serial_in_byte_counter++] = serial_in_byte;
|
|
if (0x55 == serial_in_byte) { // 0x55 - End of text
|
|
SonoffBridgeReceived();
|
|
sonoff_bridge_receive_flag = 0;
|
|
return 1;
|
|
}
|
|
}
|
|
serial_in_byte = 0;
|
|
}
|
|
if (0xAA == serial_in_byte) { // 0xAA - Start of text
|
|
serial_in_byte_counter = 0;
|
|
serial_in_byte = 0;
|
|
sonoff_bridge_receive_flag = 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void SonoffBridgeSendAck()
|
|
{
|
|
Serial.write(0xAA); // Start of Text
|
|
Serial.write(0xA0); // Acknowledge
|
|
Serial.write(0x55); // End of Text
|
|
}
|
|
|
|
void SonoffBridgeSendCode(uint32_t code)
|
|
{
|
|
Serial.write(0xAA); // Start of Text
|
|
Serial.write(0xA5); // Send following code
|
|
for (byte i = 0; i < 6; i++) {
|
|
Serial.write(Settings.rf_code[0][i]);
|
|
}
|
|
Serial.write((code >> 16) & 0xff);
|
|
Serial.write((code >> 8) & 0xff);
|
|
Serial.write(code & 0xff);
|
|
Serial.write(0x55); // End of Text
|
|
Serial.flush();
|
|
}
|
|
|
|
void SonoffBridgeSend(uint8_t idx, uint8_t key)
|
|
{
|
|
uint8_t code;
|
|
|
|
key--; // Support 1 to 16
|
|
Serial.write(0xAA); // Start of Text
|
|
Serial.write(0xA5); // Send following code
|
|
for (byte i = 0; i < 8; i++) {
|
|
Serial.write(Settings.rf_code[idx][i]);
|
|
}
|
|
if (0 == idx) {
|
|
code = (0x10 << (key >> 2)) | (1 << (key & 3)); // 11,12,14,18,21,22,24,28,41,42,44,48,81,82,84,88
|
|
} else {
|
|
code = Settings.rf_code[idx][8];
|
|
}
|
|
Serial.write(code);
|
|
Serial.write(0x55); // End of Text
|
|
Serial.flush();
|
|
#ifdef USE_DOMOTICZ
|
|
// uint32_t rid = Settings.rf_code[idx][6] << 16 | Settings.rf_code[idx][7] << 8 | code;
|
|
// DomoticzSensor(DZ_COUNT, rid); // Send rid as Domoticz Counter value
|
|
#endif // USE_DOMOTICZ
|
|
}
|
|
|
|
void SonoffBridgeLearn(uint8_t key)
|
|
{
|
|
sonoff_bridge_learn_key = key;
|
|
sonoff_bridge_learn_active = 1;
|
|
Serial.write(0xAA); // Start of Text
|
|
Serial.write(0xA1); // Start learning
|
|
Serial.write(0x55); // End of Text
|
|
}
|
|
|
|
/*********************************************************************************************\
|
|
* Commands
|
|
\*********************************************************************************************/
|
|
|
|
boolean SonoffBridgeCommand(char *type, uint16_t index, char *dataBuf, uint16_t data_len, int16_t payload)
|
|
{
|
|
char command [CMDSZ];
|
|
boolean serviced = true;
|
|
|
|
int command_code = GetCommandCode(command, sizeof(command), type, kSonoffBridgeCommands);
|
|
if ((command_code >= CMND_RFSYNC) && (command_code <= CMND_RFCODE)) { // RfSync, RfLow, RfHigh, RfHost and RfCode
|
|
char *p;
|
|
char stemp [10];
|
|
uint32_t code = 0;
|
|
uint8_t radix = 10;
|
|
|
|
uint8_t set_index = command_code *2;
|
|
|
|
if (dataBuf[0] == '#') {
|
|
dataBuf++;
|
|
data_len--;
|
|
radix = 16;
|
|
}
|
|
|
|
if (data_len) {
|
|
code = strtol(dataBuf, &p, radix);
|
|
if (code) {
|
|
if (CMND_RFCODE == command_code) {
|
|
sonoff_bridge_last_send_code = code;
|
|
SonoffBridgeSendCode(code);
|
|
} else {
|
|
if (1 == payload) {
|
|
code = pgm_read_byte(kDefaultRfCode + set_index) << 8 | pgm_read_byte(kDefaultRfCode + set_index +1);
|
|
}
|
|
uint8_t msb = code >> 8;
|
|
uint8_t lsb = code & 0xFF;
|
|
if ((code > 0) && (code < 0x7FFF) && (msb != 0x55) && (lsb != 0x55)) { // Check for End of Text codes
|
|
Settings.rf_code[0][set_index] = msb;
|
|
Settings.rf_code[0][set_index +1] = lsb;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (CMND_RFCODE == command_code) {
|
|
code = sonoff_bridge_last_send_code;
|
|
} else {
|
|
code = Settings.rf_code[0][set_index] << 8 | Settings.rf_code[0][set_index +1];
|
|
}
|
|
if (10 == radix) {
|
|
snprintf_P(stemp, sizeof(stemp), PSTR("%d"), code);
|
|
} else {
|
|
snprintf_P(stemp, sizeof(stemp), PSTR("\"#%X\""), code);
|
|
}
|
|
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_XVALUE, command, stemp);
|
|
}
|
|
else if ((CMND_RFKEY == command_code) && (index > 0) && (index <= 16)) {
|
|
if (!sonoff_bridge_learn_active) {
|
|
if (2 == payload) { // Learn RF data
|
|
SonoffBridgeLearn(index);
|
|
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, D_START_LEARNING);
|
|
}
|
|
else if (3 == payload) { // Unlearn RF data
|
|
Settings.rf_code[index][0] = 0;
|
|
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, D_SET_TO_DEFAULT);
|
|
}
|
|
else if (4 == payload) { // Save RF data provided by RFSync, RfLow, RfHigh and last RfCode
|
|
for (byte i = 0; i < 6; i++) {
|
|
Settings.rf_code[index][i] = Settings.rf_code[0][i];
|
|
}
|
|
Settings.rf_code[index][6] = (sonoff_bridge_last_send_code >> 16) & 0xff;
|
|
Settings.rf_code[index][7] = (sonoff_bridge_last_send_code >> 8) & 0xff;
|
|
Settings.rf_code[index][8] = sonoff_bridge_last_send_code & 0xff;
|
|
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, D_SAVED);
|
|
} else {
|
|
if ((1 == payload) || (0 == Settings.rf_code[index][0])) {
|
|
SonoffBridgeSend(0, index); // Send default RF data
|
|
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, D_DEFAULT_SENT);
|
|
} else {
|
|
SonoffBridgeSend(index, 0); // Send learned RF data
|
|
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, D_LEARNED_SENT);
|
|
}
|
|
}
|
|
} else {
|
|
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, sonoff_bridge_learn_key, D_LEARNING_ACTIVE);
|
|
}
|
|
} else {
|
|
serviced = false;
|
|
}
|
|
|
|
return serviced;
|
|
}
|