-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add hardware abstraction component for the ESP32 TimerCam. (#318)
* Add ESP32-Timer-Cam hardware abstraction * add timer cam example * update readme * update to use new logger functions * ci: update * doc: update * readme: update
- Loading branch information
Showing
14 changed files
with
489 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
idf_component_register( | ||
INCLUDE_DIRS "include" | ||
SRC_DIRS "src" | ||
REQUIRES driver adc base_component bm8563 i2c interrupt led math task | ||
REQUIRED_IDF_TARGETS "esp32" | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
menu "Esp-Box Configuration" | ||
config ESP_TIMER_CAM_INTERRUPT_STACK_SIZE | ||
int "Interrupt stack size" | ||
default 4096 | ||
help | ||
Size of the stack used for the interrupt handler. | ||
endmenu |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.5) | ||
|
||
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 esp32-timer-cam" | ||
CACHE STRING | ||
"List of components to include" | ||
) | ||
|
||
project(esp_timer_cam_example) | ||
|
||
set(CMAKE_CXX_STANDARD 20) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# ESP32-TIMER-CAM Example | ||
|
||
This example shows how to use the `espp::EspTimerCam` hardware abstraction | ||
component to automatically detect and initialize components on the | ||
ESP32-TimerCam. | ||
|
||
It initializes the LED, RTC, I2C, and ADC. | ||
|
||
https://github.com/user-attachments/assets/b0c0eb28-0ce1-466d-b6e5-debbc0e7f3f6 | ||
|
||
## How to use example | ||
|
||
### Hardware Required | ||
|
||
This example is designed to run on the ESP32-TimerCam. | ||
|
||
### Build and Flash | ||
|
||
Build the project and flash it to the board, then run monitor tool to view | ||
serial output: | ||
|
||
``` | ||
idf.py -p PORT -b 1500000 flash monitor | ||
``` | ||
|
||
(Replace PORT with the name of the serial port to use.) | ||
|
||
Note: the baudrate for the ESP32 TimerCam cannot go up to 2 MBaud, so 1.5MBaud | ||
is recommended. | ||
|
||
(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 | ||
|
||
![CleanShot 2024-08-27 at 11 31 16](https://github.com/user-attachments/assets/9c3ebe48-af46-4021-afab-6235870eddf4) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
idf_component_register(SRC_DIRS "." | ||
INCLUDE_DIRS ".") |
43 changes: 43 additions & 0 deletions
43
components/esp32-timer-cam/example/main/esp_timer_cam_example.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
#include <chrono> | ||
#include <deque> | ||
#include <stdlib.h> | ||
#include <vector> | ||
|
||
#include "esp32-timer-cam.hpp" | ||
|
||
using namespace std::chrono_literals; | ||
|
||
extern "C" void app_main(void) { | ||
espp::Logger logger({.tag = "ESP32 TimerCam Example", .level = espp::Logger::Verbosity::INFO}); | ||
logger.info("Starting example!"); | ||
|
||
//! [esp timer cam example] | ||
espp::EspTimerCam &timer_cam = espp::EspTimerCam::get(); | ||
|
||
// initialize the LED | ||
static constexpr float led_breathing_period = 3.5f; | ||
if (!timer_cam.initialize_led(led_breathing_period)) { | ||
logger.error("Failed to initialize LED!"); | ||
return; | ||
} | ||
|
||
// initialize the rtc | ||
if (!timer_cam.initialize_rtc()) { | ||
logger.error("Failed to initialize RTC!"); | ||
return; | ||
} | ||
|
||
// start the LED breathing | ||
timer_cam.start_led_breathing(); | ||
|
||
// loop forever | ||
while (true) { | ||
// print out the battery voltage | ||
logger.info("Battery voltage: {:.02f} V", timer_cam.get_battery_voltage()); | ||
std::this_thread::sleep_for(100ms); | ||
// go up a line to overwrite the previous message | ||
logger.move_up(); | ||
logger.clear_line(); | ||
} | ||
//! [esp timer cam example] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
CONFIG_IDF_TARGET="esp32" | ||
|
||
CONFIG_FREERTOS_HZ=1000 | ||
|
||
# set compiler optimization level to -O2 (compile for performance) | ||
CONFIG_COMPILER_OPTIMIZATION_PERF=y | ||
|
||
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y | ||
CONFIG_ESPTOOLPY_FLASHSIZE="4MB" | ||
|
||
# ESP32-specific | ||
# | ||
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y | ||
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ=240 | ||
|
||
# Common ESP-related | ||
# | ||
CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=4096 | ||
CONFIG_ESP_MAIN_TASK_STACK_SIZE=16384 | ||
|
||
# Set esp-timer task stack size to 6KB | ||
CONFIG_ESP_TIMER_TASK_STACK_SIZE=6144 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
#pragma once | ||
|
||
#include <memory> | ||
#include <string> | ||
#include <vector> | ||
|
||
#include <driver/gpio.h> | ||
#include <driver/i2s_std.h> | ||
#include <driver/spi_master.h> | ||
#include <hal/spi_types.h> | ||
|
||
#include <freertos/FreeRTOS.h> | ||
#include <freertos/queue.h> | ||
#include <freertos/stream_buffer.h> | ||
#include <freertos/task.h> | ||
|
||
#include "base_component.hpp" | ||
#include "bm8563.hpp" | ||
#include "gaussian.hpp" | ||
#include "i2c.hpp" | ||
#include "interrupt.hpp" | ||
#include "led.hpp" | ||
#include "oneshot_adc.hpp" | ||
|
||
namespace espp { | ||
/// The EspTimerCam class provides an interface to the ESP32-S3-BOX and | ||
/// ESP32-S3-BOX-3 development boards. | ||
/// | ||
/// The class provides access to the following features: | ||
/// - I2C | ||
/// - LED | ||
/// - Battery Voltage Measurement | ||
/// - RTC | ||
/// - Camera pin definition | ||
/// | ||
/// The class is a singleton and can be accessed using the get() method. | ||
/// | ||
/// \section esp_timer_cam_example Example | ||
/// \snippet esp_timer_cam_example.cpp esp timer cam example | ||
class EspTimerCam : public BaseComponent { | ||
public: | ||
using Rtc = espp::Bm8563; | ||
|
||
/// @brief Access the singleton instance of the EspTimerCam class | ||
/// @return Reference to the singleton instance of the EspTimerCam class | ||
static EspTimerCam &get() { | ||
static EspTimerCam instance; | ||
return instance; | ||
} | ||
|
||
EspTimerCam(const EspTimerCam &) = delete; | ||
EspTimerCam &operator=(const EspTimerCam &) = delete; | ||
EspTimerCam(EspTimerCam &&) = delete; | ||
EspTimerCam &operator=(EspTimerCam &&) = delete; | ||
|
||
/// Get a reference to the internal I2C bus | ||
/// \return A reference to the internal I2C bus | ||
/// \note The internal I2C bus is used for the touchscreen and audio codec | ||
I2c &internal_i2c(); | ||
|
||
/// Get a reference to the interrupts | ||
/// \return A reference to the interrupts | ||
espp::Interrupt &interrupts(); | ||
|
||
///////////////////////////////////////////////////////////////////////////// | ||
// LED | ||
///////////////////////////////////////////////////////////////////////////// | ||
|
||
/// @brief Initialize the LED | ||
/// @param breathing_period The period of the LED breathing effect | ||
/// @return True if the LED was successfully initialized, false otherwise | ||
bool initialize_led(float breathing_period=3.5f); | ||
|
||
/// @brief Start the LED breathing effect | ||
void start_led_breathing(); | ||
|
||
/// @brief Stop the LED breathing effect | ||
void stop_led_breathing(); | ||
|
||
/// @brief Set the LED brightness | ||
/// @param brightness The brightness of the LED, in the range [0, 1] | ||
/// @return True if the LED brightness was successfully set, false otherwise | ||
/// @note The LED brightness can only be set if the LED is not currently | ||
/// breathing | ||
bool set_led_brightness(float brightness); | ||
|
||
/// @brief Get the LED brightness | ||
/// @return The brightness of the LED, in the range [0, 1] | ||
float get_led_brightness(); | ||
|
||
/// @brief Set the LED breathing period | ||
/// @param period The period of the LED breathing effect in seconds | ||
/// @return True if the LED breathing period was successfully set, false | ||
/// otherwise | ||
bool set_led_breathing_period(float period); | ||
|
||
/// @brief Get the LED breathing period | ||
/// @return The period of the LED breathing effect in seconds | ||
float get_led_breathing_period(); | ||
|
||
/// @brief Get the LED | ||
/// @return A shared pointer to the LED | ||
std::shared_ptr<espp::Led> led(); | ||
|
||
/// @brief Get the Gaussian waveform used for the LED breathing effect | ||
/// @return A reference to the Gaussian waveform used for the LED breathing | ||
/// effect | ||
espp::Gaussian &gaussian(); | ||
|
||
///////////////////////////////////////////////////////////////////////////// | ||
// Battery | ||
///////////////////////////////////////////////////////////////////////////// | ||
|
||
/// @brief Get the battery voltage | ||
/// @return The battery voltage in volts | ||
float get_battery_voltage(); | ||
|
||
///////////////////////////////////////////////////////////////////////////// | ||
// RTC | ||
///////////////////////////////////////////////////////////////////////////// | ||
|
||
/// @brief Initialize the RTC | ||
/// @return True if the RTC was successfully initialized, false otherwise | ||
bool initialize_rtc(); | ||
|
||
/// @brief Get the RTC | ||
/// @return A shared pointer to the RTC | ||
std::shared_ptr<Rtc> rtc(); | ||
|
||
protected: | ||
EspTimerCam(); | ||
float led_breathe(); | ||
bool led_task_callback(std::mutex &m, std::condition_variable &cv); | ||
|
||
// internal i2c (touchscreen, audio codec) | ||
static constexpr auto internal_i2c_port = I2C_NUM_0; | ||
static constexpr auto internal_i2c_clock_speed = 400 * 1000; | ||
static constexpr gpio_num_t internal_i2c_sda = GPIO_NUM_12; | ||
static constexpr gpio_num_t internal_i2c_scl = GPIO_NUM_14; | ||
|
||
// Camera | ||
|
||
|
||
// TODO: allow core id configuration | ||
I2c internal_i2c_{{.port = internal_i2c_port, | ||
.sda_io_num = internal_i2c_sda, | ||
.scl_io_num = internal_i2c_scl, | ||
.sda_pullup_en = GPIO_PULLUP_ENABLE, | ||
.scl_pullup_en = GPIO_PULLUP_ENABLE}}; | ||
|
||
// we'll only add each interrupt pin if the initialize method is called | ||
espp::Interrupt interrupts_{ | ||
{.interrupts = {}, | ||
.task_config = {.name = "esp timer cam interrupts", | ||
.stack_size_bytes = CONFIG_ESP_TIMER_CAM_INTERRUPT_STACK_SIZE}}}; | ||
|
||
// LED | ||
std::vector<espp::Led::ChannelConfig> led_channels_{{ | ||
.gpio = 2, | ||
.channel = LEDC_CHANNEL_5, | ||
.timer = LEDC_TIMER_2, | ||
}}; | ||
std::shared_ptr<espp::Led> led_; | ||
std::unique_ptr<espp::Task> led_task_; | ||
std::atomic<float> breathing_period_{3.5f}; | ||
std::chrono::high_resolution_clock::time_point breathing_start_{}; | ||
espp::Gaussian gaussian_{{.gamma = 0.1f, .alpha = 1.0f, .beta = 0.5f}}; | ||
|
||
// RTC | ||
std::shared_ptr<Rtc> rtc_; | ||
|
||
// Battery ADC | ||
espp::AdcConfig battery_channel_{.unit = ADC_UNIT_1, | ||
.channel = ADC_CHANNEL_2, // GPIO 38 | ||
.attenuation = ADC_ATTEN_DB_12}; | ||
|
||
// NOTE: for some reason, I cannot use Continuous ADC in combination with | ||
// esp32-camera... | ||
espp::OneshotAdc adc_{{.unit = battery_channel_.unit, | ||
.channels = {battery_channel_}, | ||
.log_level = espp::Logger::Verbosity::WARN}}; | ||
|
||
}; // class EspTimerCam | ||
} // namespace espp |
Oops, something went wrong.