Skip to content

feat(pcf85063): Add espp::Pcf85063 peripheral for PCF85063 RTC #476

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ jobs:
target: esp32s3
- path: 'components/nvs/example'
target: esp32s3
- path: 'components/pcf85063/example'
target: esp32s3
- path: 'components/pid/example'
target: esp32
- path: 'components/qmi8658/example'
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/upload_components.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ jobs:
components/neopixel
components/nvs
components/pid
components/pcf85063
components/qmi8658
components/qtpy
components/qwiicnes
Expand Down
2 changes: 1 addition & 1 deletion components/bm8563/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
idf_component_register(
INCLUDE_DIRS "include"
REQUIRES "base_peripheral"
REQUIRES "base_peripheral" "utils"
)
7 changes: 2 additions & 5 deletions components/bm8563/example/main/bm8563_example.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,8 @@ extern "C" void app_main(void) {
});
// now make the bm8563
auto bm8563 = espp::Bm8563({
.write = std::bind(&espp::I2c::write, &i2c, std::placeholders::_1, std::placeholders::_2,
std::placeholders::_3),
.write_then_read =
std::bind(&espp::I2c::write_read, &i2c, std::placeholders::_1, std::placeholders::_2,
std::placeholders::_3, std::placeholders::_4, std::placeholders::_5),
.write = std::bind_front(&espp::I2c::write, &i2c),
.write_then_read = std::bind_front(&espp::I2c::write_read, &i2c),
});

std::error_code ec;
Expand Down
1 change: 1 addition & 0 deletions components/bm8563/idf_component.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ dependencies:
idf:
version: '>=5.0'
espp/base_peripheral: '>=1.0'
espp/utils: '>=1.0'
38 changes: 15 additions & 23 deletions components/bm8563/include/bm8563.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <functional>

#include "base_peripheral.hpp"
#include "espp_chrono.hpp"

namespace espp {
/// @brief The BM8563 RTC driver.
Expand Down Expand Up @@ -75,8 +76,8 @@ class Bm8563 : public BasePeripheral<> {

/// @brief The configuration structure.
struct Config {
BasePeripheral::write_fn write; ///< The I2C write function.
BasePeripheral::write_then_read_fn write_then_read; ///< The I2C write then read function.
BasePeripheral<>::write_fn write; ///< The I2C write function.
BasePeripheral<>::write_then_read_fn write_then_read; ///< The I2C write then read function.
espp::Logger::Verbosity log_level{
espp::Logger::Verbosity::WARN}; ///< Log verbosity for the input driver.
};
Expand All @@ -95,16 +96,6 @@ class Bm8563 : public BasePeripheral<> {
}
}

/// @brief Convert a BCD value to a byte.
/// @param value The BCD value.
/// @return The byte value.
static uint8_t bcd2byte(uint8_t value) { return (value >> 4) * 10 + (value & 0x0f); }

/// @brief Convert a byte value to BCD.
/// @param value The byte value.
/// @return The BCD value.
static uint8_t byte2bcd(uint8_t value) { return ((value / 10) << 4) + value % 10; }

/// @brief Get the date and time.
/// @param ec The error code.
/// @return The date and time.
Expand Down Expand Up @@ -143,10 +134,10 @@ class Bm8563 : public BasePeripheral<> {
}
Date d;
int base_year = (data[2] & CENTURY_BIT) ? 1900 : 2000;
d.year = base_year + bcd2byte(data[3] & 0xff);
d.month = bcd2byte(data[2] & 0x1f);
d.weekday = bcd2byte(data[1] & 0x07);
d.day = bcd2byte(data[0] & 0x3f);
d.year = base_year + bcd_to_decimal(data[3] & 0xff);
d.month = bcd_to_decimal(data[2] & 0x1f);
d.weekday = bcd_to_decimal(data[1] & 0x07);
d.day = bcd_to_decimal(data[0] & 0x3f);
return d;
}

