Merge pull request #2 from arendst/development

update fork from arendst/tasmota
This commit is contained in:
Mike 2018-09-25 14:19:45 +02:00 committed by GitHub
commit 37905a2d26
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 3220 additions and 326 deletions

View File

@ -4,6 +4,9 @@ about: Create a report to help us improve
---
**IMPORTANT NOTICE**
If you do not complete the template below it is likely that your issue will not be addressed. When providing information about your issue please be as extensive as possible so that it can be solved by as little as possible responses.
**Describe the bug**
_A clear and concise description of what the bug is._
@ -11,6 +14,7 @@ _A clear and concise description of what the bug is._
_Also, make sure these boxes are checked [x] before submitting your issue - Thank you!_
- [ ] _Searched the problem in issues and in the wiki_
- [ ] _Hardware used_ :
- [ ] _Development/Compiler/Upload tools used_ :
- [ ] _Provide the output of command_``status 0`` :
```
STATUS 0 OUTPUT HERE

View File

@ -4,12 +4,16 @@ about: Users Troubleshooting Help
---
**IMPORTANT NOTICE**
If you do not complete the template below it is likely that your issue will not be addressed. When providing information about your issue please be as extensive as possible so that it can be solved by as little as possible responses.
Make sure these boxes are checked [x] before submitting your issue - Thank you!
- [ ] Searched the problem in issues (https://github.com/arendst/Sonoff-Tasmota/issues)
- [ ] Searched the problem in the wiki (https://github.com/arendst/Sonoff-Tasmota/wiki/Troubleshooting)
- [ ] Searched the problem in the forum (https://groups.google.com/d/forum/sonoffusers)
- [ ] Searched the problem in the chat (https://discord.gg/Ks2Kzd4)
- [ ] Development/Compiler/Upload tools used :
- [ ] Hardware used :
- [ ] Provide the output of command ``status 0`` :
```

View File

