diff --git a/docs/2006030032_XINLUDA-XL9535_C561273.pdf b/docs/2006030032_XINLUDA-XL9535_C561273.pdf new file mode 100644 index 0000000..b3ff4ce Binary files /dev/null and b/docs/2006030032_XINLUDA-XL9535_C561273.pdf differ diff --git a/docs/RX8010SJ_en.pdf b/docs/RX8010SJ_en.pdf new file mode 100644 index 0000000..e4fec26 Binary files /dev/null and b/docs/RX8010SJ_en.pdf differ diff --git a/include/NTPupdate.h b/include/NTPupdate.h index f24baa2..557c19b 100644 --- a/include/NTPupdate.h +++ b/include/NTPupdate.h @@ -1,6 +1,7 @@ #pragma once -#include "TimeLib.h" +#include #include "globals.h" +#include "rtc.hpp" static const char ntpServerName[] = "0.pool.ntp.org"; static const int localPort = 8944; diff --git a/include/TEF6686.h b/include/TEF6686.h index b6236d0..48f76a1 100644 --- a/include/TEF6686.h +++ b/include/TEF6686.h @@ -5,6 +5,7 @@ #include "Tuner_Interface.h" #include "RdsPiBuffer.hpp" #include "change_detector.h" +#include "rtc.hpp" extern const unsigned char tuner_init_tab[] PROGMEM; extern const unsigned char tuner_init_tab9216[] PROGMEM; @@ -455,7 +456,7 @@ typedef struct _rds_ { unsigned long blockcounter[33]; uint16_t rdsA, rdsB, rdsC, rdsD, rdsErr, rdsStat, correctPI, rdsplusTag1, rdsplusTag2, PICTlock = 0; bool ps12error, ps34error, ps56error, ps78error; - time_t time; + int32_t clock_correction; int32_t offset; uint16_t aid[10]; uint32_t dabaffreq; diff --git a/include/constants.h b/include/constants.h index 0774a49..8ed322a 100644 --- a/include/constants.h +++ b/include/constants.h @@ -1,8 +1,31 @@ #pragma once -#define VERSION "v2.20.5c" +#include -#define REVERSE false +#define VERSION "v2.20.5d" + +#define ROTARY_PIN_A 34 +#define ROTARY_PIN_B 36 +#define ROTARY_BUTTON 39 +#define PIN_POT 35 +#define BATTERY_PIN 13 +#define BANDBUTTON 4 +#define BWBUTTON 25 +#define MODEBUTTON 26 +#define CONTRASTPIN 2 +#define STANDBYLED 19 +#define SMETERPIN 27 +#define TOUCHIRQ 33 +#define EXT_IRQ 14 + +#define XL9555_ADDRESS 0x20 // GPIO driver used in the DP666 for the 0-9 + DX(Backspace) + Enter buttons +// Assumes that A0 = A1 = A2 = 0 of the chip, this can range from 0x20 to 0x27 + +#define TEF668X_ADDRESS 0x64 // I2C address of the TEF itself! Not sure if this even changes + +#define RX8010SJ_ADDRESS 0x32 // Address of the RTC chip in the DP666 receivers + +#define REVERSE false #define ALEFT -1 #define ACENTER 0 @@ -15,12 +38,12 @@ (x_ < y_) ? x_ : y_; } #define TIMER_OFFSET_TIMER (TIMER_500_TICK) -#define TIMER_BW_TIMER 300 +#define TIMER_BW_TIMER (TIMER_500_TICK) #define TIMER_SNR_TIMER 50 -#define TIMER_BAT_TIMER 250 +#define TIMER_BAT_TIMER (TIMER_500_TICK) #define TIMER_500_TICK 500 -#define BAT_LEVEL_STAGE 4 +#define BAT_LEVEL_STAGE 8 #define BATTERY_LOW_VALUE 3.2 #define BATTERY_FULL_VALUE 4.12 diff --git a/include/globals.h b/include/globals.h index 40a020d..2ae26cf 100644 --- a/include/globals.h +++ b/include/globals.h @@ -10,24 +10,11 @@ #include #include #include -#include "WiFiConnect.h" -#include "WiFiConnectParam.h" -#include "ESP32Time.h" +#include +#include #include "scrolling_text.h" +#include "rtc.hpp" -#define ROTARY_PIN_A 34 -#define ROTARY_PIN_B 36 -#define ROTARY_BUTTON 39 -#define PIN_POT 35 -#define BATTERY_PIN 13 -#define BANDBUTTON 4 -#define BWBUTTON 25 -#define MODEBUTTON 26 -#define CONTRASTPIN 2 -#define STANDBYLED 19 -#define SMETERPIN 27 -#define TOUCHIRQ 33 -#define EXT_IRQ 14 extern bool RDSstatus; extern bool RDSstatusold; @@ -52,14 +39,12 @@ extern bool memorystore; extern bool memreset, memtune; extern bool menu, menuopen; extern bool mwstepsize; -extern bool NTPupdated; extern bool optenc; extern bool rdsflagreset; extern bool rdsreset; extern bool rdsstatscreen; extern bool RDSSPYTCP, RDSSPYUSB; extern bool rotaryaccelerate; -extern bool rtcset; extern bool scandxmode; extern bool scanholdflag; extern bool scanholdonsignal; @@ -359,7 +344,6 @@ extern const size_t language_entrynumber; extern mem presets[EE_PRESETS_CNT]; extern TEF6686 radio; -extern ESP32Time rtc; extern TFT_eSprite FrequencySprite; extern TFT_eSprite RDSSprite; @@ -369,6 +353,7 @@ extern TFT_eSprite OneBigLineSprite; extern TFT_eSprite SignalSprite; extern TFT_eSprite PSSprite; extern TFT_eSprite PTYSprite; +extern TFT_eSprite CTSprite; extern WiFiConnect wc; extern WiFiServer Server; diff --git a/include/logbook.h b/include/logbook.h index 9b056e7..b2d2170 100644 --- a/include/logbook.h +++ b/include/logbook.h @@ -5,6 +5,7 @@ using fs::FS; #include #include #include "globals.h" +#include "rtc.hpp" void handleRoot(); void handleDownloadCSV(); diff --git a/include/rds.h b/include/rds.h index 32ee038..98a3f79 100644 --- a/include/rds.h +++ b/include/rds.h @@ -7,6 +7,8 @@ #include #include #include "globals.h" +#include "NTPupdate.h" +#include "rtc.hpp" void ShowAdvancedRDS(); void readRds(); diff --git a/include/rtc.hpp b/include/rtc.hpp new file mode 100644 index 0000000..b9ab276 --- /dev/null +++ b/include/rtc.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include "constants.h" +#include +#include + +extern bool rtcset; +extern bool NTPupdated; + +extern ESP32Time rtc; +extern bool rx_rtc_avail; +extern RX8010SJ::Adapter rx_rtc; + +void timeToDateTime(time_t t, struct RX8010SJ::DateTime* dateTime); +time_t dateTimeToTime(const struct RX8010SJ::DateTime* dateTime); + +void sync_to_rx_rtc(int32_t offset = 0); +void sync_from_rx_rtc(int32_t offset = 0); \ No newline at end of file diff --git a/lib/RX8010SJ/RX8010SJ.cpp b/lib/RX8010SJ/RX8010SJ.cpp new file mode 100644 index 0000000..3b9611d --- /dev/null +++ b/lib/RX8010SJ/RX8010SJ.cpp @@ -0,0 +1,429 @@ +#include "RX8010SJ.h" + +namespace RX8010SJ { + + Adapter::Adapter(byte i2cSlaveAddr) { + i2cAddress = i2cSlaveAddr; + } + + Adapter::~Adapter() {} + + /** + * + * PUBLIC FUNCTIONS + * + */ + + /** + * GENERAL + */ + + bool Adapter::initAdapter() { + Wire.begin(); + delay(40); + + return initModule(); + } + + bool Adapter::initModule() { + byte flagValue = readFromModule(RX8010_FLAG); + byte vlf = getValueFromBinary(flagValue, RX8010_VLF_POS); + + // It's 1 when the module had power issues and needs to be reinitialised + if (vlf == 1) { + // The oscillator takes some time stabilise + while (vlf == 1) { + flagValue = setBinary(flagValue, RX8010_VLF_POS, 0); + writeToModule(RX8010_FLAG, vlf); + delay(10); + flagValue = readFromModule(RX8010_FLAG); + vlf = getValueFromBinary(flagValue, RX8010_VLF_POS); + } + + resetModule(); + + return true; + } + + return false; + } + + void Adapter::resetModule() { + writeToModule(RX8010_RESV17, RX8010_ADDR17_DEF_VAL); + writeToModule(RX8010_RESV30, RX8010_ADDR30_DEF_VAL); + writeToModule(RX8010_RESV31, RX8010_ADDR31_DEF_VAL); + writeToModule(RX8010_IRQ, RX8010_IRQ_DEF_VAL); + + writeFlag(RX8010_EXT, RX8010_TE_POS, 0); + writeFlag(RX8010_FLAG, RX8010_VLF_POS, 0); + + writeToModule(RX8010_CTRL, RX8010_CTRL_DEF_VAL); + } + + void Adapter::toggleGlobalStop(bool stopEnabled) { + writeFlag(RX8010_CTRL, RX8010_STOP_POS, stopEnabled ? 1 : 0); + } + + /** + * DATE TIME + */ + + DateTime Adapter::readDateTime() { + byte secondBin = readFromModule(RX8010_SEC); + byte minuteBin = readFromModule(RX8010_MIN); + byte hourBin = readFromModule(RX8010_HOUR); + byte dayOfWeekBin = readFromModule(RX8010_WDAY); + byte dayOfMonthBin = readFromModule(RX8010_MDAY); + byte monthBin = readFromModule(RX8010_MONTH); + byte yearBin = readFromModule(RX8010_YEAR); + + DateTime dateTime; + + dateTime.second = sumValueFromBinary(secondBin, 7); + dateTime.minute =sumValueFromBinary(minuteBin, 7); + dateTime.hour = sumValueFromBinary(hourBin, 6); + dateTime.dayOfWeek = getSingleBit(dayOfWeekBin); + dateTime.dayOfMonth = sumValueFromBinary(dayOfMonthBin, 6); + dateTime.month = sumValueFromBinary(monthBin, 5); + dateTime.year = sumValueFromBinary(yearBin, 8); + + return dateTime; + } + + void Adapter::writeDateTime(DateTime dateTime) { + byte second = dateTime.second % 10; + byte minute = dateTime.minute % 10; + byte hour = dateTime.hour % 10; + byte dayOfWeek = setBinary(0, dateTime.dayOfWeek, 1); + byte dayOfMonth = dateTime.dayOfMonth % 10; + byte month = dateTime.month % 10; + byte year = dateTime.year % 10; + + second = setFortyBinary(second, dateTime.second); + second = setTwentyBinary(second, dateTime.second); + second = setTenBinary(second, dateTime.second); + + minute = setFortyBinary(minute, dateTime.minute); + minute = setTwentyBinary(minute, dateTime.minute); + minute = setTenBinary(minute, dateTime.minute); + + hour = setTwentyBinary(hour, dateTime.hour); + hour = setTenBinary(hour, dateTime.hour); + + dayOfMonth = setTwentyBinary(dayOfMonth, dateTime.dayOfMonth); + dayOfMonth = setTenBinary(dayOfMonth, dateTime.dayOfMonth); + + month = setTenBinary(month, dateTime.month); + + year = setEightyBinary(year, dateTime.year); + year = setFortyBinary(year, dateTime.year); + year = setTwentyBinary(year, dateTime.year); + year = setTenBinary(year, dateTime.year); + + writeToModule(RX8010_SEC, second); + writeToModule(RX8010_MIN, minute); + writeToModule(RX8010_HOUR, hour); + writeToModule(RX8010_WDAY, dayOfWeek); + writeToModule(RX8010_MDAY, dayOfMonth); + writeToModule(RX8010_MONTH, month); + writeToModule(RX8010_YEAR, year); + } + + /** + * FCT + */ + + void Adapter::setFCTCounter(uint16_t multiplier, byte frequency) { + byte firstHalf = multiplier & 0b11111111; + byte secondHalf = multiplier >> 8; + + writeToModule(RX8010_TCOUNT0, firstHalf); + writeToModule(RX8010_TCOUNT1, secondHalf); + + writeFlag(RX8010_EXT, RX8010_TSEL0_POS, getValueFromBinary(frequency, RX8010_TSEL0_POS)); + writeFlag(RX8010_EXT, RX8010_TSEL1_POS, getValueFromBinary(frequency, RX8010_TSEL1_POS)); + writeFlag(RX8010_EXT, RX8010_TSEL2_POS, getValueFromBinary(frequency, RX8010_TSEL2_POS)); + } + + uint16_t Adapter::getFCTCounter() { + byte firstHalf = readFromModule(RX8010_TCOUNT0); + byte secondHalf = readFromModule(RX8010_TCOUNT1); + + return firstHalf + (secondHalf << 8); + } + + void Adapter::setFCTOutput(byte pin) { + if (pin > 1) { + writeFlag(RX8010_CTRL, RX8010_TIE_POS, 0); + } else { + writeFlag(RX8010_IRQ, RX8010_TMPIN_POS, pin); + writeFlag(RX8010_CTRL, RX8010_TIE_POS, 1); + } + } + + void Adapter::enableFCT() { + writeFlag(RX8010_CTRL, RX8010_TSTP_POS, 0); + writeFlag(RX8010_CTRL, RX8010_TIE_POS, 1); + writeFlag(RX8010_EXT, RX8010_TE_POS, 1); + } + + void Adapter::disableFCT() { + writeFlag(RX8010_EXT, RX8010_TE_POS, 0); + writeFlag(RX8010_CTRL, RX8010_TSTP_POS, 1); + } + + bool Adapter::checkFCT() { + byte flag = readFromModule(RX8010_FLAG); + bool interrupted = getValueFromBinary(flag, RX8010_TF_POS) == 1; + + if (interrupted) { + writeFlag(RX8010_FLAG, RX8010_TF_POS, 0); + } + + return interrupted; + } + + /** + * ALARM + */ + + void Adapter::setAlarm(DateTime time, byte mode) { + byte minute; + byte hour; + + if (time.minute == RX8010_ALARM_IGNORE) { + minute = RX8010_AL_DISABLED; + } else { + minute = time.minute % 10; + minute = setFortyBinary(minute, time.minute); + minute = setTwentyBinary(minute, time.minute); + minute = setTenBinary(minute, time.minute); + } + + if (time.hour == RX8010_ALARM_IGNORE) { + hour = RX8010_AL_DISABLED; + } else { + hour = time.hour % 10; + hour = setTwentyBinary(hour, time.hour); + hour = setTenBinary(hour, time.hour); + } + + writeToModule(RX8010_ALMIN, minute); + writeToModule(RX8010_ALHOUR, hour); + + if (mode == RX8010_ALARM_MOD_WEEK) { + writeToModule(RX8010_ALWDAY, time.dayOfWeek == RX8010_ALARM_IGNORE ? RX8010_AL_DISABLED : time.dayOfWeek); + } else { + byte day; + + if (time.hour == RX8010_ALARM_IGNORE) { + day = RX8010_AL_DISABLED; + } else { + day = time.dayOfMonth % 10; + day = setTwentyBinary(hour, time.hour); + day = setTenBinary(hour, time.hour); + } + + writeToModule(RX8010_ALWDAY, day); + } + + writeFlag(RX8010_EXT, RX8010_WADA_POS, mode == RX8010_ALARM_MOD_WEEK ? 0 : 1); + } + + void Adapter::enableAlarm() { + writeFlag(RX8010_FLAG, RX8010_AF_POS, 0); + writeFlag(RX8010_CTRL, RX8010_AIE_POS, 1); + } + + void Adapter::disableAlarm() { + writeFlag(RX8010_CTRL, RX8010_AIE_POS, 0); + writeFlag(RX8010_FLAG, RX8010_AF_POS, 0); + } + + bool Adapter::checkAlarm() { + byte flag = readFromModule(RX8010_FLAG); + bool triggered = getValueFromBinary(flag, RX8010_AF_POS) == 1; + + if (triggered) { + writeFlag(RX8010_FLAG, RX8010_AF_POS, 0); + } + + return triggered; + } + + /** + * TIME UPDATE INTERRUPT + */ + + void Adapter::setTUIMode(byte mode) { + writeFlag(RX8010_EXT, RX8010_USEL_POS, mode); + } + + void Adapter::enableTUI() { + writeFlag(RX8010_CTRL, RX8010_UIE_POS, 1); + } + + void Adapter::disableTUI() { + writeFlag(RX8010_CTRL, RX8010_UIE_POS, 0); + } + + bool Adapter::checkTUI() { + byte flag = readFromModule(RX8010_FLAG); + bool interrupted = getValueFromBinary(flag, RX8010_UF_POS) == 1; + + if (interrupted) { + writeFlag(RX8010_FLAG, RX8010_UF_POS, 0); + } + + return interrupted; + } + + /** + * FREQUENCY OUTPUT + */ + + void Adapter::enableFOUT(byte frequency, byte pin) { + switch (frequency) { + case 3: + writeFlag(RX8010_EXT, RX8010_FSEL0_POS, 1); + writeFlag(RX8010_EXT, RX8010_FSEL1_POS, 1); + break; + case 2: + writeFlag(RX8010_EXT, RX8010_FSEL0_POS, 0); + writeFlag(RX8010_EXT, RX8010_FSEL1_POS, 1); + break; + case 1: + writeFlag(RX8010_EXT, RX8010_FSEL0_POS, 1); + writeFlag(RX8010_EXT, RX8010_FSEL1_POS, 0); + break; + case 0: + default: + disableFOUT(); + return; + } + + writeFlag(RX8010_IRQ, RX8010_FOPIN0_POS, pin); + writeFlag(RX8010_IRQ, RX8010_FOPIN1_POS, 0); + } + + void Adapter::disableFOUT() { + writeFlag(RX8010_EXT, RX8010_FSEL0_POS, 0); + writeFlag(RX8010_EXT, RX8010_FSEL1_POS, 0); + } + + /** + * + * PRIVATE FUNCTIONS + * + */ + + byte Adapter::readFromModule(byte address) { + Wire.beginTransmission(i2cAddress); + Wire.write(address); + Wire.endTransmission(); + Wire.requestFrom((uint8_t)i2cAddress, (uint8_t) 1); + + if (Wire.available()) { + return Wire.read(); + } + + return -1; + } + + void Adapter::writeToModule(byte address, byte data) { + Wire.beginTransmission(i2cAddress); + Wire.write(address); + Wire.write(data); + Wire.endTransmission(); + } + + void Adapter::writeFlag(byte address, byte pos, byte value) { + byte addressValue = readFromModule(address); + addressValue = setBinary(addressValue, pos, value); + writeToModule(address, addressValue); + } + + byte Adapter::getSingleBit(byte binary) { + for (byte i = 0; i <= 7; i++) { + if (binary >> i == 1) { + return i; + } + } + + return 0; + } + + byte Adapter::getValueFromBinary(byte binary, byte pos) { + return getValueFromBinary(binary, pos, 1); + } + + byte Adapter::getValueFromBinary(byte binary, byte pos, byte val) { + return ((binary >> pos) & 1) == 1 ? val : 0; + } + + byte Adapter::sumValueFromBinary(byte binary, byte length) { + byte sum = 0; + + for (byte i = 0; i < length; i++) { + byte value; + + if (i < 4 ) { + value = 1 << i; + } else { + value = 10 * (1 << (i - 4)); + } + + sum += getValueFromBinary(binary, i, value); + } + + return sum; + } + + byte Adapter::setEightyBinary(byte binary, byte val) { + if (val >= 80) { + return setBinary(binary, 7, 1); + } + + return setBinary(binary, 7, 0); + } + + byte Adapter::setFortyBinary(byte binary, byte val) { + if (val >= 40 && val < 80) { + return setBinary(binary, 6, 1); + } + + return setBinary(binary, 6, 0); + } + + byte Adapter::setTwentyBinary(byte binary, byte val) { + if ((val >= 20 && val < 40) || + (val >= 60 && val < 80)) { + + return setBinary(binary, 5, 1); + } + + return setBinary(binary, 5, 0); + } + + byte Adapter::setTenBinary(byte binary, byte val) { + if ((val >= 10 && val < 20) || + (val >= 30 && val < 40) || + (val >= 50 && val < 60) || + (val >= 70 && val < 80) || + (val >= 90 && val < 100)) { + + return setBinary(binary, 4, 1); + } + + return setBinary(binary, 4, 0); + } + + byte Adapter::setBinary(byte binary, byte pos, byte flagVal) { + if (flagVal == 1) { + return binary | (1 << pos); + } + + return binary & (~(1 << pos)); + } + +} \ No newline at end of file diff --git a/lib/RX8010SJ/RX8010SJ.h b/lib/RX8010SJ/RX8010SJ.h new file mode 100644 index 0000000..38cc3b1 --- /dev/null +++ b/lib/RX8010SJ/RX8010SJ.h @@ -0,0 +1,152 @@ +#ifndef RX8010SJ_ADAPTER + #define RX8010SJ_ADAPTER + + #include "Arduino.h" + #include "Wire.h" + + #define RX8010_FCT_FREQ_4096 0b000 + #define RX8010_FCT_FREQ_64 0b001 + #define RX8010_FCT_FREQ_1 0b010 + #define RX8010_FCT_FREQ_1_60 0b011 + #define RX8010_FCT_FREQ_1_3600 0b100 + + #define RX8010_FCT_OUT_IRQ1 0b01 + #define RX8010_FCT_OUT_IRQ2 0b00 + #define RX8010_FCT_OUT_OFF 0b10 + + #define RX8010_ALARM_IGNORE 0b11111111 + #define RX8010_ALARM_MOD_MONTH 0b0 + #define RX8010_ALARM_MOD_WEEK 0b1 + + #define RX8010_TUI_MOD_SEC 0b0 + #define RX8010_TUI_MOD_MIN 0b1 + + #define RX8010_FOUT_FREQ_OFF 0b00 + #define RX8010_FOUT_FREQ_1 0b01 + #define RX8010_FOUT_FREQ_1024 0b10 + #define RX8010_FOUT_FREQ_32768 0b11 + #define RX8010_FOUT_IRQ1 0b01 + #define RX8010_FOUT_IRQ2 0b00 + + namespace RX8010SJ { + #define RX8010_SEC 0x10 + #define RX8010_MIN 0x11 + #define RX8010_HOUR 0x12 + #define RX8010_WDAY 0x13 + #define RX8010_MDAY 0x14 + #define RX8010_MONTH 0x15 + #define RX8010_YEAR 0x16 + #define RX8010_RESV17 0x17 + #define RX8010_ALMIN 0x18 + #define RX8010_ALHOUR 0x19 + #define RX8010_ALWDAY 0x1A + #define RX8010_TCOUNT0 0x1B + #define RX8010_TCOUNT1 0x1C + #define RX8010_EXT 0x1D + #define RX8010_FLAG 0x1E + #define RX8010_CTRL 0x1F + /* 0x20 to 0x2F are user registers */ + #define RX8010_RESV30 0x30 + #define RX8010_RESV31 0x31 + #define RX8010_IRQ 0x32 + + /* Default values for reseting the module */ + #define RX8010_ADDR17_DEF_VAL 0xD8 + #define RX8010_ADDR30_DEF_VAL 0x00 + #define RX8010_ADDR31_DEF_VAL 0x08 + #define RX8010_ADDR31_DEF_VAL 0x08 + #define RX8010_IRQ_DEF_VAL 0x04 + #define RX8010_CTRL_DEF_VAL 0x04 + + #define RX8010_VLF_POS 1 + #define RX8010_TE_POS 4 + #define RX8010_TSEL0_POS 0 + #define RX8010_TSEL1_POS 1 + #define RX8010_TSEL2_POS 2 + #define RX8010_TIE_POS 4 + #define RX8010_TMPIN_POS 2 + #define RX8010_TF_POS 4 + #define RX8010_STOP_POS 6 + #define RX8010_TSTP_POS 2 + #define RX8010_AIE_POS 3 + #define RX8010_WADA_POS 3 + #define RX8010_AF_POS 3 + #define RX8010_USEL_POS 5 + #define RX8010_UF_POS 5 + #define RX8010_UIE_POS 5 + #define RX8010_FSEL0_POS 6 + #define RX8010_FSEL1_POS 7 + #define RX8010_FOPIN0_POS 0 + #define RX8010_FOPIN1_POS 1 + + #define RX8010_AL_DISABLED 0b10000000 + + struct DateTime { + byte second; + byte minute; + byte hour; + byte dayOfWeek; // Has to be set + byte dayOfMonth; + byte month; + byte year; // Two digits + }; + + class Adapter { + public: + // Constructor/destructor + Adapter(byte i2cSlaveAddr); + virtual ~Adapter(); + + // Methods + bool initAdapter(); + bool initModule(); + void resetModule(void); + void toggleGlobalStop(bool stopEnabled); + + // Calendar + DateTime readDateTime(void); + void writeDateTime(DateTime dateTime); + + // Fixed cycle interrupt + void setFCTCounter(uint16_t multiplier, byte frequency); + uint16_t getFCTCounter(); + void setFCTOutput(byte pin); + void enableFCT(); + void disableFCT(); + bool checkFCT(); + + // Alarm + void setAlarm(DateTime time, byte mode); + void enableAlarm(); + void disableAlarm(); + bool checkAlarm(); + + // Time Update Interrupt + void setTUIMode(byte mode); + void enableTUI(); + void disableTUI(); + bool checkTUI(); + + // Frequency output + void enableFOUT(byte frequency, byte pin); + void disableFOUT(); + + private: + byte i2cAddress; + + byte readFromModule(byte address); + void writeToModule(byte address, byte data); + void writeFlag(byte address, byte pos, byte value); + byte getSingleBit(byte binary); + byte getValueFromBinary(byte binary, byte pos); + byte getValueFromBinary(byte binary, byte pos, byte val); + byte sumValueFromBinary(byte binary, byte length); + byte setEightyBinary(byte binary, byte val); + byte setFortyBinary(byte binary, byte val); + byte setTwentyBinary(byte binary, byte val); + byte setTenBinary(byte binary, byte val); + byte setBinary(byte binary, byte pos, byte flagVal); + }; + } + +#endif \ No newline at end of file diff --git a/lib/RX8010SJ/library.json b/lib/RX8010SJ/library.json new file mode 100644 index 0000000..4f5e969 --- /dev/null +++ b/lib/RX8010SJ/library.json @@ -0,0 +1,20 @@ +{ + "name": "RX8010SJ", + "version": "1.03", + "repository": + { + "type": "git", + "url": "https://github.com/Wh1teRabbitHU/RX8010SJ" + }, + "authors": + [ + { + "name": "Wh1teRabbitHU", + "email": "ruszka.tamas@gmail.com", + "maintainer": true + } + ], + "frameworks": "arduino", + "platforms": "espressif32", + "headers": "TFT_eSPI.h" +} diff --git a/platformio.ini b/platformio.ini index 9d099ba..1499847 100644 --- a/platformio.ini +++ b/platformio.ini @@ -4,6 +4,7 @@ default_envs = dp66x [esp32dev] platform = espressif32 upload_speed = 921600 +monitor_speed = 115200 board = esp32dev framework = arduino board_build.partitions = maxapp.csv @@ -19,7 +20,9 @@ build_flags = -Wl,--gc-sections -DARDUINO_LOOP_STACK_SIZE=4096 -Werror - +monitor_filters = + esp32_exception_decoder + default build_unflags = -fexceptions -frtti diff --git a/src/NTPupdate.cpp b/src/NTPupdate.cpp index ceb49bb..c0fa862 100644 --- a/src/NTPupdate.cpp +++ b/src/NTPupdate.cpp @@ -55,6 +55,11 @@ void NTPupdate() { if (currentTime) { rtc.setTime(currentTime); + if(rx_rtc_avail) { + RX8010SJ::DateTime rx_currenttime = RX8010SJ::DateTime(); + timeToDateTime(currentTime, &rx_currenttime); + rx_rtc.writeDateTime(rx_currenttime); + } rtcset = true; NTPupdated = true; radio.rds.ctupdate = false; diff --git a/src/TEF6686.cpp b/src/TEF6686.cpp index 704df9a..d721790 100644 --- a/src/TEF6686.cpp +++ b/src/TEF6686.cpp @@ -2,7 +2,7 @@ #include #include #include -#include "SPIFFS.h" +#include #include "constants.h" #include "utils.h" @@ -1212,11 +1212,12 @@ void TEF6686::readRDS(byte showrdserrors) { case RDS_GROUP_4A: { if (!rdsBerrorThreshold && !rdsCerrorThreshold && !rdsDerrorThreshold && rds.ctupdate && (rds.PICTlock == pi || rds.PICTlock == 0)) { - uint32_t mjd = (rds.rdsB & 0x03) << 15 | ((rds.rdsC >> 1) & 0x7FFF);; + auto rtc_time = rtc.getEpoch(); + uint32_t mjd = (rds.rdsB & 0x03) << 15 | ((rds.rdsC >> 1) & 0x7FFF); uint16_t hour, minute, day = 5, month = 1, year = 2026; int32_t timeoffset; - long J = mjd + 2400001 + 68569; + long J = mjd + 2400001 + 68569; long C = 4 * J / 146097; J = J - (146097 * C + 3) / 4; long Y = 4000 * (J + 1) / 1461001; @@ -1247,20 +1248,23 @@ void TEF6686::readRDS(byte showrdserrors) { tm.tm_isdst = -1; time_t rdstime = mktime(&tm); - if (lastrdstime == 0) { - lastrdstime = rdstime; - lasttimeoffset = timeoffset; - } - if ((rdstime == lastrdstime + 60 && timeoffset == lasttimeoffset) || showrdserrors != 0) { + if (((rdstime == lastrdstime + 60 && timeoffset == lasttimeoffset) || (lastrdstime == 0 && lasttimeoffset == 0)) || showrdserrors != 0) { rds.hasCT = true; - rds.time = rdstime; rds.offset = timeoffset; + rtcset = true; + + time_t rds_utc_time = rdstime + timeoffset; + rds.clock_correction = rtc_time - rds_utc_time; + + if (!NTPupdated) { + rtc.setTime(rds_utc_time); + sync_to_rx_rtc(); + } } else rds.hasCT = false; lastrdstime = rdstime; lasttimeoffset = timeoffset; } } break; - case RDS_GROUP_10A: { if (!rdsCerrorThreshold && !rdsDerrorThreshold) { uint8_t segment = bitRead(rds.rdsB, 0); diff --git a/src/Tuner_Interface.cpp b/src/Tuner_Interface.cpp index ec8cfd5..283c065 100644 --- a/src/Tuner_Interface.cpp +++ b/src/Tuner_Interface.cpp @@ -51,7 +51,7 @@ const unsigned char tuner_init_tab55000[] PROGMEM = { }; bool Tuner_WriteBuffer(unsigned char *buf, uint16_t len) { - Wire.beginTransmission(0x64); + Wire.beginTransmission(TEF668X_ADDRESS); for (uint16_t i = 0; i < len; i++) Wire.write(buf[i]); uint8_t r = Wire.endTransmission(); if (!Data_Accelerator) delay(2); @@ -59,7 +59,7 @@ bool Tuner_WriteBuffer(unsigned char *buf, uint16_t len) { } bool Tuner_ReadBuffer(unsigned char *buf, uint16_t len) { - Wire.requestFrom(0x64, len); + Wire.requestFrom(TEF668X_ADDRESS, len); if (Wire.available() == len) { for (uint16_t i = 0; i < len; i++) buf[i] = Wire.read(); return true; @@ -92,7 +92,7 @@ bool Tuner_Table_Write(const unsigned char *tab) { } void Tuner_Reset(void) { - Wire.beginTransmission(0x64); + Wire.beginTransmission(TEF668X_ADDRESS); Wire.write(0x1e); Wire.write(0x5a); Wire.write(0x01); @@ -104,13 +104,13 @@ void Tuner_Reset(void) { void Tuner_Patch(byte TEF) { Tuner_Reset(); delay(100); - Wire.beginTransmission(0x64); + Wire.beginTransmission(TEF668X_ADDRESS); Wire.write(0x1c); Wire.write(0x00); Wire.write(0x00); Wire.endTransmission(); delay(100); - Wire.beginTransmission(0x64); + Wire.beginTransmission(TEF668X_ADDRESS); Wire.write(0x1c); Wire.write(0x00); Wire.write(0x74); @@ -123,13 +123,13 @@ void Tuner_Patch(byte TEF) { Tuner_Patch_Load(pPatchBytes205, PatchSize205); break; } - Wire.beginTransmission(0x64); + Wire.beginTransmission(TEF668X_ADDRESS); Wire.write(0x1c); Wire.write(0x00); Wire.write(0x00); Wire.endTransmission(); delay(100); - Wire.beginTransmission(0x64); + Wire.beginTransmission(TEF668X_ADDRESS); Wire.write(0x1c); Wire.write(0x00); Wire.write(0x75); @@ -142,7 +142,7 @@ void Tuner_Patch(byte TEF) { Tuner_Patch_Load(pLutBytes205, LutSize205); break; } - Wire.beginTransmission(0x64); + Wire.beginTransmission(TEF668X_ADDRESS); Wire.write(0x1c); Wire.write(0x00); Wire.write(0x00); diff --git a/src/comms.cpp b/src/comms.cpp index 9a75db8..4449d7d 100644 --- a/src/comms.cpp +++ b/src/comms.cpp @@ -720,7 +720,6 @@ void XDRGTKRoutine() { if (scandxmode) cancelDXScan(); if (!XDRScan) BWsetRecall = BWset; XDRScan = true; - Data_Accelerator = true; switch (buff[1]) { case 'a': scanner_start = (atoi(buff + 2) + 5) / 10; break; @@ -748,9 +747,8 @@ void XDRGTKRoutine() { } doBW(); break; - case 'w': - unsigned int bwtemp; - bwtemp = atoi(buff + 2); + case 'w': { + unsigned int bwtemp = atoi(buff + 2); switch (bwtemp) { case 0: BWset = 0; break; case 56000: BWset = 1; break; @@ -772,6 +770,7 @@ void XDRGTKRoutine() { } doBW(); break; + } case '\0': radio.setMute(); @@ -808,7 +807,6 @@ void XDRGTKRoutine() { } break; } - Data_Accelerator = false; break; } case 'W': { unsigned int bwtemp = atoi(buff + 1); diff --git a/src/globals.cpp b/src/globals.cpp index cbc3adb..bed6aba 100644 --- a/src/globals.cpp +++ b/src/globals.cpp @@ -25,14 +25,12 @@ bool memorystore; bool memreset, memtune; bool menu, menuopen; bool mwstepsize; -bool NTPupdated; bool optenc; bool rdsflagreset; bool rdsreset; bool rdsstatscreen; bool RDSSPYTCP, RDSSPYUSB; bool rotaryaccelerate = true; -bool rtcset; bool scandxmode; bool scanholdflag; bool scanholdonsignal; @@ -332,7 +330,6 @@ const size_t language_entrynumber = sizeof(myLanguage[0]) / sizeof(myLanguage[0] mem presets[EE_PRESETS_CNT]; TEF6686 radio; -ESP32Time rtc(0); // FrequencySprite.createSprite(200, 50); // RDSSprite.createSprite(165, 19); @@ -345,10 +342,10 @@ ESP32Time rtc(0); TFT_eSprite FrequencySprite = TFT_eSprite(&tft); TFT_eSprite RDSSprite = TFT_eSprite(&tft); TFT_eSprite PTYSprite = TFT_eSprite(&tft); +TFT_eSprite SignalSprite = TFT_eSprite(&tft); TFT_eSprite SquelchSprite = TFT_eSprite(&tft); TFT_eSprite FullLineSprite = TFT_eSprite(&tft); TFT_eSprite OneBigLineSprite = TFT_eSprite(&tft); -TFT_eSprite SignalSprite = TFT_eSprite(&tft); TFT_eSprite PSSprite = TFT_eSprite(&tft); WiFiConnect wc; diff --git a/src/gui.cpp b/src/gui.cpp index 67c550c..0c9120a 100644 --- a/src/gui.cpp +++ b/src/gui.cpp @@ -3081,11 +3081,8 @@ void BuildDisplay() { tft.drawBitmap(68, 5, RDSLogo, 35, 22, GreyoutColor); tft.drawBitmap(249, 4, Speaker, 28, 24, GreyoutColor); - if (!StereoToggle) { - tft.drawBitmap(38, 5, Mono, 22, 22, SecondaryColor); - } else { - tft.drawBitmap(32, 5, Stereo, 32, 22, GreyoutColor); - } + if (!StereoToggle) tft.drawBitmap(38, 5, Mono, 22, 22, SecondaryColor); + else tft.drawBitmap(32, 5, Stereo, 32, 22, GreyoutColor); if (autosquelch) showAutoSquelch(1); @@ -3822,13 +3819,6 @@ void MenuUpDown(bool dir) { } } - if (ConverterSet >= 200) { - Wire.beginTransmission(0x12); - Wire.write(ConverterSet >> 8); - Wire.write(ConverterSet & (0xFF)); - Wire.endTransmission(); - } - OneBigLineSprite.setTextDatum(TL_DATUM); OneBigLineSprite.setTextColor(ActiveColor, ActiveColorSmooth, false); OneBigLineSprite.drawString("MHz", 155, 0); diff --git a/src/main.cpp b/src/main.cpp index 14ae127..2e20e3b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -841,10 +841,10 @@ int GetNum() { int cnt = 0; unsigned int num; - Wire.beginTransmission(0x20); + Wire.beginTransmission(XL9555_ADDRESS); Wire.write(0x00); Wire.endTransmission(); - Wire.requestFrom(0x20, 2); + Wire.requestFrom(XL9555_ADDRESS, 2); if (Wire.available() == 2) { keypadtimer = millis(); @@ -1146,6 +1146,52 @@ void setup() { gpio_set_drive_capability((gpio_num_t) 23, GPIO_DRIVE_CAP_0); setupmode = true; + + Tuner_I2C_Init(); + Serial.begin(115200); + byte error, address; + for (address = 1; address < 127; address++) { // I2C addresses 0x01–0x7F + Wire.beginTransmission(address); + error = Wire.endTransmission(); + + if (error == 0) { + Serial.print("I2C device found at 0x"); + if (address < 16) Serial.print("0"); + Serial.print(address, HEX); + if(address == RX8010SJ_ADDRESS) { + Serial.print(" RTC"); + rx_rtc_avail = true; + } + Serial.println(" !"); + } else if (error == 4) { + Serial.print("Unknown error at 0x"); + if (address < 16) Serial.print("0"); + Serial.println(address, HEX); + } + } + Serial.flush(); + Serial.end(); + + rtc.setTime(0); + if(rx_rtc_avail) { + RX8010SJ::DateTime defaulttime = RX8010SJ::DateTime(); + defaulttime.second = 21; + defaulttime.minute = 45; + defaulttime.hour = 11; + defaulttime.dayOfWeek = 6; + defaulttime.dayOfMonth = 11; + defaulttime.month = 1; + defaulttime.year = 26; + bool reset = rx_rtc.initModule(); // initModule, not initAdapter, adapter also reinits wire + if(reset) { + Serial.println("RTC reset with defaults"); + rx_rtc.writeDateTime(defaulttime); + } else { + rtcset = true; + sync_from_rx_rtc(); + } + } + EEPROM.begin(EE_TOTAL_CNT); loadData(); @@ -1417,7 +1463,7 @@ void setup() { } tftPrint(ACENTER, "Patch: v" + String(TEF), 160, 202, ActiveColor, ActiveColorSmooth, 28); - Wire.beginTransmission(0x20); + Wire.beginTransmission(XL9555_ADDRESS); Wire.write(0x06); Wire.write(0xFF); Wire.write(0xFF); diff --git a/src/rds.cpp b/src/rds.cpp index bda560b..f1c2d9d 100644 --- a/src/rds.cpp +++ b/src/rds.cpp @@ -514,50 +514,42 @@ void showPS() { void showCT() { char timeStr[16]; char dateStr[9]; - time_t t; - - if (radio.rds.hasCT && !dropout && !NTPupdated) t = radio.rds.time + radio.rds.offset; - else { - t = rtc.getEpoch() + (NTPupdated ? 0 : radio.rds.offset); - - // Update RDS time during dropout - if (dropout) radio.rds.time = static_cast(rtc.getEpoch()); - } + time_t t = rtc.getEpoch() + (NTPupdated ? 0 : radio.rds.offset); if (NTPupdated) { t += NTPoffset * 3600; // Convert offset from hours to seconds if (autoDST && isDST(t)) t += 3600; } + auto localtm = localtime(&t); + if (clockampm) { // USA region: 12-hour AM/PM format - int hour = localtime(&t)->tm_hour; + int hour = localtm->tm_hour; if (hour < 1 || hour > 12) { if (hour == 0) hour = 12; else if (hour > 12) hour -= 12; } - String ampm = (localtime(&t)->tm_hour >= 12) ? "PM" : "AM"; - snprintf(timeStr, sizeof(timeStr), "%d:%02d %s", hour, localtime(&t)->tm_min, ampm.c_str()); + String ampm = (localtm->tm_hour >= 12) ? "PM" : "AM"; + snprintf(timeStr, sizeof(timeStr), "%d:%02d %s", hour, localtm->tm_min, ampm.c_str()); } else { - int hour = localtime(&t)->tm_hour; + int hour = localtm->tm_hour; if (hour < 0 || hour > 23) hour = 0; - snprintf(timeStr, sizeof(timeStr), "%02d:%02d", hour, localtime(&t)->tm_min); + snprintf(timeStr, sizeof(timeStr), "%02d:%02d", hour, localtm->tm_min); + } - rds_clock = String(timeStr); - if (clockampm) strftime(dateStr, sizeof(dateStr), "%m-%d-%y", localtime(&t)); - else strftime(dateStr, sizeof(dateStr), "%d-%m-%y", localtime(&t)); + if (clockampm) strftime(dateStr, sizeof(dateStr), "%m-%d-%y", localtm); + else strftime(dateStr, sizeof(dateStr), "%d-%m-%y", localtm); rds_date = String(dateStr); if (!screenmute && showclock && (rds_clock != rds_clockold || rds_date != rds_dateold || radio.rds.hasCT.changed(0))) { if ((radio.rds.hasCT && RDSstatus) || NTPupdated) { rtcset = true; - if (!NTPupdated) rtc.setTime(radio.rds.time); - tftReplace(ACENTER, rds_clockold, rds_clock, 134, 1, RDSColor, RDSColorSmooth, BackgroundColor, 16); tftReplace(ACENTER, rds_dateold, rds_date, 134, 15, RDSColor, RDSColorSmooth, BackgroundColor, 16); } else { // Handle dropout scenarios diff --git a/src/rtc.cpp b/src/rtc.cpp new file mode 100644 index 0000000..a0137fa --- /dev/null +++ b/src/rtc.cpp @@ -0,0 +1,58 @@ +#include "rtc.hpp" + +bool rtcset; +bool NTPupdated; + +ESP32Time rtc(0); +bool rx_rtc_avail = false; +RX8010SJ::Adapter rx_rtc = RX8010SJ::Adapter(RX8010SJ_ADDRESS); + +void timeToDateTime(time_t t, struct RX8010SJ::DateTime* dateTime) { + struct tm* timeinfo = gmtime(&t); + dateTime->second = timeinfo->tm_sec; + dateTime->minute = timeinfo->tm_min; + dateTime->hour = timeinfo->tm_hour; + dateTime->dayOfWeek = (timeinfo->tm_wday + 6) % 7; + dateTime->dayOfMonth = timeinfo->tm_mday; + dateTime->month = timeinfo->tm_mon + 1; // tm_mon is 0-11, convert to 1-12 + dateTime->year = (timeinfo->tm_year + 1900) % 100; // Get last 2 digits +} + +time_t timegm(struct tm *tm) { + // https://linux.die.net/man/3/timegm + time_t ret; + char *tz; + + tz = getenv("TZ"); + setenv("TZ", "", 1); + tzset(); + ret = mktime(tm); + if (tz) setenv("TZ", tz, 1); + else unsetenv("TZ"); + tzset(); + return ret; +} + +time_t dateTimeToTime(const struct RX8010SJ::DateTime* dateTime) { + struct tm timeinfo; + timeinfo.tm_sec = dateTime->second; + timeinfo.tm_min = dateTime->minute; + timeinfo.tm_hour = dateTime->hour; + timeinfo.tm_mday = dateTime->dayOfMonth; + timeinfo.tm_mon = dateTime->month - 1; + timeinfo.tm_year = dateTime->year + 100; + timeinfo.tm_isdst = 0; + return timegm(&timeinfo); +} + +void sync_to_rx_rtc(int32_t offset) { + if(!rx_rtc_avail) return; + RX8010SJ::DateTime rx_currenttime = RX8010SJ::DateTime(); + timeToDateTime(rtc.getEpoch() + offset, &rx_currenttime); + rx_rtc.writeDateTime(rx_currenttime); +} +void sync_from_rx_rtc(int32_t offset) { + if(!rx_rtc_avail) return; + RX8010SJ::DateTime dateTime = rx_rtc.readDateTime(); + rtc.setTime(dateTimeToTime(&dateTime) + offset); +} \ No newline at end of file