Expand All @@ -155,9 +146,9 @@ class Bm8563 : public BasePeripheral<> {
/// @param ec The error code.
void set_date(const Date &d, std::error_code &ec) {
logger_.info("setting date");
const uint8_t data[] = {byte2bcd(d.day), byte2bcd(d.weekday),
(uint8_t)(byte2bcd(d.month) | ((d.year < 2000) ? 0x80 : 0x00)),
byte2bcd(d.year % 100)};
const uint8_t data[] = {decimal_to_bcd(d.day), decimal_to_bcd(d.weekday),
(uint8_t)(decimal_to_bcd(d.month) | ((d.year < 2000) ? 0x80 : 0x00)),
decimal_to_bcd(d.year % 100)};
write_many_to_register((uint8_t)Registers::DATE, data, 4, ec);
}

Expand All @@ -172,9 +163,9 @@ class Bm8563 : public BasePeripheral<> {
return {};
}
Time t;
t.hour = bcd2byte(data[2] & 0x3f);
t.minute = bcd2byte(data[1] & 0x7f);
t.second = bcd2byte(data[0] & 0x7f);
t.hour = bcd_to_decimal(data[2] & 0x3f);
t.minute = bcd_to_decimal(data[1] & 0x7f);
t.second = bcd_to_decimal(data[0] & 0x7f);
return t;
}