@ -1,10 +1,40 @@
/* 6.2.1.3 20180907
/* 6.2.1.7 20180925
* Remove restart after ntpserver change and force NTP re-sync (#3890)
* Release full Shelly2 support
* Released tools/decode-config.py by Norbert Richter to decode configuration data. See file for information
*
* 6.2.1.6 20180922
* Removed commands PowerCal, VoltageCal and CurrentCal as more functionality is provided by commands PowerSet, VoltageSet and CurrentSet
* Allow decimals as input to commands PowerSet, VoltageSet and CurrentSet
* Add support for PCA9685 12bit 16pin hardware PWM driver (#3866)
* Add power value below 5W to Sonoff Pow R2 and S31 (#3745)
* Add force_update to Home Assistant discovery (#3873)
* Fix rule trigger POWER1#STATE execution after restart and SetOption0 is 0 (#3856)
* Disable serial logging on Shelly2 as serial is being used by energy monitoring (#3878)
* Fix Shelly2 wrong FrequencySet calculation and add input range checks (#3882)
*
* 6.2.1.5 20180921
* Add authentication to HTTP web pages
* Add energy monitoring to Shelly2 (#2789)
* Rewrite TSL2561 driver to fix some issues (#3681)
*
* 6.2.1.4 20180916
* Add command SerialSend5 to send raw serial data like "A5074100545293"
* Update MCP230xx driver
* Update Czech translation
* Update MP3 driver (#3800)
* Add userid/password option to decode-status.py (#3796)
* Fix syslog when emulation is selected (#2109, #3784)
* Fix Pzem2 compilation error (#3766, #3767)
* Add uncalibrated energy monitoring to Shelly2 (#2789)
*
* 6.2.1.3 20180907
* Change web Configure Module GPIO drop down list order for better readability
* Fix showing Period Power in energy threshold messages
* Fix ButtonRetain to not use default topic for clearing retain messages (#3737)
* Add sleep to Nova Fitness SDS01X sensor (#2841, #3724, #3749)
* Add Analog input AD0 enabled to sonoff-sensors.bin (#3756, #3757)
* Add Support to Xiaomi-Phillips Bulbs
* Add Support for Xiaomi-Philips Bulbs (#3787)
*
* 6.2.1.2 20180906
* Fix KNX PA exception. Regression from 6.2.1 buffer overflow caused by subStr() (#3700, #3710)

View File

@ -174,6 +174,7 @@
#define D_CMND_BLINKTIME "BlinkTime"
#define D_CMND_BLINKCOUNT "BlinkCount"
#define D_CMND_SENSOR "Sensor"
#define D_CMND_DRIVER "Driver"
#define D_CMND_SAVEDATA "SaveData"
#define D_CMND_SETOPTION "SetOption"
#define D_CMND_TEMPERATURE_RESOLUTION "TempRes"
@ -286,12 +287,10 @@
#define D_CMND_CURRENTLOW "CurrentLow"
#define D_CMND_CURRENTHIGH "CurrentHigh"
#define D_CMND_ENERGYRESET "EnergyReset"
#define D_CMND_POWERCAL "PowerCal"
#define D_CMND_POWERSET "PowerSet"
#define D_CMND_VOLTAGECAL "VoltageCal"
#define D_CMND_VOLTAGESET "VoltageSet"
#define D_CMND_CURRENTCAL "CurrentCal"
#define D_CMND_CURRENTSET "CurrentSet"
#define D_CMND_FREQUENCYSET "FrequencySet"
#define D_CMND_MAXPOWER "MaxPower"
#define D_CMND_MAXPOWERHOLD "MaxPowerHold"
#define D_CMND_MAXPOWERWINDOW "MaxPowerWindow"
@ -419,7 +418,8 @@ enum UnitNames {
UNIT_SECTORS,
UNIT_VOLT,
UNIT_WATT,
UNIT_WATTHOUR };
UNIT_WATTHOUR,
UNIT_HERTZ };
const char kUnitNames[] PROGMEM =
D_UNIT_AMPERE "|"
D_UNIT_HOUR "|"
@ -439,7 +439,8 @@ const char kUnitNames[] PROGMEM =
D_UNIT_SECTORS "|"
D_UNIT_VOLT "|"
D_UNIT_WATT "|"
D_UNIT_WATTHOUR ;
D_UNIT_WATTHOUR "|"
D_UNIT_HERTZ ;
const char S_JSON_COMMAND_NVALUE_SPACE_UNIT[] PROGMEM = "{\"%s\":\"%d %s\"}";
const char S_JSON_COMMAND_LVALUE_SPACE_UNIT[] PROGMEM = "{\"%s\":\"%lu %s\"}";
@ -467,6 +468,9 @@ const char S_JSON_COMMAND_INDEX_NVALUE_ACTIVE_NVALUE[] PROGMEM = "{\"%s%d\":\"%d
const char S_JSON_SENSOR_INDEX_NVALUE[] PROGMEM = "{\"" D_CMND_SENSOR "%d\":%d}";
const char S_JSON_SENSOR_INDEX_SVALUE[] PROGMEM = "{\"" D_CMND_SENSOR "%d\":\"%s\"}";
const char S_JSON_DRIVER_INDEX_NVALUE[] PROGMEM = "{\"" D_CMND_DRIVER "%d\":%d}";
const char S_JSON_DRIVER_INDEX_SVALUE[] PROGMEM = "{\"" D_CMND_DRIVER "%d\":\"%s\"}";
const char JSON_SNS_TEMP[] PROGMEM = "%s,\"%s\":{\"" D_JSON_TEMPERATURE "\":%s}";
const char JSON_SNS_TEMPHUM[] PROGMEM = "%s,\"%s\":{\"" D_JSON_TEMPERATURE "\":%s,\"" D_JSON_HUMIDITY "\":%s}";

View File

@ -94,7 +94,7 @@
#define D_FALSE "Nepravda"
#define D_FILE "Soubor"
#define D_FREE_MEMORY "Volná paměť"
#define D_FREQUENCY "Frequency"
#define D_FREQUENCY "Kmitočet"
#define D_GAS "Plyn"
#define D_GATEWAY "Výchozí brána"
#define D_GROUP "Skupina"
@ -128,7 +128,7 @@
#define D_POWERUSAGE_APPARENT "Apparent Power"
#define D_POWERUSAGE_REACTIVE "Reactive Power"
#define D_PRESSURE "Tlak"
#define D_PRESSUREATSEALEVEL "Tlak na úrovni hladiny moře"
#define D_PRESSUREATSEALEVEL "Tlak na hladině moře"
#define D_PROGRAM_FLASH_SIZE "Velikost paměti flash"
#define D_PROGRAM_SIZE "Velikost programu"
#define D_PROJECT "Projekt"

View File

@ -322,9 +322,13 @@ struct SYSCFG {
uint16_t mcp230xx_int_timer; // 718
byte free_71A[180]; // 71A
byte free_71A[174]; // 71A
char mems[MAX_RULE_MEMS][10]; // 7CE
unsigned long energy_frequency_calibration; // 7C8
byte free_7CC[2]; // 7CC
char mems[MAX_RULE_MEMS][10]; // 7CE
// 800 Full - no more free locations
char rules[MAX_RULE_SETS][MAX_RULE_SIZE]; // 800 uses 512 bytes in v5.12.0m, 3 x 512 bytes in v5.14.0b
@ -333,21 +337,21 @@ struct SYSCFG {
} Settings;
struct RTCRBT {
uint16_t valid; // 000
uint8_t fast_reboot_count; // 002
uint8_t free_003[1]; // 003
uint16_t valid; // 280 (RTC memory offset 100 - sizeof(RTCRBT))
uint8_t fast_reboot_count; // 282
uint8_t free_003[1]; // 283
} RtcReboot;
struct RTCMEM {
uint16_t valid; // 000
byte oswatch_blocked_loop; // 002
uint8_t ota_loader; // 003
unsigned long energy_kWhtoday; // 004
unsigned long energy_kWhtotal; // 008
unsigned long pulse_counter[MAX_COUNTERS]; // 00C
power_t power; // 01C
uint8_t free_020[60]; // 020
// 05C next free location (64 (=core) + 100 (=tasmota offset) + 92 (=0x5C RTCMEM struct) = 256 bytes (max = 512))
uint16_t valid; // 290 (RTC memory offset 100)
byte oswatch_blocked_loop; // 292
uint8_t ota_loader; // 293
unsigned long energy_kWhtoday; // 294
unsigned long energy_kWhtotal; // 298
unsigned long pulse_counter[MAX_COUNTERS]; // 29C
power_t power; // 2AC
uint8_t free_020[60]; // 2B0
// 2EC - 2FF free locations
} RtcSettings;
struct TIME_T {

View File

@ -95,7 +95,7 @@ void RtcSettingsSave()
void RtcSettingsLoad()
{
ESP.rtcUserMemoryRead(100, (uint32_t*)&RtcSettings, sizeof(RTCMEM));
ESP.rtcUserMemoryRead(100, (uint32_t*)&RtcSettings, sizeof(RTCMEM)); // 0x290
#ifdef DEBUG_THEO
AddLog_P(LOG_LEVEL_DEBUG, PSTR("Dump: Load"));
RtcSettingsDump();
@ -145,7 +145,7 @@ void RtcRebootSave()
void RtcRebootLoad()
{
ESP.rtcUserMemoryRead(100 - sizeof(RTCRBT), (uint32_t*)&RtcReboot, sizeof(RTCRBT));
ESP.rtcUserMemoryRead(100 - sizeof(RTCRBT), (uint32_t*)&RtcReboot, sizeof(RTCRBT)); // 0x280
if (RtcReboot.valid != RTC_MEM_VALID) {
memset(&RtcReboot, 0, sizeof(RTCRBT));
RtcReboot.valid = RTC_MEM_VALID;

View File

@ -83,7 +83,7 @@ enum TasmotaCommands {
CMND_LOGHOST, CMND_LOGPORT, CMND_IPADDRESS, CMND_NTPSERVER, CMND_AP, CMND_SSID, CMND_PASSWORD, CMND_HOSTNAME,
CMND_WIFICONFIG, CMND_FRIENDLYNAME, CMND_SWITCHMODE,
CMND_TELEPERIOD, CMND_RESTART, CMND_RESET, CMND_TIMEZONE, CMND_TIMESTD, CMND_TIMEDST, CMND_ALTITUDE, CMND_LEDPOWER, CMND_LEDSTATE,
CMND_I2CSCAN, CMND_SERIALSEND, CMND_BAUDRATE, CMND_SERIALDELIMITER };
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_BLINKTIME "|" D_CMND_BLINKCOUNT "|" D_CMND_SENSOR "|" D_CMND_SAVEDATA "|" D_CMND_SETOPTION "|" D_CMND_TEMPERATURE_RESOLUTION "|" D_CMND_HUMIDITY_RESOLUTION "|"
@ -93,7 +93,7 @@ const char kTasmotaCommands[] PROGMEM =
D_CMND_LOGHOST "|" D_CMND_LOGPORT "|" D_CMND_IPADDRESS "|" D_CMND_NTPSERVER "|" D_CMND_AP "|" D_CMND_SSID "|" D_CMND_PASSWORD "|" D_CMND_HOSTNAME "|"
D_CMND_WIFICONFIG "|" D_CMND_FRIENDLYNAME "|" D_CMND_SWITCHMODE "|"
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;
D_CMND_I2CSCAN "|" D_CMND_SERIALSEND "|" D_CMND_BAUDRATE "|" D_CMND_SERIALDELIMITER "|" D_CMND_DRIVER;
const uint8_t kIFan02Speed[4][3] = {{6,6,6}, {7,6,6}, {7,7,6}, {7,6,7}};
@ -335,7 +335,8 @@ void SetDevicePower(power_t rpower, int source)
}
}
XdrvSetPower(rpower);
XdrvMailbox.index = rpower;
XdrvCall(FUNC_SET_POWER);
if ((SONOFF_DUAL == Settings.module) || (CH4 == Settings.module)) {
Serial.write(0xA0);
@ -461,10 +462,11 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len)
grpflg = (strstr(topicBuf, Settings.mqtt_grptopic) != NULL);
fallback_topic_flag = (strstr(topicBuf, mqtt_client) != NULL);
type = strrchr(topicBuf, '/') +1; // Last part of received topic is always the command (type)
type = strrchr(topicBuf, '/'); // Last part of received topic is always the command (type)
index = 1;
if (type != NULL) {
type++;
for (i = 0; i < strlen(type); i++) {
type[i] = toupper(type[i]);
}
@ -679,7 +681,7 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len)
}
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, (Settings.save_data > 1) ? stemp1 : GetStateText(Settings.save_data));
}
else if (CMND_SENSOR == command_code) {
else if ((CMND_SENSOR == command_code) || (CMND_DRIVER == command_code)) {
XdrvMailbox.index = index;
XdrvMailbox.data_len = data_len;
XdrvMailbox.payload16 = payload16;
@ -687,8 +689,11 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len)
XdrvMailbox.grpflg = grpflg;
XdrvMailbox.topic = command;
XdrvMailbox.data = dataBuf;
XsnsCall(FUNC_COMMAND);
// if (!XsnsCall(FUNC_COMMAND)) type = NULL;
if (CMND_SENSOR == command_code) {
XsnsCall(FUNC_COMMAND);
} else {
XdrvCall(FUNC_COMMAND);
}
}
else if ((CMND_SETOPTION == command_code) && (index < 82)) {
byte ptype;
@ -969,22 +974,25 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len)
}
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, Settings.baudrate * 1200);
}
else if ((CMND_SERIALSEND == command_code) && (index > 0) && (index <= 4)) {
else if ((CMND_SERIALSEND == command_code) && (index > 0) && (index <= 5)) {
SetSeriallog(LOG_LEVEL_NONE);
Settings.flag.mqtt_serial = 1;
Settings.flag.mqtt_serial_raw = (4 == index) ? 1 : 0;
Settings.flag.mqtt_serial_raw = (index > 3) ? 1 : 0;
if (data_len > 0) {
if (1 == index) {
Serial.printf("%s\n", dataBuf);
Serial.printf("%s\n", dataBuf); // "Hello Tiger\n"
}
else if (2 == index || 4 == index) {
for (int i = 0; i < data_len; i++) {
Serial.write(dataBuf[i]);
Serial.write(dataBuf[i]); // "Hello Tiger" or "A0"
}
}
else if (3 == index) {
uint16_t dat_len = data_len;
Serial.printf("%s", Unescape(dataBuf, &dat_len));
Serial.printf("%s", Unescape(dataBuf, &dat_len)); // "Hello\f"
}
else if (5 == index) {
SerialSendRaw(dataBuf, data_len); // "AA004566"
}
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_DONE);
}
@ -1035,7 +1043,8 @@ void MqttDataHandler(char* topic, byte* data, unsigned int data_len)
for (i = 0; i < strlen(Settings.ntp_server[index -1]); i++) {
if (Settings.ntp_server[index -1][i] == ',') Settings.ntp_server[index -1][i] = '.';
}
restart_flag = 2;
// restart_flag = 2; // Issue #3890
ntp_force_sync = 1;
}
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_INDEX_SVALUE, command, index, Settings.ntp_server[index -1]);
}
@ -2606,7 +2615,6 @@ void setup()
SetPulseTimer(i, Settings.pulse_timer[i]);
}
}
blink_powersave = power;
snprintf_P(log_data, sizeof(log_data), PSTR(D_PROJECT " %s %s (" D_CMND_TOPIC " %s, " D_FALLBACK " %s, " D_CMND_GROUPTOPIC " %s) " D_VERSION " %s-" ARDUINO_ESP8266_RELEASE),

View File

@ -20,7 +20,7 @@
#ifndef _SONOFF_VERSION_H_
#define _SONOFF_VERSION_H_
#define VERSION 0x06020103
#define VERSION 0x06020107
#define D_PROGRAMNAME "Sonoff-Tasmota"
#define D_AUTHOR "Theo Arends"

View File

@ -704,6 +704,21 @@ void ClaimSerial()
Settings.baudrate = baudrate / 1200;
}
void SerialSendRaw(char *codes, int size)
{
char *p;
char stemp[3];
uint8_t code;
while (size > 0) {
snprintf(stemp, sizeof(stemp), codes);
code = strtol(stemp, &p, 16);
Serial.write(code);
size -= 2;
codes += 2;
}
}
uint32_t GetHash(const char *buffer, size_t size)
{
uint32_t hash = 0;

View File

@ -294,9 +294,12 @@
// #define USE_SI1145 // Enable SI1145/46/47 sensor (I2C address 0x60) (+1k code)
#define USE_LM75AD // Enable LM75AD sensor (I2C addresses 0x48 - 0x4F) (+0k5 code)
// #define USE_APDS9960 // Enable APDS9960 Proximity Sensor (I2C address 0x39). Disables SHT and VEML6070 (+4k7 code)
// #define USE_MCP230xx // Enable MCP23008/MCP23017 for GP INPUT ONLY (I2C addresses 0x20 - 0x27) providing command Sensor29 for configuration (+4k7 code)
// #define USE_MCP230xx // Enable MCP23008/MCP23017 - Must define I2C Address in #define USE_MCP230xx_ADDR below - range 0x20 - 0x27 (+4k7 code)
// #define USE_MCP230xx_ADDR 0x20 // Enable MCP23008/MCP23017 I2C Address to use (Must be within range 0x20 through 0x27 - set according to your wired setup)
// #define USE_MCP230xx_OUTPUT // Enable MCP23008/MCP23017 OUTPUT support through sensor29 commands (+1k5 code)
// #define USE_MCP230xx_DISPLAYOUTPUT // Enable MCP23008/MCP23017 to display state of OUTPUT pins on Web UI (+0k2 code)
// #define USE_PCA9685 // Enable PCA9685 I2C HW PWM Driver - Must define I2C Address in #define USE_PCA9685_ADDR below - range 0x40 - 0x47 (+1k4 code)
// #define USE_PCA9685_ADDR 0x40 // Enable PCA9685 I2C Address to use (Must be within range 0x40 through 0x47 - set according to your wired setup)
// #define USE_MPR121 // Enable MPR121 controller (I2C addresses 0x5A, 0x5B, 0x5C and 0x5D) in input mode for touch buttons (+1k3 code)
// #define USE_CCS811 // Enable CCS811 sensor (I2C address 0x5A) (+2k2 code)
// #define USE_MPU6050 // Enable MPU6050 sensor (I2C address 0x68 AD0 low or 0x69 AD0 high) (+2k6 code)
@ -342,11 +345,12 @@
//#define USE_SDM630 // Add support for Eastron SDM630-Modbus energy meter (+2k code)
#define SDM630_SPEED 9600 // SDM630-Modbus RS485 serial speed (default: 9600 baud)
//#define USE_MP3_PLAYER // Use of the DFPlayer Mini MP3 Player RB-DFR-562 commands: play, volume and stop
// #define MP3_VOLUME 10 // Set the startup volume on init, the range can be 0..30(max)
#define MP3_VOLUME 10 // Set the startup volume on init, the range can be 0..30(max)
// Power monitoring sensors -----------------------
#define USE_PZEM004T // Add support for PZEM004T Energy monitor (+2k code)
#define USE_PZEM2 // Add support for PZEM003,014,016,017 Energy monitor (+1k1 code)
#define USE_MCP39F501 // Add support for MCP39F501 Energy monitor as used in Shelly 2 (+3k1 code)
// -- Low level interface devices -----------------
#define USE_IR_REMOTE // Send IR remote commands using library IRremoteESP8266 and ArduinoJson (+4k code, 0k3 mem, 48 iram)

View File

@ -29,8 +29,6 @@
uint8_t *efm8bb1_update = NULL;
#endif // USE_RF_FLASH
#define D_TASMOTA_TOKEN "Tasmota-Token"
enum UploadTypes { UPL_TASMOTA, UPL_SETTINGS, UPL_EFM8BB1 };
const char HTTP_HEAD[] PROGMEM =
@ -58,17 +56,13 @@ const char HTTP_HEAD[] PROGMEM =
"eb('s1').value=l.innerText||l.textContent;"
"eb('p1').focus();"
"}"
"function lx(){"
"if(to==1){"
"if(tp<30){"
"tp++;"
"lt=setTimeout(lx,33);" // Wait for token from server
"}else{"
"lt=setTimeout(la,1355);" // Discard action and retry
"}"
"return;"
"function la(p){"
"var a='';"
"if(la.arguments.length==1){"
"a=p;"
"clearTimeout(lt);"
"}"
"if(x!=null){x.abort();}" // Abort if no response within 2 seconds (happens on restart 1)
"if(x!=null){x.abort();}" // Abort if no response within 2 seconds (happens on restart 1)
"x=new XMLHttpRequest();"
"x.onreadystatechange=function(){"
"if(x.readyState==4&&x.status==200){"
@ -76,32 +70,15 @@ const char HTTP_HEAD[] PROGMEM =
"eb('l1').innerHTML=s;"
"}"
"};"
"x.open('GET','ay'+pc,true);" // Async request
"x.setRequestHeader('" D_TASMOTA_TOKEN "',to);"
"x.send();" // Perform command if available and get updated information
"pc='';"
"lt=setTimeout(la,2345-(tp*33));"
"}"
"function la(p){"
"if(la.arguments.length==1){"
"pc='?'+p;"
"clearTimeout(lt);"
"}else{pc='';}"
"to=1;tp=0;"
"if(x!=null){x.abort();}" // Abort if no response within 2 seconds (happens on restart 1)
"x=new XMLHttpRequest();"
"x.onreadystatechange=function(){"
"if(x.readyState==4&&x.status==200){to=x.getResponseHeader('" D_TASMOTA_TOKEN "');}else{to=1;}"
"};"
"x.open('GET','az',true);" // Async request
"x.send();" // Get token from server
"lx();"
"x.open('GET','ay'+a,true);"
"x.send();"
"lt=setTimeout(la,2345);"
"}"
"function lb(p){"
"la('d='+p);"
"la('?d='+p);"
"}"
"function lc(p){"
"la('c='+p);"
"la('?t='+p);"
"}";
const char HTTP_HEAD_STYLE[] PROGMEM =
@ -340,7 +317,7 @@ 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_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";
@ -349,8 +326,6 @@ const char HDR_CTYPE_XML[] PROGMEM = "text/xml";
const char HDR_CTYPE_JSON[] PROGMEM = "application/json";
const char HDR_CTYPE_STREAM[] PROGMEM = "application/octet-stream";
const char HDR_TASMOTA_TOKEN[] PROGMEM = D_TASMOTA_TOKEN;
#define DNS_PORT 53
enum HttpOptions {HTTP_OFF, HTTP_USER, HTTP_ADMIN, HTTP_MANAGER};
@ -366,7 +341,6 @@ uint8_t upload_progress_dot_count;
uint8_t config_block_count = 0;
uint8_t config_xor_on = 0;
uint8_t config_xor_on_set = CONFIG_FILE_XOR;
long ajax_token = 1;
// Helper function to avoid code duplication (saves 4k Flash)
static void WebGetArg(const char* arg, char* out, size_t max)
@ -400,11 +374,10 @@ void StartWebserver(int type, IPAddress ipweb)
WebServer->on("/up", HandleUpgradeFirmware);
WebServer->on("/u1", HandleUpgradeFirmwareStart); // OTA
WebServer->on("/u2", HTTP_POST, HandleUploadDone, HandleUploadLoop);
WebServer->on("/u2", HTTP_OPTIONS, HandlePreflightRequest);
WebServer->on("/cs", HandleConsole);
WebServer->on("/ax", HandleAjaxConsoleRefresh);
WebServer->on("/ay", HandleAjaxStatusRefresh);
WebServer->on("/az", HandleToken);
WebServer->on("/u2", HTTP_OPTIONS, HandlePreflightRequest);
WebServer->on("/cm", HandleHttpCommand);
WebServer->on("/rb", HandleRestart);
#ifndef BE_MINIMAL
@ -509,6 +482,15 @@ void SetHeader()
#endif
}
bool WebAuthenticate(void)
{
if (Settings.web_password[0] != 0) {
return WebServer->authenticate(WEB_USERNAME, Settings.web_password);
} else {
return true;
}
}
void ShowPage(String &page, bool auth)
{
if (auth && (Settings.web_password[0] != 0) && !WebServer->authenticate(WEB_USERNAME, Settings.web_password)) {
@ -619,7 +601,7 @@ void HandleRoot()
if (idx > 0) { page += F("</tr><tr>"); }
for (byte j = 0; j < 4; j++) {
idx++;
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("<td style='width:25%'><button onclick='la(\"k=%d\");'>%d</button></td>"), idx, idx);
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("<td style='width:25%'><button onclick='la(\"?k=%d\");'>%d</button></td>"), idx, idx);
page += mqtt_data;
}
}
@ -634,34 +616,13 @@ void HandleRoot()
}
}
void HandleToken()
{
char token[11];
ajax_token = random(2, 0x7FFFFFFF);
snprintf_P(token, sizeof(token), PSTR("%u"), ajax_token);
SetHeader();
WebServer->sendHeader(FPSTR(HDR_TASMOTA_TOKEN), token);
snprintf_P(token, sizeof(token), PSTR("%u"), random(0x7FFFFFFF));
WebServer->send(200, FPSTR(HDR_CTYPE_HTML), token);
const char* header_key[] = { D_TASMOTA_TOKEN };
WebServer->collectHeaders(header_key, 1);
}
void HandleAjaxStatusRefresh()
{
if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
char svalue[80];
char tmp[100];
if (WebServer->header(FPSTR(HDR_TASMOTA_TOKEN)).toInt() != ajax_token) {
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR(D_FILE_NOT_FOUND));
SetHeader();
WebServer->send(404, FPSTR(HDR_CTYPE_PLAIN), mqtt_data);
return;
}
ajax_token = 1;
WebGetArg("o", tmp, sizeof(tmp));
if (strlen(tmp)) {
ShowWebSource(SRC_WEBGUI);
@ -736,6 +697,7 @@ boolean HttpUser()
void HandleConfiguration()
{
if (HttpUser()) { return; }
if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURATION);
String page = FPSTR(HTTP_HEAD);
@ -759,6 +721,7 @@ void HandleConfiguration()
void HandleModuleConfiguration()
{
if (HttpUser()) { return; }
if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
char stemp[20];
uint8_t midx;
@ -829,6 +792,7 @@ void HandleWifiConfiguration()
void HandleWifi(boolean scan)
{
if (HttpUser()) { return; }
if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_WIFI);
@ -925,6 +889,7 @@ void HandleWifi(boolean scan)
void HandleMqttConfiguration()
{
if (HttpUser()) { return; }
if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_MQTT);
String page = FPSTR(HTTP_HEAD);
@ -948,6 +913,7 @@ void HandleMqttConfiguration()
void HandleLoggingConfiguration()
{
if (HttpUser()) { return; }
if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_LOGGING);
String page = FPSTR(HTTP_HEAD);
@ -995,6 +961,7 @@ void HandleLoggingConfiguration()
void HandleOtherConfiguration()
{
if (HttpUser()) { return; }
if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_OTHER);
char stemp[40];
@ -1032,6 +999,7 @@ void HandleOtherConfiguration()
void HandleBackupConfiguration()
{
if (HttpUser()) { return; }
if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_BACKUP_CONFIGURATION));
if (!SettingsBufferAlloc()) { return; }
@ -1067,6 +1035,7 @@ void HandleBackupConfiguration()
void HandleSaveSettings()
{
if (HttpUser()) { return; }
if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
char stemp[TOPSZ];
char stemp2[TOPSZ];
@ -1232,6 +1201,7 @@ void HandleSaveSettings()
void HandleResetConfiguration()
{
if (HttpUser()) { return; }
if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
char svalue[33];
@ -1252,6 +1222,7 @@ void HandleResetConfiguration()
void HandleRestoreConfiguration()
{
if (HttpUser()) { return; }
if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_RESTORE_CONFIGURATION);
String page = FPSTR(HTTP_HEAD);
@ -1270,6 +1241,7 @@ void HandleRestoreConfiguration()
void HandleInformation()
{
if (HttpUser()) { return; }
if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_INFORMATION);
char stopic[TOPSZ];
@ -1386,6 +1358,7 @@ void HandleInformation()
void HandleUpgradeFirmware()
{
if (HttpUser()) { return; }
if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_FIRMWARE_UPGRADE);
String page = FPSTR(HTTP_HEAD);
@ -1405,6 +1378,7 @@ void HandleUpgradeFirmware()
void HandleUpgradeFirmwareStart()
{
if (HttpUser()) { return; }
if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
char svalue[100];
AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_UPGRADE_STARTED));
@ -1432,6 +1406,7 @@ void HandleUpgradeFirmwareStart()
void HandleUploadDone()
{
if (HttpUser()) { return; }
if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_UPLOAD_DONE));
char error[100];
@ -1684,6 +1659,7 @@ void HandlePreflightRequest()
void HandleHttpCommand()
{
if (HttpUser()) { return; }
// if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
char svalue[INPUT_BUFFER_SIZE]; // Large to serve Backlog
AddLog_P(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP D_COMMAND));
@ -1741,6 +1717,7 @@ void HandleHttpCommand()
void HandleConsole()
{
if (HttpUser()) { return; }
if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONSOLE);
String page = FPSTR(HTTP_HEAD);
@ -1756,6 +1733,7 @@ void HandleConsole()
void HandleAjaxConsoleRefresh()
{
if (HttpUser()) { return; }
if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
char svalue[INPUT_BUFFER_SIZE]; // Large to serve Backlog
byte cflg = 1;
byte counter = 0; // Initial start, should never be 0 again
@ -1812,6 +1790,7 @@ void HandleAjaxConsoleRefresh()
void HandleRestart()
{
if (HttpUser()) { return; }
if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_RESTART);
String page = FPSTR(HTTP_HEAD);

View File

@ -29,14 +29,14 @@
enum EnergyCommands {
CMND_POWERDELTA,
CMND_POWERLOW, CMND_POWERHIGH, CMND_VOLTAGELOW, CMND_VOLTAGEHIGH, CMND_CURRENTLOW, CMND_CURRENTHIGH,
CMND_POWERCAL, CMND_POWERSET, CMND_VOLTAGECAL, CMND_VOLTAGESET, CMND_CURRENTCAL, CMND_CURRENTSET,
CMND_POWERSET, CMND_VOLTAGESET, CMND_CURRENTSET, CMND_FREQUENCYSET,
CMND_ENERGYRESET, CMND_MAXENERGY, CMND_MAXENERGYSTART,
CMND_MAXPOWER, CMND_MAXPOWERHOLD, CMND_MAXPOWERWINDOW,
CMND_SAFEPOWER, CMND_SAFEPOWERHOLD, CMND_SAFEPOWERWINDOW };
const char kEnergyCommands[] PROGMEM =
D_CMND_POWERDELTA "|"
D_CMND_POWERLOW "|" D_CMND_POWERHIGH "|" D_CMND_VOLTAGELOW "|" D_CMND_VOLTAGEHIGH "|" D_CMND_CURRENTLOW "|" D_CMND_CURRENTHIGH "|"
D_CMND_POWERCAL "|" D_CMND_POWERSET "|" D_CMND_VOLTAGECAL "|" D_CMND_VOLTAGESET "|" D_CMND_CURRENTCAL "|" D_CMND_CURRENTSET "|"
D_CMND_POWERSET "|" D_CMND_VOLTAGESET "|" D_CMND_CURRENTSET "|" D_CMND_FREQUENCYSET "|"
D_CMND_ENERGYRESET "|" D_CMND_MAXENERGY "|" D_CMND_MAXENERGYSTART "|"
D_CMND_MAXPOWER "|" D_CMND_MAXPOWERHOLD "|" D_CMND_MAXPOWERWINDOW "|"
D_CMND_SAFEPOWER "|" D_CMND_SAFEPOWERHOLD "|" D_CMND_SAFEPOWERWINDOW ;
@ -98,6 +98,8 @@ void EnergyUpdateToday()
void Energy200ms()
{
energy_power_on = (power != 0) | Settings.flag.no_power_on_check;
energy_fifth_second++;
if (5 == energy_fifth_second) {
energy_fifth_second = 0;
@ -121,8 +123,6 @@ void Energy200ms()
}
}
energy_power_on = (power &1) | Settings.flag.no_power_on_check;
XnrgCall(FUNC_EVERY_200_MSECOND);
if (energy_calc_power_factor) {
@ -415,42 +415,22 @@ boolean EnergyCommand()
command, energy_total_chr, energy_yesterday_chr, energy_daily_chr);
status_flag = 1;
}
else if ((CMND_POWERCAL == command_code) && XnrgCall(FUNC_COMMAND)) {
if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 32001)) {
Settings.energy_power_calibration = (XdrvMailbox.payload > 4000) ? XdrvMailbox.payload : HLW_PREF_PULSE; // HLW = 12530, CSE = 5364
}
nvalue = Settings.energy_power_calibration;
unit = UNIT_MICROSECOND;
}
else if ((CMND_VOLTAGECAL == command_code) && XnrgCall(FUNC_COMMAND)) {
if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 32001)) {
Settings.energy_voltage_calibration = (XdrvMailbox.payload > 999) ? XdrvMailbox.payload : HLW_UREF_PULSE; // HLW = 1950, CSE = 1912
}
nvalue = Settings.energy_voltage_calibration;
unit = UNIT_MICROSECOND;
}
else if ((CMND_CURRENTCAL == command_code) && XnrgCall(FUNC_COMMAND)) {
if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 32001)) {
Settings.energy_current_calibration = (XdrvMailbox.payload > 1100) ? XdrvMailbox.payload : HLW_IREF_PULSE; // HLW = 3500, CSE = 16140
}
nvalue = Settings.energy_current_calibration;
unit = UNIT_MICROSECOND;
}
else if ((CMND_POWERSET == command_code) && XnrgCall(FUNC_COMMAND)) { // Watt
snprintf_P(command, sizeof(command), PSTR(D_CMND_POWERCAL));
nvalue = Settings.energy_power_calibration;
unit = UNIT_MICROSECOND;
}
else if ((CMND_VOLTAGESET == command_code) && XnrgCall(FUNC_COMMAND)) { // Volt
snprintf_P(command, sizeof(command), PSTR(D_CMND_VOLTAGECAL));
nvalue = Settings.energy_voltage_calibration;
unit = UNIT_MICROSECOND;
}
else if ((CMND_CURRENTSET == command_code) && XnrgCall(FUNC_COMMAND)) { // milliAmpere
snprintf_P(command, sizeof(command), PSTR(D_CMND_CURRENTCAL));
nvalue = Settings.energy_current_calibration;
unit = UNIT_MICROSECOND;
}
else if ((CMND_FREQUENCYSET == command_code) && XnrgCall(FUNC_COMMAND)) { // Hz
nvalue = Settings.energy_frequency_calibration;
unit = UNIT_MICROSECOND;
}
#if FEATURE_POWER_LIMIT
else if (CMND_MAXPOWER == command_code) {
@ -514,6 +494,11 @@ boolean EnergyCommand()
else serviced = false; // Unknown command
if (serviced && !status_flag) {
if (UNIT_MICROSECOND == unit) {
snprintf_P(command, sizeof(command), PSTR("%sCal"), command);
}
if (Settings.flag.value_units) {
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_LVALUE_SPACE_UNIT, command, nvalue, GetTextIndexed(sunit, sizeof(sunit), unit, kUnitNames));
} else {

View File

@ -199,21 +199,6 @@ uint8_t SnfBrUpdateInit()
/********************************************************************************************/
void SonoffBridgeSendRaw(char *codes, int size)
{
char *p;
char stemp[3];
uint8_t code;
while (size > 0) {
snprintf(stemp, sizeof(stemp), codes);
code = strtol(stemp, &p, 16);
Serial.write(code);
size -= 2;
codes += 2;
}
}
void SonoffBridgeReceivedRaw()
{
// Decoding according to https://github.com/Portisch/RF-Bridge-EFM8BB1
@ -552,11 +537,11 @@ boolean SonoffBridgeCommand()
break;
case 192: // 0xC0 - Beep
char beep[] = "AAC000C055";
SonoffBridgeSendRaw(beep, sizeof(beep));
SerialSendRaw(beep, sizeof(beep));
break;
}
} else {
SonoffBridgeSendRaw(XdrvMailbox.data, XdrvMailbox.data_len);
SerialSendRaw(XdrvMailbox.data, XdrvMailbox.data_len);
sonoff_bridge_receive_raw_flag = 1;
}
}

