From c20bc70d1c37ef6a453168d4603258eb4d9b6c88 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sat, 12 Mar 2022 17:38:49 +0100 Subject: [PATCH] Add support for PCF85363 RTC as used in Shelly 3EM Add support for PCF85363 RTC as used in Shelly 3EM (#13515) --- CHANGELOG.md | 3 +- I2CDEVICES.md | 1 + RELEASENOTES.md | 3 +- tasmota/my_user_config.h | 1 + tasmota/support.ino | 10 ++ tasmota/support_features.ino | 7 +- tasmota/tasmota_configurations.h | 1 + tasmota/tasmota_configurations_ESP32.h | 2 + tasmota/xsns_96_pcf85363.ino | 161 +++++++++++++++++++++++++ tools/decode-status.py | 2 +- 10 files changed, 185 insertions(+), 6 deletions(-) create mode 100644 tasmota/xsns_96_pcf85363.ino diff --git a/CHANGELOG.md b/CHANGELOG.md index 694996602..dabd150c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,8 @@ All notable changes to this project will be documented in this file. - Full DS3231 integration and synchronisation when using UBX (=GPS), NTP or manual time - LVGL Splash screen and ``SetOption135 1`` to disable splash screen - Command ``RfTimeout 100..60000`` to disable duplicate RfReceive. Default 1000 (#15061) -- Support for Shelly 3EM (#13515) +- Support for ADE7880 3 phase energy monitor as used in Shelly 3EM (#13515) +- Support for PCF85363 RTC as used in Shelly 3EM (#13515) ### Changed - Extent number of pulsetimers from 8 to 32 (#8266) diff --git a/I2CDEVICES.md b/I2CDEVICES.md index a14badcfe..da66f9f3f 100644 --- a/I2CDEVICES.md +++ b/I2CDEVICES.md @@ -99,3 +99,4 @@ Index | Define | Driver | Device | Address(es) | Description 63 | USE_HM330X | xsns_93 | HM330X | 0x40 | Particule sensor 64 | USE_HDC2010 | xsns_94 | HDC2010 | 0x40 | Temperature and Humidity sensor 65 | USE_ADE7880 | xnrg_23 | ADE7880 | 0x38 | Energy monitor + 66 | USE_PCF85363 | xsns_99 | PCF85363 | 0x51 | Real time clock diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 7948115f5..3e01b97a8 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -112,7 +112,8 @@ The latter links can be used for OTA upgrades too like ``OtaUrl http://ota.tasmo - NeoPool commands ``NPpHMin``, ``NPpHMax``, ``NPpH``, ``NPRedox``, ``NPHydrolysis``, ``NPIonization``, ``NPChlorine`` and ``NPControl`` [#15015](https://github.com/arendst/Tasmota/issues/15015) - NeoPool system voltages display - TasmotaSerial implement ``end()`` -- Support for Shelly 3EM [#13515](https://github.com/arendst/Tasmota/issues/13515) +- Support for ADE7880 3 phase energy monitor as used in Shelly 3EM [#13515](https://github.com/arendst/Tasmota/issues/13515) +- Support for PCF85363 RTC as used in Shelly 3EM (#13515) - Full DS3231 integration and synchronisation when using UBX (=GPS), NTP or manual time - ESP32 Berry always enable rules - ESP32 Berry bootloop protection diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 67fb2ac6b..36d53ae40 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -669,6 +669,7 @@ // #define HM330X_DEFAULT_ADDRESS 0x40 // Option: change default I2C address for HM330X used in SeedSTudio Particucle Sensor // #define HM330X_WARMUP_DELAY 30 // Option: change warmup delay during which data are not read from sensor after a power up // #define HM330X_HIDE_OUT_OF_DATE false // Option: change to true to hide data from web GUI and SENSOR while sensor is asleep +// #define USE_PCF85363 // [I2cDriver66] Enable PCF85363 RTC (I2C address 0x51) (+0k7 code) // #define USE_DISPLAY // Add I2C Display Support (+2k code) #define USE_DISPLAY_MODES1TO5 // Enable display mode 1 to 5 in addition to mode 0 diff --git a/tasmota/support.ino b/tasmota/support.ino index c9ca9b3c3..e56fe4d73 100644 --- a/tasmota/support.ino +++ b/tasmota/support.ino @@ -1965,6 +1965,16 @@ void SerialSendDecimal(char *values) } } +/*********************************************************************************************/ + +uint8_t Bcd2Dec(uint8_t n) { + return n - 6 * (n >> 4); +} + +uint8_t Dec2Bcd(uint8_t n) { + return n + 6 * (n / 10); +} + /*********************************************************************************************\ * Sleep aware time scheduler functions borrowed from ESPEasy \*********************************************************************************************/ diff --git a/tasmota/support_features.ino b/tasmota/support_features.ino index 289fb1a8b..7c4e1aea6 100644 --- a/tasmota/support_features.ino +++ b/tasmota/support_features.ino @@ -795,10 +795,11 @@ void ResponseAppendFeatures(void) feature8 |= 0x00400000; // xnrg_22_bl6523.ino #endif #if defined(USE_ENERGY_SENSOR) && defined(USE_I2C) && defined(USE_ADE7880) - feature8 |= 0x00800000; + feature8 |= 0x00800000; // xnrg_23_ade7880.ino +#endif +#if defined(USE_I2C) && defined(USE_PCF85363) + feature8 |= 0x01000000; // xsns_96_pcf85393.ino #endif - -// feature8 |= 0x01000000; // feature8 |= 0x02000000; // feature8 |= 0x04000000; // feature8 |= 0x08000000; diff --git a/tasmota/tasmota_configurations.h b/tasmota/tasmota_configurations.h index c03bd7cf9..fec5287a3 100644 --- a/tasmota/tasmota_configurations.h +++ b/tasmota/tasmota_configurations.h @@ -150,6 +150,7 @@ //#define USE_AM2320 // [I2cDriver60] Enable AM2320 temperature and humidity Sensor (I2C address 0x5C) (+1k code) //#define USE_T67XX // [I2cDriver61] Enable Telaire T67XX CO2 sensor (I2C address 0x15) (+1k3 code) //#define USE_HDC2010 // [I2cDriver64] Enable HDC2010 temperature/humidity sensor (I2C address 0x40) (+1k5 code) +//#define USE_PCF85363 // [I2cDriver66] Enable PCF85363 RTC (I2C address 0x51) (+0k7 code) //#define USE_SPI // Hardware SPI using GPIO12(MISO), GPIO13(MOSI) and GPIO14(CLK) in addition to two user selectable GPIOs(CS and DC) //#define USE_RC522 // Add support for MFRC522 13.56Mhz Rfid reader (+6k code) diff --git a/tasmota/tasmota_configurations_ESP32.h b/tasmota/tasmota_configurations_ESP32.h index 8a533e0b4..23abf625d 100644 --- a/tasmota/tasmota_configurations_ESP32.h +++ b/tasmota/tasmota_configurations_ESP32.h @@ -242,6 +242,7 @@ //#define USE_BM8563 // [I2cDriver59] Enable BM8563 RTC - found in M5Stack - support both I2C buses on ESP32 (I2C address 0x51) (+2k5 code) //#define USE_AM2320 // [I2cDriver60] Enable AM2320 temperature and humidity Sensor (I2C address 0x5C) (+1k code) //#define USE_T67XX // [I2cDriver61] Enable Telaire T67XX CO2 sensor (I2C address 0x15) (+1k3 code) +//#define USE_PCF85363 // [I2cDriver66] Enable PCF85363 RTC (I2C address 0x51) (+0k7 code) //#define USE_MHZ19 // Add support for MH-Z19 CO2 sensor (+2k code) //#define USE_SENSEAIR // Add support for SenseAir K30, K70 and S8 CO2 sensor (+2k3 code) @@ -376,6 +377,7 @@ //#define USE_EZORGB // [I2cDriver55] Enable support for EZO's RGB sensor (+0k5 code) - Shared EZO code required for any EZO device (+1k2 code) //#define USE_EZOPMP // [I2cDriver55] Enable support for EZO's PMP sensor (+0k3 code) - Shared EZO code required for any EZO device (+1k2 code) //#define USE_SEESAW_SOIL // [I2cDriver56] Enable Capacitice Soil Moisture & Temperature Sensor (I2C addresses 0x36 - 0x39) (+1k3 code) +//#define USE_PCF85363 // [I2cDriver66] Enable PCF85363 RTC (I2C address 0x51) (+0k7 code) #define USE_SPI // Hardware SPI using GPIO12(MISO), GPIO13(MOSI) and GPIO14(CLK) in addition to two user selectable GPIOs(CS and DC) //#define USE_RC522 // Add support for MFRC522 13.56Mhz Rfid reader (+6k code) diff --git a/tasmota/xsns_96_pcf85363.ino b/tasmota/xsns_96_pcf85363.ino new file mode 100644 index 000000000..c7a070636 --- /dev/null +++ b/tasmota/xsns_96_pcf85363.ino @@ -0,0 +1,161 @@ +/* + xsns_96_pcf85363.ino - PCF85363 RTC chip support for Tasmota + + SPDX-FileCopyrightText: 2022 Theo Arends + + SPDX-License-Identifier: GPL-3.0-only +*/ + +#ifdef USE_I2C +#ifdef USE_PCF85363 +/*********************************************************************************************\ + * PCF85363 support + * + * I2C Address: 0x51 +\*********************************************************************************************/ + +#define XSNS_96 96 +#define XI2C_66 66 // See I2CDEVICES.md + +#define USE_PCF85363_ADDR 0x51 // PCF85363 I2C Address + +bool pcf85363_detected = false; + +/*-------------------------------------------------------------------------------------------*\ + * Read time and return the epoch time (second since 1-1-1970 00:00) +\*-------------------------------------------------------------------------------------------*/ +uint32_t Pcf85363ReadTime(void) { + Wire.beginTransmission((uint8_t)USE_PCF85363_ADDR); + Wire.write(0x00); + Wire.endTransmission(); + + uint8_t buffer[8]; + Wire.requestFrom((int)USE_PCF85363_ADDR, (int)8); + for (uint32_t i = 0; i < 8; i++) { buffer[i] = Wire.read(); } + Wire.endTransmission(); + + TIME_T tm; + tm.second = Bcd2Dec(buffer[1] & 0x7F); + tm.minute = Bcd2Dec(buffer[2] & 0x7F); + tm.hour = Bcd2Dec(buffer[3]); + tm.day_of_month = Bcd2Dec(buffer[4]); + tm.day_of_week = buffer[5]; + tm.month = Bcd2Dec(buffer[6]); + tm.year = 30 + Bcd2Dec(buffer[7]); // Offset from 1970. So 2022 - 1970 = 52 + return MakeTime(tm); +} + +/*-------------------------------------------------------------------------------------------*\ + * Get time as TIME_T and set time to this value +\*-------------------------------------------------------------------------------------------*/ +void Pcf85363SetTime(uint32_t epoch_time) { + TIME_T tm; + BreakTime(epoch_time, tm); + + uint8_t buffer[8]; + buffer[0] = 0x00; // 100th_seconds (not used) + buffer[1] = Dec2Bcd(tm.second); + buffer[2] = Dec2Bcd(tm.minute); + buffer[3] = Dec2Bcd(tm.hour); + buffer[4] = Dec2Bcd(tm.day_of_month); + buffer[5] = tm.day_of_week; + buffer[6] = Dec2Bcd(tm.month); + buffer[7] = Dec2Bcd(tm.year -30); // Offset from 1970 +/* + // Handbook page 13 + Wire.beginTransmission((uint8_t)USE_PCF85363_ADDR); + Wire.write(0x2E); + Wire.write(0x01); // Set stop + Wire.write(0xA4); // Clear prescaler + for (uint32_t i = 0; i < 8; i++) { Wire.write(buffer[i]); } + Wire.endTransmission(); + Wire.beginTransmission((uint8_t)USE_PCF85363_ADDR); + Wire.write(0x2E); + Wire.write(0x00); // Set start + Wire.endTransmission(); +*/ + Wire.beginTransmission((uint8_t)USE_PCF85363_ADDR); + Wire.write(0x00); + for (uint32_t i = 0; i < 8; i++) { Wire.write(buffer[i]); } + Wire.endTransmission(); +} + +/*-------------------------------------------------------------------------------------------*\ + * Dump all registers +\*-------------------------------------------------------------------------------------------*/ +/* +void Pcf85363Dump(void) { + uint8_t buffer[64]; + + // 0x00 to 0x2F + Wire.beginTransmission((uint8_t)USE_PCF85363_ADDR); + Wire.write(0x00); + Wire.endTransmission(); + Wire.requestFrom((int)USE_PCF85363_ADDR, (int)48); + for (uint32_t i = 0; i < 48; i++) { + buffer[i] = Wire.read(); + } + Wire.endTransmission(); + AddLog(LOG_LEVEL_DEBUG, PSTR("P85: Read 0x00: %48_H"), buffer); + + // 0x40 to 0x7F + Wire.beginTransmission((uint8_t)USE_PCF85363_ADDR); + Wire.write(0x40); + Wire.endTransmission(); + Wire.requestFrom((int)USE_PCF85363_ADDR, (int)64); + for (uint32_t i = 0; i < 64; i++) { + buffer[i] = Wire.read(); + } + Wire.endTransmission(); + AddLog(LOG_LEVEL_DEBUG, PSTR("P85: Read 0x40: %64_H"), buffer); +} +*/ +/*********************************************************************************************/ + +void Pcf85363Detect(void) { + if (!I2cSetDevice(USE_PCF85363_ADDR)) { return; } + I2cSetActiveFound(USE_PCF85363_ADDR, "PCF85363"); + + pcf85363_detected = true; + + if (Rtc.utc_time < START_VALID_TIME) { // Not sync with NTP/GPS (time not valid), so read time + uint32_t time = Pcf85363ReadTime(); // Read UTC TIME + if (time > START_VALID_TIME) { + Rtc.utc_time = time; + RtcSync("PCF85363"); + } + } +} + +void Pcf85363TimeSynced(void) { + if ((Rtc.utc_time > START_VALID_TIME) && // Valid UTC time + (abs((int32_t)(Rtc.utc_time - Pcf85363ReadTime())) > 2)) { // Time has drifted from RTC more than 2 seconds + Pcf85363SetTime(Rtc.utc_time); // Update time + AddLog(LOG_LEVEL_DEBUG, PSTR("P85: Re-synced (" D_UTC_TIME ") %s"), GetDateAndTime(DT_UTC).c_str()); + } +} + +/*********************************************************************************************\ + * Interface +\*********************************************************************************************/ + +bool Xsns96(uint8_t function) { + if (!I2cEnabled(XI2C_66)) { return false; } + + bool result = false; + + if (FUNC_INIT == function) { + Pcf85363Detect(); + } + else if (pcf85363_detected) { + switch (function) { + case FUNC_TIME_SYNCED: + Pcf85363TimeSynced(); + break; + } + } + return result; +} + +#endif // USE_PCF85363 +#endif // USE_I2C diff --git a/tools/decode-status.py b/tools/decode-status.py index ee9868a76..a0d0b77ec 100755 --- a/tools/decode-status.py +++ b/tools/decode-status.py @@ -263,7 +263,7 @@ a_features = [[ "USE_HRG15","USE_VINDRIKTNING","USE_SCD40","USE_HM330X", "USE_HDC2010","USE_LSC_MCSL","USE_SONOFF_SPM","USE_SHIFT595", "USE_SDM230","USE_CM110x","USE_BL6523","USE_ADE7880", - "","","","", + "USE_PCF85363","","","", "","","","" ]]