Expand All @@ -183,7 +174,8 @@ class Bm8563 : public BasePeripheral<> {
/// @param ec The error code.
void set_time(const Time &t, std::error_code &ec) {
logger_.info("Setting time");
const uint8_t data[] = {byte2bcd(t.second), byte2bcd(t.minute), byte2bcd(t.hour)};
const uint8_t data[] = {decimal_to_bcd(t.second), decimal_to_bcd(t.minute),
decimal_to_bcd(t.hour)};
write_many_to_register((uint8_t)Registers::TIME, data, 3, ec);
}

Expand Down
4 changes: 4 additions & 0 deletions components/pcf85063/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
idf_component_register(
INCLUDE_DIRS "include"
REQUIRES "base_peripheral" "utils"
)
12 changes: 12 additions & 0 deletions components/pcf85063/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# PCF85063 Example

[![Badge](https://components.espressif.com/components/espp/pcf85063/badge.svg)](https://components.espressif.com/components/espp/pcf85063)

The `pcf85063` component provides a driver for the PCF85063 Real Time Clock (RTC).

It supports configuring and reading the time, setting alarms, and using the timer functionality.

## Example

The [example](./example) shows how to use the `espp::Pcf85063` component to initialize and
communicate with the PCF85063 RTC.
21 changes: 21 additions & 0 deletions components/pcf85063/example/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.20)

include($ENV{IDF_PATH}/tools/cmake/project.cmake)

# add the component directories that we want to use
set(EXTRA_COMPONENT_DIRS
"../../../components/"
)

set(
COMPONENTS
"main esptool_py i2c pcf85063"
CACHE STRING
"List of components to include"
)

project(pcf85063_example)

set(CMAKE_CXX_STANDARD 20)
30 changes: 30 additions & 0 deletions components/pcf85063/example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# PCF85063 Example

This example shows how to use the `espp::Pcf85063` component to initialize and
communicate with the PCF85063 RTC.

## How to use example

### Hardware Required

This example is designed to run on a custom board with a PCF85063 RTC.
You will need to configure the I2C pins for your specific hardware.

### Build and Flash

Build the project and flash it to the board, then run monitor tool to view
serial output:

```
idf.py -p PORT flash monitor
```

(Replace PORT with the name of the serial port to use.)

(To exit the serial monitor, type ``Ctrl-]``.)

See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.

## Example Output

<img width="453" height="330" alt="CleanShot 2025-07-13 at 22 14 08" src="https://github.com/user-attachments/assets/3b51ac17-db40-4d2f-90d2-476c90e085ba" />
2 changes: 2 additions & 0 deletions components/pcf85063/example/main/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
idf_component_register(SRC_DIRS "."
INCLUDE_DIRS ".")
34 changes: 34 additions & 0 deletions components/pcf85063/example/main/Kconfig.projbuild
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
menu "Example Configuration"

choice EXAMPLE_HARDWARE
prompt "Hardware"
default EXAMPLE_HARDWARE_CUSTOM
help
Select the hardware to run this example on.

config EXAMPLE_HARDWARE_CUSTOM
bool "Custom"
endchoice

config EXAMPLE_I2C_SCL_GPIO
int "SCL GPIO Num"
range 0 50
default 19
help
GPIO number for I2C Master clock line.

config EXAMPLE_I2C_SDA_GPIO
int "SDA GPIO Num"
range 0 50
default 22
help
GPIO number for I2C Master data line.

config EXAMPLE_I2C_CLOCK_SPEED_HZ
int "I2C Clock Speed"
range 100 1000000
default 400000
help
I2C clock speed in Hz.

endmenu
80 changes: 80 additions & 0 deletions components/pcf85063/example/main/pcf85063_example.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#include <chrono>
#include <vector>

#include "i2c.hpp"
#include "pcf85063.hpp"
#include "task.hpp"

using namespace std::chrono_literals;

extern "C" void app_main(void) {
espp::Logger logger({.tag = "PCF85063 Example", .level = espp::Logger::Verbosity::INFO});
logger.info("Starting example!");

//! [pcf85063_example]

// make the i2c we'll use to communicate
static constexpr auto i2c_port = I2C_NUM_0;
static constexpr auto i2c_clock_speed = CONFIG_EXAMPLE_I2C_CLOCK_SPEED_HZ;
static constexpr gpio_num_t i2c_sda = (gpio_num_t)CONFIG_EXAMPLE_I2C_SDA_GPIO;
static constexpr gpio_num_t i2c_scl = (gpio_num_t)CONFIG_EXAMPLE_I2C_SCL_GPIO;
espp::I2c i2c({.port = i2c_port,
.sda_io_num = i2c_sda,
.scl_io_num = i2c_scl,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.clk_speed = i2c_clock_speed});

// now make the pcf85063
espp::Pcf85063 rtc({.device_address = espp::Pcf85063::DEFAULT_ADDRESS,
.write = std::bind_front(&espp::I2c::write, &i2c),
.read = std::bind_front(&espp::I2c::read, &i2c)});

// set the time
std::tm time;
time.tm_sec = 0;
time.tm_min = 0;
time.tm_hour = 0;
time.tm_mday = 1;
time.tm_mon = 0;
time.tm_year = 2023 - 1900;
std::error_code ec;
rtc.set_time(time, ec);
if (ec) {
logger.error("Failed to set time: {}", ec.message());
return;
}
logger.info("Time set to: {}.{:02d}.{:02d} {:02d}:{:02d}:{:02d}", time.tm_year + 1900,
time.tm_mon + 1, time.tm_mday, time.tm_hour, time.tm_min, time.tm_sec);

// now start a task to read the time every second
auto task_fn = [&](std::mutex &m, std::condition_variable &cv) -> bool {
std::error_code ec;
auto time = rtc.get_time(ec);
if (ec) {
logger.error("Failed to get time: {}", ec.message());
return true; // stop the task
}
logger.info("Time: {}.{:02d}.{:02d} {:02d}:{:02d}:{:02d}", time.tm_year + 1900, time.tm_mon + 1,
time.tm_mday, time.tm_hour, time.tm_min, time.tm_sec);
// use the mutex and condition variable to perform an interruptible sleep
std::unique_lock<std::mutex> lock(m);
cv.wait_for(lock, 1s, [] { return false; }); // wait for 1 second or until notified
// don't stop the task
return false;
};

auto task = espp::Task({.callback = task_fn,
.task_config =
{
.name = "pcf85063",
.stack_size_bytes = 4096,
},
.log_level = espp::Logger::Verbosity::INFO});
task.start();
//! [pcf85063_example]

while (true) {
std::this_thread::sleep_for(1s);
}
}
4 changes: 4 additions & 0 deletions components/pcf85063/example/sdkconfig.defaults
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
CONFIG_IDF_TARGET="esp32s3"

# set the functions into IRAM
CONFIG_SPI_MASTER_IN_IRAM=y
21 changes: 21 additions & 0 deletions components/pcf85063/idf_component.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
## IDF Component Manager Manifest File
license: "MIT"
description: "PCF85063 Real-Time Clock (RTC) component for ESP-IDF"
url: "https://github.com/esp-cpp/espp/tree/main/components/pcf85063"
repository: "git://github.com/esp-cpp/espp.git"
maintainers:
- William Emfinger <waemfinger@gmail.com>
documentation: "https://esp-cpp.github.io/espp/rtc/pcf85063.html"
examples:
- path: example
tags:
- cpp
- Component
- PCF85063
- RTC
- Peripheral
dependencies:
idf:
version: '>=5.0'
espp/base_peripheral: '>=1.0'
espp/utils: '>=1.0'
Loading
Loading