View File

@ -375,10 +375,8 @@ const char S_CONFIGURE_DOMOTICZ[] PROGMEM = D_CONFIGURE_DOMOTICZ;
void HandleDomoticzConfiguration()
{
if (HTTP_USER == webserver_state) {
HandleRoot();
return;
}
if (HttpUser()) { return; }
if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_DOMOTICZ);
char stemp[32];

View File

@ -676,10 +676,8 @@ const char S_CONFIGURE_TIMER[] PROGMEM = D_CONFIGURE_TIMER;
void HandleTimerConfiguration()
{
if (HTTP_USER == webserver_state) {
HandleRoot();
return;
}
if (HttpUser()) { return; }
if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_TIMER);
String page = FPSTR(HTTP_HEAD);

View File

@ -366,6 +366,7 @@ void RulesEvery50ms()
if (Settings.rule_enabled) { // Any rule enabled
char json_event[120];
if (-1 == rules_new_power) { rules_new_power = power; }
if (rules_new_power != rules_old_power) {
if (rules_old_power != -1) {
for (byte i = 0; i < devices_present; i++) {

View File

@ -803,15 +803,13 @@ const char HTTP_FORM_KNX_ADD_TABLE_ROW2[] PROGMEM =
void HandleKNXConfiguration()
{
if (HttpUser()) { return; }
if (!WebAuthenticate()) { return WebServer->requestAuthentication(); }
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_KNX);
char tmp[100];
String stmp;
if (HTTP_USER == webserver_state) {
HandleRoot();
return;
}
AddLog_P(LOG_LEVEL_DEBUG, S_LOG_HTTP, S_CONFIGURE_KNX);
if ( WebServer->hasArg("save") ) {
KNX_Save_Settings();
HandleConfiguration();

View File

@ -19,7 +19,7 @@
#ifdef USE_HOME_ASSISTANT
const char HASS_DISCOVER_SWITCH[] PROGMEM =
const char HASS_DISCOVER_RELAY[] PROGMEM =
"{\"name\":\"%s\"," // dualr2 1
"\"command_topic\":\"%s\"," // cmnd/dualr2/POWER2
"\"state_topic\":\"%s\"," // stat/dualr2/RESULT (implies "\"optimistic\":\"false\",")
@ -39,7 +39,8 @@ const char HASS_DISCOVER_BUTTON[] PROGMEM =
// "\"optimistic\":\"false\"," // false is Hass default when state_topic is set
"\"availability_topic\":\"%s\"," // tele/dualr2/LWT
"\"payload_available\":\"" D_ONLINE "\"," // Online
"\"payload_not_available\":\"" D_OFFLINE "\""; // Offline
"\"payload_not_available\":\"" D_OFFLINE "\"," // Offline
"\"force_update\":true";
const char HASS_DISCOVER_LIGHT_DIMMER[] PROGMEM =
"%s,\"brightness_command_topic\":\"%s\"," // cmnd/led2/Dimmer
@ -102,7 +103,7 @@ void HAssDiscoverRelay()
GetTopic_P(command_topic, CMND, mqtt_topic, value_template);
GetTopic_P(state_topic, STAT, mqtt_topic, S_RSLT_RESULT);
GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT);
snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_SWITCH, name, command_topic, state_topic, value_template, Settings.state_text[0], Settings.state_text[1], availability_topic);
snprintf_P(mqtt_data, sizeof(mqtt_data), HASS_DISCOVER_RELAY, name, command_topic, state_topic, value_template, Settings.state_text[0], Settings.state_text[1], availability_topic);
if (is_light) {
char brightness_command_topic[TOPSZ];

184
sonoff/xdrv_15_pca9685.ino Normal file
View File

@ -0,0 +1,184 @@
/*
xdrv_15_pca9685.ino - Support for I2C PCA9685 12bit 16 pin hardware PWM driver
Copyright (C) 2018 Andre Thomas and 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/>.
*/
#ifdef USE_I2C
#ifdef USE_PCA9685
#define XDRV_15 15
#define PCA9685_REG_MODE1 0x00
#define PCA9685_REG_LED0_ON_L 0x06
#define PCA9685_REG_PRE_SCALE 0xFE
uint8_t pca9685_detected = 0;
uint16_t pca9685_freq = 50;
void PCA9685_Detect(void)
{
if (pca9685_detected) { return; }
uint8_t buffer;
if (I2cValidRead8(&buffer, USE_PCA9685_ADDR, PCA9685_REG_MODE1)) {
I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_MODE1, 0x20);
if (I2cValidRead8(&buffer, USE_PCA9685_ADDR, PCA9685_REG_MODE1)) {
if (0x20 == buffer) {
pca9685_detected = 1;
snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, "PCA9685", USE_PCA9685_ADDR);
AddLog(LOG_LEVEL_DEBUG);
PCA9685_Reset(); // Reset the controller
}
}
}
}
void PCA9685_Reset(void)
{
I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_MODE1, 0x80);
PCA9685_SetPWMfreq(50);
for (uint8_t pin=0;pin<16;pin++) {
PCA9685_SetPWM(pin,0,false);
}
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"PCA9685\":{\"RESET\":\"OK\"}}"));
}
void PCA9685_SetPWMfreq(double freq) {
/*
7.3.5 from datasheet
prescale value = round(25000000/(4096*freq))-1;
*/
pca9685_freq=freq;
uint8_t pre_scale_osc = round(25000000/(4096*freq))-1;
if (1526 == freq) pre_scale_osc=0xFF; // force setting for 24hz because rounding causes 1526 to be 254
uint8_t current_mode1 = I2cRead8(USE_PCA9685_ADDR, PCA9685_REG_MODE1); // read current value of MODE1 register
uint8_t sleep_mode1 = (current_mode1&0x7F) | 0x10; // Determine register value to put PCA to sleep
I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_MODE1, sleep_mode1); // Let's sleep a little
I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_PRE_SCALE, pre_scale_osc); // Set the pre-scaler
I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_MODE1, current_mode1 | 0xA0); // Reset MODE1 register to original state and enable auto increment
}
void PCA9685_SetPWM_Reg(uint8_t pin, uint16_t on, uint16_t off) {
uint8_t led_reg = PCA9685_REG_LED0_ON_L + 4 * pin;
uint32_t led_data = 0;
I2cWrite8(USE_PCA9685_ADDR, led_reg, on);
I2cWrite8(USE_PCA9685_ADDR, led_reg+1, (on >> 8));
I2cWrite8(USE_PCA9685_ADDR, led_reg+2, off);
I2cWrite8(USE_PCA9685_ADDR, led_reg+3, (off >> 8));
}
void PCA9685_SetPWM(uint8_t pin, uint16_t pwm, bool inverted) {
if (4096 == pwm) {
PCA9685_SetPWM_Reg(pin, 4096, 0); // Special use additional bit causes channel to turn on completely without PWM
} else {
PCA9685_SetPWM_Reg(pin, 0, pwm);
}
}
bool PCA9685_Command(void)
{
boolean serviced = true;
boolean validpin = false;
uint8_t paramcount = 0;
if (XdrvMailbox.data_len > 0) {
paramcount=1;
} else {
serviced = false;
return serviced;
}
char sub_string[XdrvMailbox.data_len];
for (uint8_t ca=0;ca<XdrvMailbox.data_len;ca++) {
if ((' ' == XdrvMailbox.data[ca]) || ('=' == XdrvMailbox.data[ca])) { XdrvMailbox.data[ca] = ','; }
if (',' == XdrvMailbox.data[ca]) { paramcount++; }
}
UpperCase(XdrvMailbox.data,XdrvMailbox.data);
if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"RESET")) { PCA9685_Reset(); return serviced; }
if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"PWMF")) {
if (paramcount > 1) {
uint16_t new_freq = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2));
if ((new_freq >= 24) && (new_freq <= 1526)) {
PCA9685_SetPWMfreq(new_freq);
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"PCA9685\":{\"PWMF\":%i, \"Result\":\"OK\"}}"));
return serviced;
}
} else { // No parameter was given for setfreq, so we return current setting
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"PCA9685\":{\"PWMF\":%i}}"),pca9685_freq);
return serviced;
}
}
if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 1),"PWM")) {
if (paramcount > 1) {
uint8_t pin = atoi(subStr(sub_string, XdrvMailbox.data, ",", 2));
if (paramcount > 2) {
if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 3), "ON")) {
PCA9685_SetPWM(pin, 4096, false);
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"PCA9685\":{\"PIN\":%i,\"PWM\":%i}}"),pin,4096);
serviced = true;
return serviced;
}
if (!strcmp(subStr(sub_string, XdrvMailbox.data, ",", 3), "OFF")) {
PCA9685_SetPWM(pin, 0, false);
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"PCA9685\":{\"PIN\":%i,\"PWM\":%i}}"),pin,0);
serviced = true;
return serviced;
}
uint16_t pwm = atoi(subStr(sub_string, XdrvMailbox.data, ",", 3));
if ((pin >= 0 && pin <= 15) && (pwm >= 0 && pwm <= 4096)) {
PCA9685_SetPWM(pin, pwm, false);
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"PCA9685\":{\"PIN\":%i,\"PWM\":%i}}"),pin,pwm);
serviced = true;
return serviced;
}
}
}
}
return serviced;
}
boolean Xdrv15(byte function)
{
boolean result = false;
if (i2c_flg) {
switch (function) {
case FUNC_MQTT_DATA:
break;
case FUNC_EVERY_SECOND:
PCA9685_Detect();
break;
case FUNC_EVERY_50_MSECOND:
break;
case FUNC_JSON_APPEND:
break;
case FUNC_COMMAND:
if (XDRV_15 == XdrvMailbox.index) {
PCA9685_Command();
}
break;
case FUNC_WEB_APPEND:
break;
default:
break;
}
}
return result;
}
#endif // USE_PCA9685
#endif // USE_IC2

View File

@ -35,20 +35,25 @@
* Debug commands
\*********************************************************************************************/
#define D_CMND_CFGDUMP "CfgDump"
#define D_CMND_CFGPOKE "CfgPoke"
#define D_CMND_CFGPEEK "CfgPeek"
#define D_CMND_CFGXOR "CfgXor"
#define D_CMND_CFGDUMP "CfgDump"
#define D_CMND_CFGPOKE "CfgPoke"
#define D_CMND_CFGPEEK "CfgPeek"
#define D_CMND_CFGSHOW "CfgShow"
#define D_CMND_CFGXOR "CfgXor"
#define D_CMND_CPUCHECK "CpuChk"
#define D_CMND_EXCEPTION "Exception"
#define D_CMND_CPUCHECK "CpuChk"
#define D_CMND_FREEMEM "FreeMem"
#define D_CMND_RTCDUMP "RtcDump"
#define D_CMND_HELP "Help"
enum DebugCommands { CMND_CFGDUMP, CMND_CFGPEEK, CMND_CFGPOKE, CMND_CFGXOR, CMND_EXCEPTION, CMND_CPUCHECK };
const char kDebugCommands[] PROGMEM = D_CMND_CFGDUMP "|" D_CMND_CFGPEEK "|" D_CMND_CFGPOKE "|" D_CMND_CFGXOR "|" D_CMND_EXCEPTION "|" D_CMND_CPUCHECK;
enum DebugCommands { CMND_CFGDUMP, CMND_CFGPEEK, CMND_CFGPOKE, CMND_CFGSHOW, CMND_CFGXOR, CMND_CPUCHECK, CMND_EXCEPTION, CMND_FREEMEM, CMND_RTCDUMP, CMND_HELP };
const char kDebugCommands[] PROGMEM = D_CMND_CFGDUMP "|" D_CMND_CFGPEEK "|" D_CMND_CFGPOKE "|" D_CMND_CFGSHOW "|" D_CMND_CFGXOR "|" D_CMND_CPUCHECK "|" D_CMND_EXCEPTION "|" D_CMND_FREEMEM "|" D_CMND_RTCDUMP "|" D_CMND_HELP;
uint32_t CPU_loops = 0;
uint32_t CPU_last_millis = 0;
uint32_t CPU_last_loop_time = 0;
uint8_t CPU_load_check = CPU_LOAD_CHECK;
uint8_t CPU_load_check = 0;
uint8_t CPU_show_freemem = 0;
/*******************************************************************************************/
@ -117,41 +122,6 @@ Decoding 14 results
}
}
/*******************************************************************************************/
void RtcSettingsDump()
{
#define CFG_COLS 16
uint16_t idx;
uint16_t maxrow;
uint16_t row;
uint16_t col;
uint8_t *buffer = (uint8_t *) &RtcSettings;
maxrow = ((sizeof(RTCMEM)+CFG_COLS)/CFG_COLS);
for (row = 0; row < maxrow; row++) {
idx = row * CFG_COLS;
snprintf_P(log_data, sizeof(log_data), PSTR("%03X:"), idx);
for (col = 0; col < CFG_COLS; col++) {
if (!(col%4)) {
snprintf_P(log_data, sizeof(log_data), PSTR("%s "), log_data);
}
snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X"), log_data, buffer[idx + col]);
}
snprintf_P(log_data, sizeof(log_data), PSTR("%s |"), log_data);
for (col = 0; col < CFG_COLS; col++) {
// if (!(col%4)) {
// snprintf_P(log_data, sizeof(log_data), PSTR("%s "), log_data);
// }
snprintf_P(log_data, sizeof(log_data), PSTR("%s%c"), log_data, ((buffer[idx + col] > 0x20) && (buffer[idx + col] < 0x7F)) ? (char)buffer[idx + col] : ' ');
}
snprintf_P(log_data, sizeof(log_data), PSTR("%s|"), log_data);
AddLog(LOG_LEVEL_INFO);
}
}
#endif // DEBUG_THEO
/*******************************************************************************************/
@ -224,6 +194,68 @@ void DebugFreeMem()
/*******************************************************************************************/
void DebugRtcDump(char* parms)
{
#define CFG_COLS 16
uint16_t idx;
uint16_t maxrow;
uint16_t row;
uint16_t col;
char *p;
// |<--SDK data (256 bytes)-->|<--User data (512 bytes)-->|
// 000 - 0FF: SDK
// 000 - 01B: SDK rst_info
// 100 - 2FF: User
// 280 - 283: Tasmota RtcReboot (Offset 100 (x 4bytes) - sizeof(RTCRBT) (x 4bytes))
// 290 - 2EB: Tasmota RtcSettings (Offset 100 (x 4bytes))
uint8_t buffer[768];
// ESP.rtcUserMemoryRead(0, (uint32_t*)&buffer, sizeof(buffer));
system_rtc_mem_read(0, (uint32_t*)&buffer, sizeof(buffer));
maxrow = ((sizeof(buffer)+CFG_COLS)/CFG_COLS);
uint16_t srow = strtol(parms, &p, 16) / CFG_COLS;
uint16_t mrow = strtol(p, &p, 10);
// snprintf_P(log_data, sizeof(log_data), PSTR("Cnfg: Parms %s, Start row %d, rows %d"), parms, srow, mrow);
// AddLog(LOG_LEVEL_DEBUG);
if (0 == mrow) { // Default only 8 lines
mrow = 8;
}
if (srow > maxrow) {
srow = maxrow - mrow;
}
if (mrow < (maxrow - srow)) {
maxrow = srow + mrow;
}
for (row = srow; row < maxrow; row++) {
idx = row * CFG_COLS;
snprintf_P(log_data, sizeof(log_data), PSTR("%03X:"), idx);
for (col = 0; col < CFG_COLS; col++) {
if (!(col%4)) {
snprintf_P(log_data, sizeof(log_data), PSTR("%s "), log_data);
}
snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X"), log_data, buffer[idx + col]);
}
snprintf_P(log_data, sizeof(log_data), PSTR("%s |"), log_data);
for (col = 0; col < CFG_COLS; col++) {
// if (!(col%4)) {
// snprintf_P(log_data, sizeof(log_data), PSTR("%s "), log_data);
// }
snprintf_P(log_data, sizeof(log_data), PSTR("%s%c"), log_data, ((buffer[idx + col] > 0x20) && (buffer[idx + col] < 0x7F)) ? (char)buffer[idx + col] : ' ');
}
snprintf_P(log_data, sizeof(log_data), PSTR("%s|"), log_data);
AddLog(LOG_LEVEL_INFO);
}
}
/*******************************************************************************************/
void DebugCfgDump(char* parms)
{
#define CFG_COLS 16
@ -322,6 +354,53 @@ void DebugCfgPoke(char* parms)
AddLog(LOG_LEVEL_INFO);
}
void DebugCfgShow(uint8_t more)
{
uint8_t *SetAddr;
SetAddr = (uint8_t *)&Settings;
snprintf_P(log_data, sizeof(log_data), PSTR("%03X: Hostname (%d) [%s]"), (uint8_t *)&Settings.hostname - SetAddr, sizeof(Settings.hostname)-1, Settings.hostname);
AddLog(LOG_LEVEL_INFO);
snprintf_P(log_data, sizeof(log_data), PSTR("%03X: SSids (%d) [%s], [%s]"), (uint8_t *)&Settings.sta_ssid - SetAddr, sizeof(Settings.sta_ssid[0])-1, Settings.sta_ssid[0], Settings.sta_ssid[1]);
AddLog(LOG_LEVEL_INFO);
snprintf_P(log_data, sizeof(log_data), PSTR("%03X: Friendlynames (%d) [%s], [%s], [%s], [%s]"), (uint8_t *)&Settings.friendlyname - SetAddr, sizeof(Settings.friendlyname[0])-1, Settings.friendlyname[0], Settings.friendlyname[1], Settings.friendlyname[2], Settings.friendlyname[3]);
AddLog(LOG_LEVEL_INFO);
snprintf_P(log_data, sizeof(log_data), PSTR("%03X: OTA Url (%d) [%s]"), (uint8_t *)&Settings.ota_url - SetAddr, sizeof(Settings.ota_url)-1, Settings.ota_url);
AddLog(LOG_LEVEL_INFO);
snprintf_P(log_data, sizeof(log_data), PSTR("%03X: StateText (%d) [%s], [%s], [%s], [%s]"), (uint8_t *)&Settings.state_text - SetAddr, sizeof(Settings.state_text[0])-1, Settings.state_text[0], Settings.state_text[1], Settings.state_text[2], Settings.state_text[3]);
AddLog(LOG_LEVEL_INFO);
snprintf_P(log_data, sizeof(log_data), PSTR("%03X: Syslog Host (%d) [%s]"), (uint8_t *)&Settings.syslog_host - SetAddr, sizeof(Settings.syslog_host)-1, Settings.syslog_host);
AddLog(LOG_LEVEL_INFO);
snprintf_P(log_data, sizeof(log_data), PSTR("%03X: NTP Servers (%d) [%s], [%s], [%s]"), (uint8_t *)&Settings.ntp_server - SetAddr, sizeof(Settings.ntp_server[0])-1, Settings.ntp_server[0], Settings.ntp_server[1], Settings.ntp_server[2]);
AddLog(LOG_LEVEL_INFO);
snprintf_P(log_data, sizeof(log_data), PSTR("%03X: MQTT Host (%d) [%s]"), (uint8_t *)&Settings.mqtt_host - SetAddr, sizeof(Settings.mqtt_host)-1, Settings.mqtt_host);
AddLog(LOG_LEVEL_INFO);
snprintf_P(log_data, sizeof(log_data), PSTR("%03X: MQTT Client (%d) [%s]"), (uint8_t *)&Settings.mqtt_client - SetAddr, sizeof(Settings.mqtt_client)-1, Settings.mqtt_client);
AddLog(LOG_LEVEL_INFO);
snprintf_P(log_data, sizeof(log_data), PSTR("%03X: MQTT User (%d) [%s]"), (uint8_t *)&Settings.mqtt_user - SetAddr, sizeof(Settings.mqtt_user)-1, Settings.mqtt_user);
AddLog(LOG_LEVEL_INFO);
snprintf_P(log_data, sizeof(log_data), PSTR("%03X: MQTT FullTopic (%d) [%s]"), (uint8_t *)&Settings.mqtt_fulltopic - SetAddr, sizeof(Settings.mqtt_fulltopic)-1, Settings.mqtt_fulltopic);
AddLog(LOG_LEVEL_INFO);
snprintf_P(log_data, sizeof(log_data), PSTR("%03X: MQTT Topic (%d) [%s]"), (uint8_t *)&Settings.mqtt_topic - SetAddr, sizeof(Settings.mqtt_topic)-1, Settings.mqtt_topic);
AddLog(LOG_LEVEL_INFO);
snprintf_P(log_data, sizeof(log_data), PSTR("%03X: MQTT GroupTopic (%d) [%s]"), (uint8_t *)&Settings.mqtt_grptopic - SetAddr, sizeof(Settings.mqtt_grptopic)-1, Settings.mqtt_grptopic);
AddLog(LOG_LEVEL_INFO);
snprintf_P(log_data, sizeof(log_data), PSTR("%03X: MQTT ButtonTopic (%d) [%s]"), (uint8_t *)&Settings.button_topic - SetAddr, sizeof(Settings.button_topic)-1, Settings.button_topic);
AddLog(LOG_LEVEL_INFO);
snprintf_P(log_data, sizeof(log_data), PSTR("%03X: MQTT SwitchTopic (%d) [%s]"), (uint8_t *)&Settings.switch_topic - SetAddr, sizeof(Settings.switch_topic)-1, Settings.switch_topic);
AddLog(LOG_LEVEL_INFO);
snprintf_P(log_data, sizeof(log_data), PSTR("%03X: MQTT Prefixes (%d) [%s], [%s], [%s]"), (uint8_t *)&Settings.mqtt_prefix - SetAddr, sizeof(Settings.mqtt_prefix[0])-1, Settings.mqtt_prefix[0], Settings.mqtt_prefix[1], Settings.mqtt_prefix[2]);
AddLog(LOG_LEVEL_INFO);
if (17 == more) {
snprintf_P(log_data, sizeof(log_data), PSTR("%03X: AP Passwords (%d) [%s], [%s]"), (uint8_t *)&Settings.sta_pwd - SetAddr, sizeof(Settings.sta_pwd[0])-1, Settings.sta_pwd[0], Settings.sta_pwd[1]);
AddLog(LOG_LEVEL_INFO);
snprintf_P(log_data, sizeof(log_data), PSTR("%03X: MQTT Password (%d) [%s]"), (uint8_t *)&Settings.mqtt_pwd - SetAddr, sizeof(Settings.mqtt_pwd)-1, Settings.mqtt_pwd);
AddLog(LOG_LEVEL_INFO);
snprintf_P(log_data, sizeof(log_data), PSTR("%03X: Web Password (%d) [%s]"), (uint8_t *)&Settings.web_password - SetAddr, sizeof(Settings.web_password)-1, Settings.web_password);
AddLog(LOG_LEVEL_INFO);
}
}
/*******************************************************************************************/
boolean DebugCommand()
@ -333,6 +412,15 @@ boolean DebugCommand()
if (-1 == command_code) {
serviced = false; // Unknown command
}
else if (CMND_HELP == command_code) {
snprintf_P(log_data, sizeof(log_data), kDebugCommands);
AddLog(LOG_LEVEL_INFO);
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_DONE);
}
else if (CMND_RTCDUMP == command_code) {
DebugRtcDump(XdrvMailbox.data);
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_DONE);
}
else if (CMND_CFGDUMP == command_code) {
DebugCfgDump(XdrvMailbox.data);
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_DONE);
@ -345,6 +433,10 @@ boolean DebugCommand()
DebugCfgPoke(XdrvMailbox.data);
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_DONE);
}
else if (CMND_CFGSHOW == command_code) {
DebugCfgShow(XdrvMailbox.payload);
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_SVALUE, command, D_JSON_DONE);
}
#ifdef USE_WEBSERVER
else if (CMND_CFGXOR == command_code) {
if (XdrvMailbox.data_len > 0) {
@ -366,6 +458,12 @@ boolean DebugCommand()
}
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, CPU_load_check);
}
else if (CMND_FREEMEM == command_code) {
if (XdrvMailbox.data_len > 0) {
CPU_show_freemem = XdrvMailbox.payload;
}
snprintf_P(mqtt_data, sizeof(mqtt_data), S_JSON_COMMAND_NVALUE, command, CPU_show_freemem);
}
else serviced = false; // Unknown command
return serviced;
@ -392,7 +490,7 @@ boolean Xdrv99(byte function)
result = DebugCommand();
break;
case FUNC_FREE_MEM:
DebugFreeMem();
if (CPU_show_freemem) { DebugFreeMem(); }
break;
}
return result;

View File

@ -153,14 +153,6 @@ boolean XdrvCommand(uint8_t grpflg, char *type, uint16_t index, char *dataBuf, u
return XdrvCall(FUNC_COMMAND);
}
void XdrvSetPower(power_t mpower)
{
// XdrvMailbox.valid = 1;
XdrvMailbox.index = mpower;
XdrvCall(FUNC_SET_POWER);
}
boolean XdrvMqttData(char *topicBuf, uint16_t stopicBuf, char *dataBuf, uint16_t sdataBuf)
{
XdrvMailbox.index = stopicBuf;

View File

@ -227,22 +227,19 @@ boolean HlwCommand()
{
boolean serviced = true;
if ((CMND_POWERCAL == energy_command_code) || (CMND_VOLTAGECAL == energy_command_code) || (CMND_CURRENTCAL == energy_command_code)) {
}
else if (CMND_POWERSET == energy_command_code) {
if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 3601) && hlw_cf_pulse_length) {
Settings.energy_power_calibration = (XdrvMailbox.payload * 10 * hlw_cf_pulse_length) / hlw_power_ratio;
if (CMND_POWERSET == energy_command_code) {
if (XdrvMailbox.data_len && hlw_cf_pulse_length) {
Settings.energy_power_calibration = ((unsigned long)(CharToDouble(XdrvMailbox.data) * 10) * hlw_cf_pulse_length) / hlw_power_ratio;
}
}
else if (CMND_VOLTAGESET == energy_command_code) {
if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 501) && hlw_cf1_voltage_pulse_length) {
Settings.energy_voltage_calibration = (XdrvMailbox.payload * 10 * hlw_cf1_voltage_pulse_length) / hlw_voltage_ratio;
if (XdrvMailbox.data_len && hlw_cf1_voltage_pulse_length) {
Settings.energy_voltage_calibration = ((unsigned long)(CharToDouble(XdrvMailbox.data) * 10) * hlw_cf1_voltage_pulse_length) / hlw_voltage_ratio;
}
}
else if (CMND_CURRENTSET == energy_command_code) {
if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 16001) && hlw_cf1_current_pulse_length) {
Settings.energy_current_calibration = (XdrvMailbox.payload * hlw_cf1_current_pulse_length) / hlw_current_ratio;
if (XdrvMailbox.data_len && hlw_cf1_current_pulse_length) {
Settings.energy_current_calibration = ((unsigned long)(CharToDouble(XdrvMailbox.data)) * hlw_cf1_current_pulse_length) / hlw_current_ratio;
}
}
else serviced = false; // Unknown command

View File

@ -27,6 +27,8 @@
#define XNRG_02 2
#define CSE_MAX_INVALID_POWER 128 // Number of invalid power receipts before deciding active power is zero
#define CSE_NOT_CALIBRATED 0xAA
#define CSE_PULSES_NOT_INITIALIZED -1
@ -42,10 +44,12 @@ long power_cycle = 0;
unsigned long power_cycle_first = 0;
long cf_pulses = 0;
long cf_pulses_last_time = CSE_PULSES_NOT_INITIALIZED;
uint8_t cse_power_invalid = CSE_MAX_INVALID_POWER;
void CseReceived()
{
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
// 55 5A 02 F7 60 00 03 5A 00 40 10 04 8B 9F 51 A6 58 18 72 75 61 AC A1 30 - Power not valid (load below 5W)
// 55 5A 02 F7 60 00 03 AB 00 40 10 02 60 5D 51 A6 58 03 E9 EF 71 0B 7A 36
// Hd Id VCal---- Voltage- ICal---- Current- PCal---- Power--- Ad CF--- Ck
@ -89,10 +93,11 @@ void CseReceived()
energy_voltage = (float)(Settings.energy_voltage_calibration * CSE_UREF) / (float)voltage_cycle;
}
if (adjustement & 0x10) { // Power valid
cse_power_invalid = 0;
if ((header & 0xF2) == 0xF2) { // Power cycle exceeds range
energy_power = 0;
} else {
if (0 == power_cycle_first) power_cycle_first = power_cycle; // Skip first incomplete power_cycle
if (0 == power_cycle_first) { power_cycle_first = power_cycle; } // Skip first incomplete power_cycle
if (power_cycle_first != power_cycle) {
power_cycle_first = -1;
energy_power = (float)(Settings.energy_power_calibration * CSE_PREF) / (float)power_cycle;
@ -101,8 +106,12 @@ void CseReceived()
}
}
} else {
power_cycle_first = 0;
energy_power = 0; // Powered on but no load
if (cse_power_invalid < CSE_MAX_INVALID_POWER) { // Allow measurements down to about 1W
cse_power_invalid++;
} else {
power_cycle_first = 0;
energy_power = 0; // Powered on but no load
}
}
if (adjustement & 0x20) { // Current valid
if (0 == energy_power) {
@ -195,22 +204,19 @@ boolean CseCommand()
{
boolean serviced = true;
if ((CMND_POWERCAL == energy_command_code) || (CMND_VOLTAGECAL == energy_command_code) || (CMND_CURRENTCAL == energy_command_code)) {
}
else if (CMND_POWERSET == energy_command_code) {
if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 3601) && power_cycle) {
Settings.energy_power_calibration = (XdrvMailbox.payload * power_cycle) / CSE_PREF;
if (CMND_POWERSET == energy_command_code) {
if (XdrvMailbox.data_len && power_cycle) {
Settings.energy_power_calibration = ((unsigned long)CharToDouble(XdrvMailbox.data) * power_cycle) / CSE_PREF;
}
}
else if (CMND_VOLTAGESET == energy_command_code) {
if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 501) && voltage_cycle) {
Settings.energy_voltage_calibration = (XdrvMailbox.payload * voltage_cycle) / CSE_UREF;
if (XdrvMailbox.data_len && voltage_cycle) {
Settings.energy_voltage_calibration = ((unsigned long)CharToDouble(XdrvMailbox.data) * voltage_cycle) / CSE_UREF;
}
}
else if (CMND_CURRENTSET == energy_command_code) {
if ((XdrvMailbox.payload > 0) && (XdrvMailbox.payload < 16001) && current_cycle) {
Settings.energy_current_calibration = (XdrvMailbox.payload * current_cycle) / 1000;
if (XdrvMailbox.data_len && current_cycle) {
Settings.energy_current_calibration = ((unsigned long)CharToDouble(XdrvMailbox.data) * current_cycle) / 1000;
}
}
else serviced = false; // Unknown command

View File

@ -0,0 +1,647 @@
/*
xnrg_04_mcp39f501.ino - MCP39F501 energy sensor support for Sonoff-Tasmota
Copyright (C) 2018 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/>.
*/
#ifdef USE_ENERGY_SENSOR
#ifdef USE_MCP39F501
/*********************************************************************************************\
* MCP39F501 - Energy (Shelly 2)
*
* Based on datasheet from https://www.microchip.com/wwwproducts/en/MCP39F501
* and https://github.com/OLIMEX/olimex-iot-firmware-esp8266/blob/7a7f9bb56d4b72770dba8d0f18eaa9d956dd0baf/olimex/user/modules/mod_emtr.c
\*********************************************************************************************/
#define XNRG_04 4
#define MCP_TIMEOUT 4
#define MCP_CALIBRATION_TIMEOUT 2
#define MCP_CALIBRATE_POWER 0x001
#define MCP_CALIBRATE_VOLTAGE 0x002
#define MCP_CALIBRATE_CURRENT 0x004
#define MCP_CALIBRATE_FREQUENCY 0x008
#define MCP_SINGLE_WIRE_FLAG 0x100
#define MCP_START_FRAME 0xA5
#define MCP_ACK_FRAME 0x06
#define MCP_ERROR_NAK 0x15
#define MCP_ERROR_CRC 0x51
#define MCP_SINGLE_WIRE 0xAB
#define MCP_SET_ADDRESS 0x41
#define MCP_READ 0x4E
#define MCP_READ_16 0x52
#define MCP_READ_32 0x44
#define MCP_WRITE 0x4D
#define MCP_WRITE_16 0x57
#define MCP_WRITE_32 0x45
#define MCP_SAVE_REGISTERS 0x53
#define MCP_CALIBRATION_BASE 0x0028
#define MCP_CALIBRATION_LEN 52
#define MCP_FREQUENCY_REF_BASE 0x0094
#define MCP_FREQUENCY_GAIN_BASE 0x00AE
#define MCP_FREQUENCY_LEN 4
typedef struct mcp_cal_registers_type {
uint16_t gain_current_rms;
uint16_t gain_voltage_rms;
uint16_t gain_active_power;
uint16_t gain_reactive_power;
sint32_t offset_current_rms;
sint32_t offset_active_power;
sint32_t offset_reactive_power;
sint16_t dc_offset_current;
sint16_t phase_compensation;
uint16_t apparent_power_divisor;
uint32_t system_configuration;
uint16_t dio_configuration;
uint32_t range;
uint32_t calibration_current;
uint16_t calibration_voltage;
uint32_t calibration_active_power;
uint32_t calibration_reactive_power;
uint16_t accumulation_interval;
} mcp_cal_registers_type;
unsigned long mcp_kWhcounter = 0;
uint32_t mcp_system_configuration = 0x03000000;
uint32_t mcp_active_power;
//uint32_t mcp_reactive_power;
//uint32_t mcp_apparent_power;
uint32_t mcp_current_rms;
uint16_t mcp_voltage_rms;
uint16_t mcp_line_frequency;
//sint16_t mcp_power_factor;
uint8_t mcp_address = 0;
uint8_t mcp_calibration_active = 0;
uint8_t mcp_init = 0;
uint8_t mcp_timeout = 0;
uint8_t mcp_calibrate = 0;
/*********************************************************************************************\
* Olimex tools
* https://github.com/OLIMEX/olimex-iot-firmware-esp8266/blob/7a7f9bb56d4b72770dba8d0f18eaa9d956dd0baf/olimex/user/modules/mod_emtr.c
\*********************************************************************************************/
uint8_t McpChecksum(uint8_t *data)
{
uint8_t checksum = 0;
uint8_t offset = 0;
uint8_t len = data[1] -1;
for (byte i = offset; i < len; i++) { checksum += data[i]; }
return checksum;
}
unsigned long McpExtractInt(char *data, uint8_t offset, uint8_t size)
{
unsigned long result = 0;
unsigned long pow = 1;
for (byte i = 0; i < size; i++) {
result = result + (uint8_t)data[offset + i] * pow;
pow = pow * 256;
}
return result;
}
void McpSetInt(unsigned long value, uint8_t *data, uint8_t offset, size_t size)
{
for (byte i = 0; i < size; i++) {
data[offset + i] = ((value >> (i * 8)) & 0xFF);
}
}
void McpSend(uint8_t *data)
{
if (mcp_timeout) { return; }
mcp_timeout = MCP_TIMEOUT;
data[0] = MCP_START_FRAME;
data[data[1] -1] = McpChecksum(data);
// AddLogSerial(LOG_LEVEL_DEBUG_MORE, data, data[1]);
for (byte i = 0; i < data[1]; i++) {
Serial.write(data[i]);
}
}
/********************************************************************************************/
void McpGetAddress(void)
{
uint8_t data[] = { MCP_START_FRAME, 7, MCP_SET_ADDRESS, 0x00, 0x26, MCP_READ_16, 0x00 };
McpSend(data);
}
void McpAddressReceive(void)
{
// 06 05 004D 58
mcp_address = serial_in_buffer[3];
}
/********************************************************************************************/
void McpGetCalibration(void)
{
if (mcp_calibration_active) { return; }
mcp_calibration_active = MCP_CALIBRATION_TIMEOUT;
uint8_t data[] = { MCP_START_FRAME, 8, MCP_SET_ADDRESS, (MCP_CALIBRATION_BASE >> 8) & 0xFF, MCP_CALIBRATION_BASE & 0xFF, MCP_READ, MCP_CALIBRATION_LEN, 0x00 };
McpSend(data);
}
void McpParseCalibration(void)
{
bool action = false;
mcp_cal_registers_type cal_registers;
// 06 37 C882 B6AD 0781 9273 06000000 00000000 00000000 0000 D3FF 0300 00000003 9204 120C1300 204E0000 9808 E0AB0000 D9940000 0200 24
cal_registers.gain_current_rms = McpExtractInt(serial_in_buffer, 2, 2);
cal_registers.gain_voltage_rms = McpExtractInt(serial_in_buffer, 4, 2);
cal_registers.gain_active_power = McpExtractInt(serial_in_buffer, 6, 2);
cal_registers.gain_reactive_power = McpExtractInt(serial_in_buffer, 8, 2);
cal_registers.offset_current_rms = McpExtractInt(serial_in_buffer, 10, 4);
cal_registers.offset_active_power = McpExtractInt(serial_in_buffer, 14, 4);
cal_registers.offset_reactive_power = McpExtractInt(serial_in_buffer, 18, 4);
cal_registers.dc_offset_current = McpExtractInt(serial_in_buffer, 22, 2);
cal_registers.phase_compensation = McpExtractInt(serial_in_buffer, 24, 2);
cal_registers.apparent_power_divisor = McpExtractInt(serial_in_buffer, 26, 2);
cal_registers.system_configuration = McpExtractInt(serial_in_buffer, 28, 4);
cal_registers.dio_configuration = McpExtractInt(serial_in_buffer, 32, 2);
cal_registers.range = McpExtractInt(serial_in_buffer, 34, 4);
cal_registers.calibration_current = McpExtractInt(serial_in_buffer, 38, 4);
cal_registers.calibration_voltage = McpExtractInt(serial_in_buffer, 42, 2);
cal_registers.calibration_active_power = McpExtractInt(serial_in_buffer, 44, 4);
cal_registers.calibration_reactive_power = McpExtractInt(serial_in_buffer, 48, 4);
cal_registers.accumulation_interval = McpExtractInt(serial_in_buffer, 52, 2);
if (mcp_calibrate & MCP_CALIBRATE_POWER) {
cal_registers.calibration_active_power = Settings.energy_power_calibration;
if (McpCalibrationCalc(&cal_registers, 16)) { action = true; }
}
if (mcp_calibrate & MCP_CALIBRATE_VOLTAGE) {
cal_registers.calibration_voltage = Settings.energy_voltage_calibration;
if (McpCalibrationCalc(&cal_registers, 0)) { action = true; }
}
if (mcp_calibrate & MCP_CALIBRATE_CURRENT) {
cal_registers.calibration_current = Settings.energy_current_calibration;
if (McpCalibrationCalc(&cal_registers, 8)) { action = true; }
}
mcp_timeout = 0;
if (action) { McpSetCalibration(&cal_registers); }
mcp_calibrate = 0;
Settings.energy_power_calibration = cal_registers.calibration_active_power;
Settings.energy_voltage_calibration = cal_registers.calibration_voltage;
Settings.energy_current_calibration = cal_registers.calibration_current;
mcp_system_configuration = cal_registers.system_configuration;
if (mcp_system_configuration & MCP_SINGLE_WIRE_FLAG) {
mcp_system_configuration &= ~MCP_SINGLE_WIRE_FLAG; // Reset SingleWire flag
McpSetSystemConfiguration(2);
}
}
bool McpCalibrationCalc(struct mcp_cal_registers_type *cal_registers, uint8_t range_shift)
{
uint32_t measured;
uint32_t expected;
uint16_t *gain;
uint32_t new_gain;
if (range_shift == 0) {
measured = mcp_voltage_rms;
expected = cal_registers->calibration_voltage;
gain = &(cal_registers->gain_voltage_rms);
} else if (range_shift == 8) {
measured = mcp_current_rms;
expected = cal_registers->calibration_current;
gain = &(cal_registers->gain_current_rms);
} else if (range_shift == 16) {
measured = mcp_active_power;
expected = cal_registers->calibration_active_power;
gain = &(cal_registers->gain_active_power);
} else {
return false;
}
if (measured == 0) {
return false;
}
uint32_t range = (cal_registers->range >> range_shift) & 0xFF;
calc:
new_gain = (*gain) * expected / measured;
if (new_gain < 25000) {
range++;
if (measured > 6) {
measured = measured / 2;
goto calc;
}
}
if (new_gain > 55000) {
range--;
measured = measured * 2;
goto calc;
}
*gain = new_gain;
uint32_t old_range = (cal_registers->range >> range_shift) & 0xFF;
cal_registers->range = cal_registers->range ^ (old_range << range_shift);
cal_registers->range = cal_registers->range | (range << range_shift);
return true;
}
/*
void McpCalibrationReactivePower(void)
{
cal_registers.gain_reactive_power = cal_registers.gain_reactive_power * cal_registers.calibration_reactive_power / mcp_reactive_power;
}
*/
void McpSetCalibration(struct mcp_cal_registers_type *cal_registers)
{
uint8_t data[7 + MCP_CALIBRATION_LEN + 2 + 1];
data[1] = sizeof(data);
data[2] = MCP_SET_ADDRESS; // Set address pointer
data[3] = (MCP_CALIBRATION_BASE >> 8) & 0xFF; // address
data[4] = (MCP_CALIBRATION_BASE >> 0) & 0xFF; // address
data[5] = MCP_WRITE; // Write N bytes
data[6] = MCP_CALIBRATION_LEN;
McpSetInt(cal_registers->gain_current_rms, data, 0+7, 2);
McpSetInt(cal_registers->gain_voltage_rms, data, 2+7, 2);
McpSetInt(cal_registers->gain_active_power, data, 4+7, 2);
McpSetInt(cal_registers->gain_reactive_power, data, 6+7, 2);
McpSetInt(cal_registers->offset_current_rms, data, 8+7, 4);
McpSetInt(cal_registers->offset_active_power, data, 12+7, 4);
McpSetInt(cal_registers->offset_reactive_power, data, 16+7, 4);
McpSetInt(cal_registers->dc_offset_current, data, 20+7, 2);
McpSetInt(cal_registers->phase_compensation, data, 22+7, 2);
McpSetInt(cal_registers->apparent_power_divisor, data, 24+7, 2);
McpSetInt(cal_registers->system_configuration, data, 26+7, 4);
McpSetInt(cal_registers->dio_configuration, data, 30+7, 2);
McpSetInt(cal_registers->range, data, 32+7, 4);
McpSetInt(cal_registers->calibration_current, data, 36+7, 4);
McpSetInt(cal_registers->calibration_voltage, data, 40+7, 2);
McpSetInt(cal_registers->calibration_active_power, data, 42+7, 4);
McpSetInt(cal_registers->calibration_reactive_power, data, 46+7, 4);
McpSetInt(cal_registers->accumulation_interval, data, 50+7, 2);
data[MCP_CALIBRATION_LEN+7] = MCP_SAVE_REGISTERS; // Save registers to flash
data[MCP_CALIBRATION_LEN+8] = mcp_address; // Device address
McpSend(data);
}
/********************************************************************************************/
void McpSetSystemConfiguration(uint16 interval)
{
// A5 11 41 00 42 45 03 00 01 00 41 00 5A 57 00 06 7A
uint8_t data[17];
data[ 1] = sizeof(data);
data[ 2] = MCP_SET_ADDRESS; // Set address pointer
data[ 3] = 0x00; // address
data[ 4] = 0x42; // address
data[ 5] = MCP_WRITE_32; // Write 4 bytes
data[ 6] = (mcp_system_configuration >> 24) & 0xFF; // system_configuration
data[ 7] = (mcp_system_configuration >> 16) & 0xFF; // system_configuration
data[ 8] = (mcp_system_configuration >> 8) & 0xFF; // system_configuration
data[ 9] = (mcp_system_configuration >> 0) & 0xFF; // system_configuration
data[10] = MCP_SET_ADDRESS; // Set address pointer
data[11] = 0x00; // address
data[12] = 0x5A; // address
data[13] = MCP_WRITE_16; // Write 2 bytes
data[14] = (interval >> 8) & 0xFF; // interval
data[15] = (interval >> 0) & 0xFF; // interval
McpSend(data);
}
/********************************************************************************************/
void McpGetFrequency(void)
{
if (mcp_calibration_active) { return; }
mcp_calibration_active = MCP_CALIBRATION_TIMEOUT;
uint8_t data[] = { MCP_START_FRAME, 11, MCP_SET_ADDRESS, (MCP_FREQUENCY_REF_BASE >> 8) & 0xFF, MCP_FREQUENCY_REF_BASE & 0xFF, MCP_READ_16,
MCP_SET_ADDRESS, (MCP_FREQUENCY_GAIN_BASE >> 8) & 0xFF, MCP_FREQUENCY_GAIN_BASE & 0xFF, MCP_READ_16, 0x00 };
McpSend(data);
}
void McpParseFrequency(void)
{
// 06 07 C350 8000 A0
uint16_t line_frequency_ref = serial_in_buffer[2] * 256 + serial_in_buffer[3];
uint16_t gain_line_frequency = serial_in_buffer[4] * 256 + serial_in_buffer[5];
if (mcp_calibrate & MCP_CALIBRATE_FREQUENCY) {
line_frequency_ref = Settings.energy_frequency_calibration;
if ((0xFFFF == mcp_line_frequency) || (0 == gain_line_frequency)) { // Reset values to 50Hz
mcp_line_frequency = 50000;
gain_line_frequency = 0x8000;
}
gain_line_frequency = gain_line_frequency * line_frequency_ref / mcp_line_frequency;
mcp_timeout = 0;
McpSetFrequency(line_frequency_ref, gain_line_frequency);
}
Settings.energy_frequency_calibration = line_frequency_ref;
mcp_calibrate = 0;
}
void McpSetFrequency(uint16_t line_frequency_ref, uint16_t gain_line_frequency)
{
// A5 11 41 00 94 57 C3 B4 41 00 AE 57 7E 46 53 4D 03
uint8_t data[17];
data[ 1] = sizeof(data);
data[ 2] = MCP_SET_ADDRESS; // Set address pointer
data[ 3] = (MCP_FREQUENCY_REF_BASE >> 8) & 0xFF; // address
data[ 4] = (MCP_FREQUENCY_REF_BASE >> 0) & 0xFF; // address
data[ 5] = MCP_WRITE_16; // Write register
data[ 6] = (line_frequency_ref >> 8) & 0xFF; // line_frequency_ref high
data[ 7] = (line_frequency_ref >> 0) & 0xFF; // line_frequency_ref low
data[ 8] = MCP_SET_ADDRESS; // Set address pointer
data[ 9] = (MCP_FREQUENCY_GAIN_BASE >> 8) & 0xFF; // address
data[10] = (MCP_FREQUENCY_GAIN_BASE >> 0) & 0xFF; // address
data[11] = MCP_WRITE_16; // Write register
data[12] = (gain_line_frequency >> 8) & 0xFF; // gain_line_frequency high
data[13] = (gain_line_frequency >> 0) & 0xFF; // gain_line_frequency low
data[14] = MCP_SAVE_REGISTERS; // Save registers to flash
data[15] = mcp_address; // Device address
McpSend(data);
}
/********************************************************************************************/
void McpGetData(void)
{
uint8_t data[] = { MCP_START_FRAME, 8, MCP_SET_ADDRESS, 0x00, 0x04, MCP_READ, 22, 0x00 };
McpSend(data);
}
void McpParseData(void)
{
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
// 06 19 61 06 00 00 FE 08 9B 0E 00 00 0B 00 00 00 97 0E 00 00 FF 7F 0C C6 35
// 06 19 CE 18 00 00 F2 08 3A 38 00 00 66 00 00 00 93 38 00 00 36 7F 9A C6 B7
// Ak Ln Current---- Volt- ActivePower ReActivePow ApparentPow Factr Frequ Ck
mcp_current_rms = McpExtractInt(serial_in_buffer, 2, 4);
mcp_voltage_rms = McpExtractInt(serial_in_buffer, 6, 2);
mcp_active_power = McpExtractInt(serial_in_buffer, 8, 4);
// mcp_reactive_power = McpExtractInt(serial_in_buffer, 12, 4);
// mcp_power_factor = McpExtractInt(serial_in_buffer, 20, 2);
mcp_line_frequency = McpExtractInt(serial_in_buffer, 22, 2);
if (energy_power_on) { // Powered on
energy_frequency = (float)mcp_line_frequency / 1000;
energy_voltage = (float)mcp_voltage_rms / 10;
energy_power = (float)mcp_active_power / 100;
if (0 == energy_power) {
energy_current = 0;
} else {
energy_current = (float)mcp_current_rms / 10000;
}
} else { // Powered off
energy_frequency = 0;
energy_voltage = 0;
energy_power = 0;
energy_current = 0;
}
}
/********************************************************************************************/
bool McpSerialInput(void)
{
serial_in_buffer[serial_in_byte_counter++] = serial_in_byte;
unsigned long start = millis();
while (millis() - start < 20) {
yield();
if (Serial.available()) {
serial_in_buffer[serial_in_byte_counter++] = Serial.read();
start = millis();
}
}
AddLogSerial(LOG_LEVEL_DEBUG_MORE);
if (1 == serial_in_byte_counter) {
if (MCP_ERROR_CRC == serial_in_buffer[0]) {
// AddLog_P(LOG_LEVEL_DEBUG, PSTR("MCP: Send " D_CHECKSUM_FAILURE));
mcp_timeout = 0;
}
else if (MCP_ERROR_NAK == serial_in_buffer[0]) {
// AddLog_P(LOG_LEVEL_DEBUG, PSTR("MCP: NAck"));
mcp_timeout = 0;
}
}
else if (MCP_ACK_FRAME == serial_in_buffer[0]) {
if (serial_in_byte_counter == serial_in_buffer[1]) {
if (McpChecksum((uint8_t *)serial_in_buffer) != serial_in_buffer[serial_in_byte_counter -1]) {
AddLog_P(LOG_LEVEL_DEBUG, PSTR("MCP: " D_CHECKSUM_FAILURE));
} else {
if (5 == serial_in_buffer[1]) { McpAddressReceive(); }
if (25 == serial_in_buffer[1]) { McpParseData(); }
if (MCP_CALIBRATION_LEN + 3 == serial_in_buffer[1]) { McpParseCalibration(); }
if (MCP_FREQUENCY_LEN + 3 == serial_in_buffer[1]) { McpParseFrequency(); }
}
}
mcp_timeout = 0;
}
else if (MCP_SINGLE_WIRE == serial_in_buffer[0]) {
mcp_timeout = 0;
}
return 1;
}
/********************************************************************************************/
void McpEverySecond(void)
{
if (mcp_active_power) {
energy_kWhtoday_delta += ((mcp_active_power * 10) / 36);
EnergyUpdateToday();
}
if (mcp_timeout) {
mcp_timeout--;
}
else if (mcp_calibration_active) {
mcp_calibration_active--;
}
else if (mcp_init) {
if (2 == mcp_init) {
McpGetCalibration(); // Get calibration parameters and disable SingleWire mode if enabled
}
else if (1 == mcp_init) {
McpGetFrequency(); // Get calibration parameter
}
mcp_init--;
}
else if (!mcp_address) {
McpGetAddress(); // Get device address for future calibration changes
}
else {
McpGetData(); // Get energy data
}
}
void McpSnsInit(void)
{
SetSeriallog(LOG_LEVEL_NONE); // Free serial interface from logging interference
digitalWrite(15, 1); // GPIO15 - MCP enable
}
void McpDrvInit(void)
{
if (!energy_flg) {
if (SHELLY2 == Settings.module) {
pinMode(15, OUTPUT);
digitalWrite(15, 0); // GPIO15 - MCP disable - Reset Delta Sigma ADC's
baudrate = 4800;
mcp_calibrate = 0;
mcp_timeout = 2; // Initial wait
mcp_init = 2; // Initial setup steps
energy_calc_power_factor = 1; // Calculate power factor from data
energy_flg = XNRG_04;
}
}
}
boolean McpCommand(void)
{
boolean serviced = true;
unsigned long value = 0;
if (CMND_POWERSET == energy_command_code) {
if (XdrvMailbox.data_len && mcp_active_power) {
value = (unsigned long)(CharToDouble(XdrvMailbox.data) * 100);
if ((value > 100) && (value < 200000)) { // Between 1W and 2000W
Settings.energy_power_calibration = value;
mcp_calibrate |= MCP_CALIBRATE_POWER;
McpGetCalibration();
}
}
}
else if (CMND_VOLTAGESET == energy_command_code) {
if (XdrvMailbox.data_len && mcp_voltage_rms) {
value = (unsigned long)(CharToDouble(XdrvMailbox.data) * 10);
if ((value > 1000) && (value < 2600)) { // Between 100V and 260V
Settings.energy_voltage_calibration = value;
mcp_calibrate |= MCP_CALIBRATE_VOLTAGE;
McpGetCalibration();
}
}
}
else if (CMND_CURRENTSET == energy_command_code) {
if (XdrvMailbox.data_len && mcp_current_rms) {
value = (unsigned long)(CharToDouble(XdrvMailbox.data) * 10);
if ((value > 100) && (value < 80000)) { // Between 10mA and 8A
Settings.energy_current_calibration = value;
mcp_calibrate |= MCP_CALIBRATE_CURRENT;
McpGetCalibration();
}
}
}
else if (CMND_FREQUENCYSET == energy_command_code) {
if (XdrvMailbox.data_len && mcp_line_frequency) {
value = (unsigned long)(CharToDouble(XdrvMailbox.data) * 1000);
if ((value > 45000) && (value < 65000)) { // Between 45Hz and 65Hz
Settings.energy_frequency_calibration = value;
mcp_calibrate |= MCP_CALIBRATE_FREQUENCY;
McpGetFrequency();
}
}
}
else serviced = false; // Unknown command
return serviced;
}
/*********************************************************************************************\
* Interface
\*********************************************************************************************/
int Xnrg04(byte function)
{
int result = 0;
if (FUNC_PRE_INIT == function) {
McpDrvInit();
}
else if (XNRG_04 == energy_flg) {
switch (function) {
case FUNC_INIT:
McpSnsInit();
break;
case FUNC_EVERY_SECOND:
McpEverySecond();
break;
case FUNC_COMMAND:
result = McpCommand();
break;
case FUNC_SERIAL:
result = McpSerialInput();
break;
}
}
return result;
}
#endif // USE_MCP39F501
#endif // USE_ENERGY_SENSOR

View File

@ -31,17 +31,67 @@
Tsl2561 Tsl(Wire);
uint8_t tsl2561_type = 0;
uint8_t tsl2561_valid = 0;
uint32_t tsl2561_milliLux = 0;
char tsl2561_types[] = "TSL2561";
bool Tsl2561Read()
{
if (tsl2561_valid) { tsl2561_valid--; }
uint8_t id;
bool gain;
Tsl2561::exposure_t exposure;
uint16_t scaledFull, scaledIr;
uint32_t full, ir;
if (Tsl.available()) {
if (Tsl.on()) {
if (Tsl.id(id)
&& Tsl2561Util::autoGain(Tsl, gain, exposure, scaledFull, scaledIr)
&& Tsl2561Util::normalizedLuminosity(gain, exposure, full = scaledFull, ir = scaledIr)
&& Tsl2561Util::milliLux(full, ir, tsl2561_milliLux, Tsl2561::packageCS(id))) {
} else{
tsl2561_milliLux = 0;
}
}
}
tsl2561_valid = SENSOR_MAX_MISS;
return true;
}
void Tsl2561Detect()
{
if (tsl2561_type) { return; }
if (!Tsl.available()) {
Tsl.begin();
if (Tsl.available()) {
snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, "TSL2561", Tsl.address());
tsl2561_type = 1;
snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, tsl2561_types, Tsl.address());
AddLog(LOG_LEVEL_DEBUG);
}
}
}
void Tsl2561EverySecond()
{
if (90 == (uptime %100)) {
// 1mS
Tsl2561Detect();
}
else if (!(uptime %2)) { // Update every 2 seconds
// ?mS - 4Sec
if (tsl2561_type) {
if (!Tsl2561Read()) {
AddLogMissed(tsl2561_types, tsl2561_valid);
// if (!tsl2561_valid) { tsl2561_type = 0; }
}
}
}
}
#ifdef USE_WEBSERVER
const char HTTP_SNS_TSL2561[] PROGMEM =
"%s{s}TSL2561 " D_ILLUMINANCE "{m}%u.%03u " D_UNIT_LUX "{e}"; // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
@ -49,37 +99,17 @@ const char HTTP_SNS_TSL2561[] PROGMEM =
void Tsl2561Show(boolean json)
{
uint8_t id;
bool gain;
Tsl2561::exposure_t exposure;
uint16_t scaledFull, scaledIr;
uint32_t full, ir;
uint32_t milliLux;
if (Tsl.available()) {
if (Tsl.on()) {
if( Tsl.id(id)
&& Tsl2561Util::autoGain(Tsl, gain, exposure, scaledFull, scaledIr)
&& Tsl2561Util::normalizedLuminosity(gain, exposure, full = scaledFull, ir = scaledIr)
&& Tsl2561Util::milliLux(full, ir, milliLux, Tsl2561::packageCS(id))) {
// snprintf_P(log_data, sizeof(log_data), PSTR(D_ILLUMINANCE " g:%d, e:%d, f:%u, i:%u -> %u.%03u " D_UNIT_LUX),
// gain, exposure, full, ir, milliLux/1000, milliLux%1000);
// AddLog(LOG_LEVEL_DEBUG);
if (json) {
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"TSL2561\":{\"" D_JSON_ILLUMINANCE "\":%u.%03u}"),
mqtt_data, milliLux/1000, milliLux%1000);
if (tsl2561_valid) {
if (json) {
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"TSL2561\":{\"" D_JSON_ILLUMINANCE "\":%u.%03u}"),
mqtt_data, tsl2561_milliLux / 1000, tsl2561_milliLux % 1000);
#ifdef USE_DOMOTICZ
if (0 == tele_period) DomoticzSensor(DZ_ILLUMINANCE, (milliLux+500)/1000);
if (0 == tele_period) { DomoticzSensor(DZ_ILLUMINANCE, (tsl2561_milliLux + 500) / 1000); }
#endif // USE_DOMOTICZ
#ifdef USE_WEBSERVER
} else {
snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_TSL2561, mqtt_data, milliLux/1000, milliLux%1000);
} else {
snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_TSL2561, mqtt_data, tsl2561_milliLux / 1000, tsl2561_milliLux % 1000);
#endif // USE_WEBSERVER
}
}
Tsl.off();
}
}
}
@ -96,9 +126,12 @@ boolean Xsns16(byte function)
if (i2c_flg) {
switch (function) {
case FUNC_PREP_BEFORE_TELEPERIOD:
case FUNC_INIT:
Tsl2561Detect();
break;
case FUNC_EVERY_SECOND:
Tsl2561EverySecond();
break;
case FUNC_JSON_APPEND:
Tsl2561Show(1);
break;

View File

@ -46,7 +46,7 @@ uint8_t lm75ad_type = 0;
uint8_t lm75ad_address;
uint8_t lm75ad_addresses[] = { LM75AD_ADDRESS1, LM75AD_ADDRESS2, LM75AD_ADDRESS3, LM75AD_ADDRESS4, LM75AD_ADDRESS5, LM75AD_ADDRESS6, LM75AD_ADDRESS7, LM75AD_ADDRESS8 };
void LM75ADDetect()
void LM75ADDetect(void)
{
if (lm75ad_type) { return; }
@ -64,7 +64,7 @@ void LM75ADDetect()
}
}
float LM75ADGetTemp() {
float LM75ADGetTemp(void) {
int16_t sign = 1;
uint16_t t = I2cRead16(lm75ad_address, LM75_TEMP_REGISTER);

View File

@ -31,15 +31,6 @@
#define XSNS_29 29
#define MCP230xx_ADDRESS1 0x20
#define MCP230xx_ADDRESS2 0x21
#define MCP230xx_ADDRESS3 0x22
#define MCP230xx_ADDRESS4 0x23
#define MCP230xx_ADDRESS5 0x24
#define MCP230xx_ADDRESS6 0x25
#define MCP230xx_ADDRESS7 0x26
#define MCP230xx_ADDRESS8 0x27
/*
Default register locations for MCP23008 - They change for MCP23017 in default bank mode
*/
@ -53,8 +44,6 @@ uint8_t MCP230xx_INTCAP = 0x08;
uint8_t MCP230xx_GPIO = 0x09;
uint8_t mcp230xx_type = 0;
uint8_t mcp230xx_address;
uint8_t mcp230xx_addresses[] = { MCP230xx_ADDRESS1, MCP230xx_ADDRESS2, MCP230xx_ADDRESS3, MCP230xx_ADDRESS4, MCP230xx_ADDRESS5, MCP230xx_ADDRESS6, MCP230xx_ADDRESS7, MCP230xx_ADDRESS8 };
uint8_t mcp230xx_pincount = 0;
uint8_t mcp230xx_int_en = 0;
uint8_t mcp230xx_int_prio_counter = 0;
@ -131,7 +120,7 @@ const char* IntModeTxt(uint8_t intmo) {
}
uint8_t MCP230xx_readGPIO(uint8_t port) {
return I2cRead8(mcp230xx_address, MCP230xx_GPIO + port);
return I2cRead8(USE_MCP230xx_ADDR, MCP230xx_GPIO + port);
}
void MCP230xx_ApplySettings(void) {
@ -178,11 +167,11 @@ void MCP230xx_ApplySettings(void) {
}
#endif // USE_MCP230xx_OUTPUT
}
I2cWrite8(mcp230xx_address, MCP230xx_GPPU+mcp230xx_port, reg_gppu);
I2cWrite8(mcp230xx_address, MCP230xx_GPINTEN+mcp230xx_port, reg_gpinten);
I2cWrite8(mcp230xx_address, MCP230xx_IODIR+mcp230xx_port, reg_iodir);
I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_GPPU+mcp230xx_port, reg_gppu);
I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_GPINTEN+mcp230xx_port, reg_gpinten);
I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_IODIR+mcp230xx_port, reg_iodir);
#ifdef USE_MCP230xx_OUTPUT
I2cWrite8(mcp230xx_address, MCP230xx_GPIO+mcp230xx_port, reg_portpins);
I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_GPIO+mcp230xx_port, reg_portpins);
#endif // USE_MCP230xx_OUTPUT
}
for (uint8_t idx=0;idx<mcp230xx_pincount;idx++) {
@ -192,7 +181,7 @@ void MCP230xx_ApplySettings(void) {
MCP230xx_CheckForIntCounter(); // update register on whether or not we should be counting interrupts
}
void MCP230xx_Detect()
void MCP230xx_Detect(void)
{
if (mcp230xx_type) {
return;
@ -200,34 +189,30 @@ void MCP230xx_Detect()
uint8_t buffer;
for (byte i = 0; i < sizeof(mcp230xx_addresses); i++) {
mcp230xx_address = mcp230xx_addresses[i];
I2cWrite8(mcp230xx_address, MCP230xx_IOCON, 0x80); // attempt to set bank mode - this will only work on MCP23017, so its the best way to detect the different chips 23008 vs 23017
if (I2cValidRead8(&buffer, mcp230xx_address, MCP230xx_IOCON)) {
if (0x00 == buffer) {
mcp230xx_type = 1; // We have a MCP23008
snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, "MCP23008", mcp230xx_address);
I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_IOCON, 0x80); // attempt to set bank mode - this will only work on MCP23017, so its the best way to detect the different chips 23008 vs 23017
if (I2cValidRead8(&buffer, USE_MCP230xx_ADDR, MCP230xx_IOCON)) {
if (0x00 == buffer) {
mcp230xx_type = 1; // We have a MCP23008
snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, "MCP23008", USE_MCP230xx_ADDR);
AddLog(LOG_LEVEL_DEBUG);
mcp230xx_pincount = 8;
MCP230xx_ApplySettings();
} else {
if (0x80 == buffer) {
mcp230xx_type = 2; // We have a MCP23017
snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, "MCP23017", USE_MCP230xx_ADDR);
AddLog(LOG_LEVEL_DEBUG);
mcp230xx_pincount = 8;
mcp230xx_pincount = 16;
// Reset bank mode to 0
I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_IOCON, 0x00);
// Update register locations for MCP23017
MCP230xx_GPINTEN = 0x04;
MCP230xx_GPPU = 0x0C;
MCP230xx_INTF = 0x0E;
MCP230xx_INTCAP = 0x10;
MCP230xx_GPIO = 0x12;
MCP230xx_ApplySettings();
} else {
if (0x80 == buffer) {
mcp230xx_type = 2; // We have a MCP23017
snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, "MCP23017", mcp230xx_address);
AddLog(LOG_LEVEL_DEBUG);
mcp230xx_pincount = 16;
// Reset bank mode to 0
I2cWrite8(mcp230xx_address, MCP230xx_IOCON, 0x00);
// Update register locations for MCP23017
MCP230xx_GPINTEN = 0x04;
MCP230xx_GPPU = 0x0C;
MCP230xx_INTF = 0x0E;
MCP230xx_INTCAP = 0x10;
MCP230xx_GPIO = 0x12;
MCP230xx_ApplySettings();
}
}
break;
}
}
}
@ -237,9 +222,9 @@ void MCP230xx_CheckForInterrupt(void) {
uint8_t mcp230xx_intcap = 0;
uint8_t report_int;
for (uint8_t mcp230xx_port=0;mcp230xx_port<mcp230xx_type;mcp230xx_port++) {
if (I2cValidRead8(&intf,mcp230xx_address,MCP230xx_INTF+mcp230xx_port)) {
if (I2cValidRead8(&intf,USE_MCP230xx_ADDR,MCP230xx_INTF+mcp230xx_port)) {
if (intf > 0) {
if (I2cValidRead8(&mcp230xx_intcap, mcp230xx_address, MCP230xx_INTCAP+mcp230xx_port)) {
if (I2cValidRead8(&mcp230xx_intcap, USE_MCP230xx_ADDR, MCP230xx_INTCAP+mcp230xx_port)) {
for (uint8_t intp = 0; intp < 8; intp++) {
if ((intf >> intp) & 0x01) { // we know which pin caused interrupt
report_int = 0;
@ -280,7 +265,9 @@ void MCP230xx_CheckForInterrupt(void) {
if (report_int) {
bool int_tele = false;
bool int_event = false;
unsigned long millis_since_last_int = millis() - int_millis[intp+(mcp230xx_port*8)];
unsigned long millis_now = millis();
unsigned long millis_since_last_int = millis_now - int_millis[intp+(mcp230xx_port*8)];
int_millis[intp+(mcp230xx_port*8)]=millis_now;
switch (Settings.mcp230xx_config[intp+(mcp230xx_port*8)].int_report_mode) {
case 0:
int_tele=true;
@ -297,14 +284,13 @@ void MCP230xx_CheckForInterrupt(void) {
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_TIME "\":\"%s\""), GetDateAndTime(DT_LOCAL).c_str());
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"MCP230XX_INT\":{\"D%i\":%i,\"MS\":%lu}"), mqtt_data, intp+(mcp230xx_port*8), ((mcp230xx_intcap >> intp) & 0x01),millis_since_last_int);
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s}"), mqtt_data);
MqttPublishPrefixTopic_P(RESULT_OR_STAT, mqtt_data);
MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR("MCP230XX_INT"));
}
if (int_event) {
char command[19]; // Theoretical max = 'event MCPINT_D16=1' so 18 + 1 (for the \n)
sprintf(command,"event MCPINT_D%i=%i",intp+(mcp230xx_port*8),((mcp230xx_intcap >> intp) & 0x01));
ExecuteCommand(command, SRC_RULE);
}
int_millis[intp+(mcp230xx_port*8)]=millis();
}
}
}
@ -366,7 +352,7 @@ void MCP230xx_SetOutPin(uint8_t pin,uint8_t pinstate) {
portpins ^= (1 << (pin-(port*8)));
}
}
I2cWrite8(mcp230xx_address, MCP230xx_GPIO + port, portpins);
I2cWrite8(USE_MCP230xx_ADDR, MCP230xx_GPIO + port, portpins);
if (Settings.flag.save_state) { // Firmware configured to save last known state in settings
Settings.mcp230xx_config[pin].saved_state=portpins>>(pin-(port*8))&1;
Settings.mcp230xx_config[pin+pinadd].saved_state=portpins>>(pin+pinadd-(port*8))&1;
@ -705,7 +691,7 @@ void MCP230xx_OutputTelemetry(void) {
#endif // USE_MCP230xx_OUTPUT
void MCP230xx_Interrupt_Counter_Report() {
void MCP230xx_Interrupt_Counter_Report(void) {
snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("{\"" D_JSON_TIME "\":\"%s\",\"MCP230_INTTIMER\": {"), GetDateAndTime(DT_LOCAL).c_str());
for (uint8_t pinx = 0;pinx < mcp230xx_pincount;pinx++) {
if (Settings.mcp230xx_config[pinx].int_count_en) { // Counting is enabled for this pin so we add to report

1924
tools/decode-config.py Normal file

File diff suppressed because it is too large Load Diff

View File

@ -28,12 +28,13 @@ Instructions:
and store it in file status.json
Usage:
./decode-status.py -d <hostname or IP address>
./decode-status.py -d <hostname or IP address> [-u username] [-p password]
or
./decode-status.py -f <JSON status information file>
Example:
./decode-status.py -d sonoff1
./decode-status.py -d sonoff1 -p 12345678
or
./decode-status.py -f status.json
"""
@ -42,6 +43,7 @@ import io
import os.path
import json
import pycurl
import urllib2
from sys import exit
from optparse import OptionParser
from StringIO import StringIO
@ -136,13 +138,20 @@ usage = "usage: decode-status {-d | -f} arg"
parser = OptionParser(usage)
parser.add_option("-d", "--dev", action="store", type="string",
dest="device", help="device to retrieve status from")
parser.add_option("-u", "--username", action="store", type="string",
dest="username", help="username for login", default="admin")
parser.add_option("-p", "--password", action="store", type="string",
dest="password", help="password for login", default=None)
parser.add_option("-f", "--file", metavar="FILE",
dest="jsonfile", default="status.json", help="status json file (default: status.json)")
(options, args) = parser.parse_args()
if (options.device):
buffer = StringIO()
url = str("http://{}/cm?cmnd=status%200".format(options.device))
loginstr = ""
if options.password is not None:
loginstr = "user={}&password={}&".format(urllib2.quote(options.username), urllib2.quote(options.password))
url = str("http://{}/cm?{}cmnd=status%200".format(options.device, loginstr))
c = pycurl.Curl()
c.setopt(c.URL, url)
c.setopt(c.WRITEDATA, buffer)