5 Commits

131 changed files with 2429 additions and 760 deletions

View File

@@ -1,6 +1,6 @@
################################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## Legal: All Content (c) 2025 Shaun Reed, all rights reserved ##
## About: A root project for practicing C++ ##
## This project can be built to debug and run all nested projects ##
## Or, any subdirectory with a project() statement can be selected ##
@@ -16,10 +16,32 @@ project(
DESCRIPTION "A root project for several small cpp practice projects"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
add_compile_options("-Wall")
option(KLIPS_CCACHE "Enable ccache" ON)
if (KLIPS_CCACHE)
find_program(SCCACHE_PATH sccache)
if(SCCACHE_PATH)
message(STATUS "[Klips] Found sccache: ${SCCACHE_PATH}")
set(CMAKE_CXX_COMPILER_LAUNCHER ${SCCACHE_PATH})
set(CMAKE_C_COMPILER_LAUNCHER ${SCCACHE_PATH})
else()
message(STATUS "[Klips] Failed to find sccache, falling back to ccache.")
find_program(CCACHE_PATH ccache)
if(CCACHE_PATH)
message(STATUS "[Klips] Found ccache: ${CCACHE_PATH}")
set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE_PATH})
set(CMAKE_C_COMPILER_LAUNCHER ${CCACHE_PATH})
else()
message(WARNING "[Klips] Failed to find sccache and ccache. Compilation will not be cached.")
endif()
endif()
endif()
add_subdirectory(algorithms)
add_subdirectory(catch2)
add_subdirectory(cmake-example)
@@ -28,4 +50,15 @@ add_subdirectory(datastructs)
add_subdirectory(graphics)
add_subdirectory(multithreading)
add_subdirectory(patterns)
add_subdirectory(qt)
find_package(Qt6 COMPONENTS UiPlugin Core Gui Widgets)
if (NOT Qt6_FOUND)
message(
WARNING
"[Klips] Qt examples will not be built.\n"
"On Ubuntu 24.04 Qt6 can be installed using apt:\n"
" sudo apt install qt6-base-dev qt6-tools-dev\n"
)
else()
add_subdirectory(qt)
endif()

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "A project for practicing various algorithms in C++"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "A project for practicing algorithms using graphs in C++"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

View File

@@ -14,6 +14,7 @@ project(
DESCRIPTION "Practice implementing and using object graphs in C++"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable(
algo-graphs-object graph.cpp

View File

@@ -14,6 +14,7 @@ project(
DESCRIPTION "Practice implementing and using simple graphs in C++"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable(
algo-graphs-simple graph.cpp

View File

@@ -8,9 +8,8 @@
################################################################################
*/
#include <algorithm>
#include "lib-graph.hpp"
#include <algorithm>
void Graph::BFS(int startNode)
{

View File

@@ -15,6 +15,7 @@
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <cstdint>
class Graph {

View File

@@ -14,5 +14,6 @@ project(
DESCRIPTION "Practice implementing and using templated graphs in C++"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable(algo-graphs-templated graph.cpp)

View File

@@ -18,6 +18,7 @@
#include <unordered_set>
#include <utility>
#include <vector>
#include <cstdint>
/******************************************************************************/

View File

@@ -14,6 +14,7 @@ project(
DESCRIPTION "Practice implementing and using weighted graphs in C++"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable(
algo-graphs-weighted graph.cpp

View File

@@ -18,6 +18,7 @@
#include <unordered_set>
#include <utility>
#include <vector>
#include <cstdint>
/******************************************************************************/

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "A project for practicing various sorting algorithms in C++"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "A project for practicing insertion sort in C++"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable(
algo-sort-insertion insertion-sort.cpp

View File

@@ -14,6 +14,8 @@ project (
DESCRIPTION "A project for practicing merge sort in C++"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable(
algo-sort-merge merge-sort.cpp
lib-merge.cpp lib-merge.h

View File

@@ -12,6 +12,7 @@
#include <algorithm>
#include <iostream>
#include <vector>
#include <cstdint>
void MergeSort(std::vector<int> &array, size_t lhs, size_t rhs)
{

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "A project for practicing quick sort in C++"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable(
algo-sort-quick quick-sort.cpp

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "A project for practicing radix sort in C++"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable(
algo-sort-radix radix-sort.cpp

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "A project for practicing selection sort in C++"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable(
algo-sort-select select-sort.cpp

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "A project for practicing algorithms using trees in C++"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "A project for testing BST algorithms"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable(
algo-trees-bst driver.cpp

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "A project for testing red-black tree algorithms"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable(
algo-trees-redblack driver.cpp

View File

@@ -12,6 +12,7 @@
#define REDBLACK_H
#include <iostream>
#include <cstdint>
enum Color {Black, Red};

View File

@@ -14,18 +14,22 @@ project(
DESCRIPTION "Practice project for learning Catch2"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_compile_options(-Wall)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
Include(FetchContent)
FetchContent_Declare(
Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v3.0.1
GIT_TAG v3.4.0
)
FetchContent_MakeAvailable(Catch2)
add_subdirectory(src)
add_subdirectory(test)
add_library(klips SHARED src/klips.cpp)
target_include_directories(klips PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
add_executable(test_klips src/test_klips.cpp)
target_link_libraries(test_klips PUBLIC Catch2::Catch2WithMain klips)

View File

@@ -1,22 +0,0 @@
################################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Practice project for testing with catch2 framework ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
cmake_minimum_required(VERSION 3.15)
project(
#[[NAME]] Catch2
VERSION 1.0
DESCRIPTION "Practice project for learning Catch2"
LANGUAGES CXX
)
add_compile_options(-Wall)
add_definitions("-std=c++17")
add_library(klips SHARED klips.cpp)
target_include_directories(klips PRIVATE ${CMAKE_SOURCE_DIR}/include)

View File

@@ -2,7 +2,8 @@
#include <iostream>
#include "../bin/catch.hpp"
#include "catch2/catch_all.hpp"
#include "klips.hpp"
#include "type_name.hpp"
@@ -139,7 +140,7 @@ template <> template <bool must_find> void test_config_get<std::string>::run() {
TEMPLATE_PRODUCT_TEST_CASE("Test", "[test]", test_config_get,
(int, std::string)) {
TT();
TestType t;
// TestType t;
test_config_get<int> s;
s.template run<true>();
// TestType t;

View File

@@ -1,22 +0,0 @@
################################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## About: Practice project for testing with catch2 framework ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
cmake_minimum_required(VERSION 3.15)
project(
#[[NAME]] Catch2
VERSION 1.0
DESCRIPTION "Practice project for learning Catch2"
LANGUAGES CXX
)
add_compile_options(-Wall)
add_executable(test_klips test_klips.cpp)
target_link_libraries(test_klips PRIVATE Catch2::Catch2WithMain klips)
target_include_directories(test_klips PRIVATE ${CMAKE_SOURCE_DIR}/include)

View File

@@ -22,6 +22,7 @@ project (
DESCRIPTION "A basic CMake template for C++ projects"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
# Include any directories the compiler may need
include_directories(./include)

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "A project for practicing cryptography in C++"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

View File

@@ -14,6 +14,7 @@ project(
DESCRIPTION "Practice implementing columnar transposition in C++"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable(
crypto-columnar-transposition driver.cpp

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "A project for practicing various data structures in C++"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "A project for testing a basic implementation of a BST"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable(
data-bst driver.cpp

View File

@@ -10,10 +10,11 @@
#include "bst.h"
#include <cstdint>
/********************************************************************************
* Constructors, Destructors, Operators
*********************************************************************************/
* Constructors, Destructors, Operators
*********************************************************************************/
/** Copy Assignment Operator
* @brief Empty the calling object's root BinaryNode, and copy the rhs data

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "Project for testing circular doubly linked list implementation"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable(
data-circular-doubly-linked-list driver.cpp

View File

@@ -10,6 +10,7 @@
#include "circledoublelist.h"
#include <cstdint>
/******************************************************************************
* Constructors, Destructors, Operators

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "Project for testing circular singly linked list implementation"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable(
data-circular-singly-linked-list driver.cpp

View File

@@ -10,6 +10,7 @@
#include "circlesinglelist.h"
#include <cstdint>
/******************************************************************************
* Constructors, Destructors, Operators

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "A project for testing a doubly linked list implementation"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable(
data-doubly-linked-list driver.cpp

View File

@@ -10,6 +10,7 @@
#include "doublelist.h"
#include <cstdint>
/******************************************************************************
* Constructors, Destructors, Operators

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "A project for testing a max heap implementation"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable(
data-max-heap driver.cpp

View File

@@ -10,10 +10,11 @@
#include "maxheap.h"
#include <cstdint>
/********************************************************************************
* Constructors, Destructors, Operators
*********************************************************************************/
* Constructors, Destructors, Operators
*********************************************************************************/
/** default constructor
* Constructs a heap with the given default values

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "Project for testing queue implementation"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable(
data-queue driver.cpp

View File

@@ -10,6 +10,7 @@
#include "queuelist.h"
#include <cstdint>
/******************************************************************************
* Constructors, Destructors, Operators

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "A project for testing a singly linked list implementation"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable(
data-singly-linked-list driver.cpp

View File

@@ -10,6 +10,7 @@
#include "singlelist.h"
#include <cstdint>
/******************************************************************************
* Constructors, Destructors, Operators

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "A project for testing a Stack implementation"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable(
data-stack driver.cpp

View File

@@ -10,6 +10,7 @@
#include "stacklist.h"
#include <cstdint>
/******************************************************************************
* Constructors, Destructors, Operators

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "A project for practicing templated data structures in C++"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_subdirectory(doublelist)
add_subdirectory(queuelist)

View File

@@ -14,5 +14,6 @@ project (
DESCRIPTION "A project for practicing templated doubly linked list implementations"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable(data-templates-doubly-linked-list driver.cpp)

View File

@@ -14,5 +14,6 @@ project (
DESCRIPTION "A project for practicing templated queue implementations"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable(data-templates-queue driver.cpp)

View File

@@ -14,5 +14,6 @@ project (
DESCRIPTION "A project for practicing templated Stack implementations"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable(data-templates-stack driver.cpp)

View File

@@ -14,5 +14,6 @@ project (
DESCRIPTION "A project for practicing templated Vector implementations"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable(data-templates-vectors driver.cpp)

View File

@@ -14,6 +14,7 @@ project (
DESCRIPTION "A project for testing a basic Vector implementation"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable(
data-vectors driver.cpp

View File

@@ -10,6 +10,7 @@
#include "vector.h"
#include <cstdint>
/******************************************************************************
* Constructors, Destructors, Operators

View File

@@ -1,6 +1,6 @@
################################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ##
## Legal: All Content (c) 2025 Shaun Reed, all rights reserved ##
## About: A root project for practicing C++ ##
## This project can be built to debug and run all nested projects ##
## Or, any subdirectory with a project() statement can be selected ##
@@ -16,6 +16,7 @@ project(
DESCRIPTION "A root project for practicing graphics programming in C++"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

View File

@@ -1,6 +1,6 @@
################################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ##
## Legal: All Content (c) 2025 Shaun Reed, all rights reserved ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
@@ -8,40 +8,46 @@
# Define CMake version
cmake_minimum_required(VERSION 3.15)
include(FetchContent)
project(
#[[NAME]] OpenGL-Cmake
DESCRIPTION "Example project for building OpenGL projects with CMake"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_library(graphics-lib-opengl src/lib-opengl-test.cpp)
target_include_directories(graphics-lib-opengl PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src)
# Find OpenGL package
find_package(OpenGL REQUIRED)
if (OPENGL_FOUND)
# Link opengl-test executable to OpenGL
target_include_directories(graphics-lib-opengl PUBLIC ${OPENGL_INCLUDE_DIR})
target_link_libraries(graphics-lib-opengl PUBLIC ${OPENGL_LIBRARIES})
else()
find_package(OpenGL)
if (NOT OPENGL_FOUND)
message(
"Error: CMake was unable to find the OpenGL package\n"
"Please install OpenGL and try again\n"
"[Klips] Error: CMake was unable to find OpenGL.\n"
"On Ubuntu 24.04 OpenGL can be installed using apt:\n"
" sudo apt install libopengl-dev libgl1-mesa-dev mesa-common-dev libglu1-mesa-dev\n"
)
endif()
# Link opengl-test executable to OpenGL
message(STATUS "[Klips] Found OpenGL: ${OPENGL_INCLUDE_DIR}")
target_include_directories(graphics-lib-opengl PUBLIC ${OPENGL_INCLUDE_DIR})
target_link_libraries(graphics-lib-opengl PUBLIC ${OPENGL_LIBRARIES})
# Find GLUT package
find_package(GLUT REQUIRED)
if (GLUT_FOUND)
# Link lib-opengl-test executable to GLUT
target_include_directories(graphics-lib-opengl PUBLIC ${GLUT_INCLUDE_DIR})
target_link_libraries(graphics-lib-opengl PUBLIC ${GLUT_LIBRARIES})
else()
find_package(GLUT QUIET)
if(NOT GLUT_FOUND)
message(
"Error: CMake was unable to find the GLUT package\n"
"Please install GLUT (freeglut3-dev) and try again\n"
FATAL_ERROR
"[Klips] Failed to fetch GLUT. Could not find dependency X11 input libraries.\n"
"On Ubuntu 24.04 Xi can be installed using apt:\n"
" sudo apt install libxi-dev\n"
"Alternatively, on Ubuntu 24.04 GLUT can be installed with apt:\n"
" sudo apt install freeglut3-dev\n"
)
endif()
message(STATUS "[Klips] Found GLUT: ${GLUT_INCLUDE_DIR}")
# Link lib-opengl-test executable to GLUT
target_include_directories(graphics-lib-opengl PUBLIC ${GLUT_INCLUDE_DIR})
target_link_libraries(graphics-lib-opengl PUBLIC ${GLUT_LIBRARIES})
# Add test executable
add_executable(graphics-cmake-opengl apps/test-gl.cpp)

View File

@@ -1,6 +1,6 @@
################################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2021 Shaun Reed, all rights reserved ##
## Legal: All Content (c) 2025 Shaun Reed, all rights reserved ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
################################################################################
@@ -13,6 +13,7 @@ project(
DESCRIPTION "Example project for building SDL projects with CMake"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
# Add Library
add_library(
@@ -27,31 +28,30 @@ target_include_directories( # When calling library, include a directo
)
# Search for SDL2 package
find_package(SDL2 REQUIRED sdl2)
# If SDL2 was found successfully, link to lib-sdl-test
if (SDL2_FOUND)
# Any target that links with this library will also link to SDL2
# + Because we choose PUBLIC visibility
target_include_directories(graphics-lib-sdl PUBLIC ${SDL2_INCLUDE_DIRS})
target_link_libraries(graphics-lib-sdl PUBLIC "${SDL2_LIBRARIES}")
# Creating executable
add_executable(
graphics-cmake-sdl # Exe name
apps/sdl-test.cpp # Exe Source(s)
)
# Linking the exe to library
target_link_libraries(
graphics-cmake-sdl # Executable to link
PRIVATE # Visibility
graphics-lib-sdl # Library to link
)
else()
message(
"Error: CMake was unable to find SDL2 package.\n"
"Please install the libsdl2-dev package and try again.\n"
find_package(SDL2 QUIET)
if (NOT SDL2_FOUND)
message(FATAL_ERROR
"[Klips] Failed to find SDL2.\n"
"On Ubuntu 24.04 SDL2 can be installed using apt:\n"
" sudo apt install libsdl2-dev\n"
)
endif()
message(STATUS "[Klips] Found SDL2: ${SDL2_INCLUDE_DIRS}")
# Any target that links with this library will also link to SDL2
# + Because we choose PUBLIC visibility
target_include_directories(graphics-lib-sdl PUBLIC ${SDL2_INCLUDE_DIRS})
target_link_libraries(graphics-lib-sdl PUBLIC "${SDL2_LIBRARIES}")
# Creating executable
add_executable(
graphics-cmake-sdl # Exe name
apps/sdl-test.cpp # Exe Source(s)
)
# Linking the exe to library
target_link_libraries(
graphics-cmake-sdl # Executable to link
PRIVATE # Visibility
graphics-lib-sdl # Library to link
)

View File

@@ -14,6 +14,7 @@ project(
DESCRIPTION "Practice with multithreaded programming in C++"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
add_compile_options("-Wall")

View File

@@ -18,6 +18,7 @@ project(
DESCRIPTION "Example of condition_variables in multithreaded C++"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable(
multithread-conditions driver.cpp

View File

@@ -18,6 +18,7 @@ project(
DESCRIPTION "Example and solution for deadlocks in C++"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable(
multithread-deadlock driver.cpp

View File

@@ -18,6 +18,7 @@ project(
DESCRIPTION "Example and solution for livelocks in C++"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable(
multithread-livelock driver.cpp

View File

@@ -14,6 +14,7 @@ project(
DESCRIPTION "Example and solution for race conditions"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable(
multithread-race-condition driver.cpp

View File

@@ -14,6 +14,7 @@ project(
DESCRIPTION "A project for practicing various design patterns in C++"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

View File

@@ -13,6 +13,7 @@ project(
DESCRIPTION "An example of the abstract factory design pattern in C++"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_compile_options("-Wall")
add_executable(

View File

@@ -14,6 +14,7 @@ project(
DESCRIPTION "An example of the adapter design pattern in C++"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_compile_options("-Wall")
add_executable(

View File

@@ -2,6 +2,7 @@
#ifndef ADAPTER_HPP
#define ADAPTER_HPP
#include <ctime>
#include <random>
// Target implementation to adapt to a new interface

View File

@@ -14,6 +14,7 @@ project(
DESCRIPTION "An example of the bridge design pattern in C++"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_compile_options("-Wall")
add_executable(

View File

@@ -14,6 +14,7 @@ project(
DESCRIPTION "An example of the factory design pattern in C++"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_compile_options("-Wall")
add_executable(

View File

@@ -14,6 +14,7 @@ project(
DESCRIPTION "An example of the state design pattern in C++"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_compile_options("-Wall")
add_executable(

View File

@@ -14,6 +14,7 @@ project(
DESCRIPTION "An example of the prototype design pattern in C++"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_compile_options("-Wall")
add_executable(

View File

@@ -14,6 +14,7 @@ project(
DESCRIPTION "An example of the singleton design pattern in C++"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_executable(
patterns-singleton main.cpp

View File

@@ -14,6 +14,7 @@ project(
DESCRIPTION "An example of the state design pattern in C++"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_compile_options("-Wall")
add_executable(

View File

@@ -14,6 +14,7 @@ project(
DESCRIPTION "An example of the visitor design pattern in C++"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_compile_options("-Wall")
add_executable(

View File

@@ -14,6 +14,7 @@ project(
DESCRIPTION "A root project for several small Qt6 practice projects"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
add_compile_options("-Wall")

View File

@@ -14,6 +14,9 @@ project(
DESCRIPTION "Example of a widget plugin collection for Qt Designer"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
# Lowercase string to use as a slug for executable names for identification.
string(TOLOWER ${PROJECT_NAME} PROJECT_NAME_LOWER)
include(GenerateExportHeader)
@@ -51,43 +54,56 @@ endif()
set(QT_INSTALL_DIR "${QT_DIR}/6.3.1/gcc_64/" CACHE PATH "Path to Qt6 install")
list(APPEND CMAKE_PREFIX_PATH "${QT_INSTALL_DIR}")
find_package(Qt6 REQUIRED COMPONENTS UiPlugin Core Gui Widgets)
find_package(Qt6 COMPONENTS UiPlugin Core Gui Widgets)
if (NOT Qt6_FOUND)
message(
FATAL_ERROR
"[Klips] Error: CMake was unable to find Qt6 libraries.\n"
"The example will not be built until the build is configured with these packages installed.\n"
"On Ubuntu 24.04 Qt6 can be installed using apt:\n"
" sudo apt-get install qt6-base-dev qt6-tools-dev\n"
)
endif()
# Creating a library with two plugins for the collection.
qt_add_library(widget-plugin-library
set(WIDGET_PLUGIN_LIBRARY widget-plugin-library_${PROJECT_NAME_LOWER})
qt_add_library(${WIDGET_PLUGIN_LIBRARY}
textview.cpp textview.h
widgetplugin.cpp widgetplugin.h
)
target_sources(widget-plugin-library PRIVATE
target_sources(${WIDGET_PLUGIN_LIBRARY} PRIVATE
textview.cpp textview.h
treeview.cpp treeview.h
widgetplugin.cpp widgetplugin.h
)
set_target_properties(widget-plugin-library PROPERTIES
set_target_properties(${WIDGET_PLUGIN_LIBRARY} PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
target_link_libraries(widget-plugin-library
target_link_libraries(${WIDGET_PLUGIN_LIBRARY}
PUBLIC Qt::UiPlugin Qt::Core Qt::Gui Qt::Widgets
)
install(TARGETS widget-plugin-library
install(TARGETS ${WIDGET_PLUGIN_LIBRARY}
RUNTIME DESTINATION "${QT_PLUGIN_LIBRARY_DIR}"
BUNDLE DESTINATION "${QT_PLUGIN_LIBRARY_DIR}"
LIBRARY DESTINATION "${QT_PLUGIN_LIBRARY_DIR}"
)
generate_export_header(widget-plugin-library)
generate_export_header(${WIDGET_PLUGIN_LIBRARY}
BASE_NAME widget_plugin_library
EXPORT_FILE_NAME "${CMAKE_CURRENT_BINARY_DIR}/widget-plugin-library_export.h"
)
# Creating the collection
qt_add_library(widget-plugin-collection
set(WIDGET_PLUGIN_COLLECTION widget-plugin-collection_${PROJECT_NAME_LOWER})
qt_add_library(${WIDGET_PLUGIN_COLLECTION}
widgetplugincollection.cpp widgetplugincollection.h
)
target_link_libraries(widget-plugin-collection
Qt6::Widgets Qt6::UiPlugin widget-plugin-library
target_link_libraries(${WIDGET_PLUGIN_COLLECTION}
Qt6::Widgets Qt6::UiPlugin ${WIDGET_PLUGIN_LIBRARY}
)
install(TARGETS widget-plugin-collection
install(TARGETS ${WIDGET_PLUGIN_COLLECTION}
RUNTIME DESTINATION "${QT_PLUGIN_INSTALL_DIR}"
BUNDLE DESTINATION "${QT_PLUGIN_INSTALL_DIR}"
LIBRARY DESTINATION "${QT_PLUGIN_INSTALL_DIR}"
@@ -101,10 +117,11 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/app-dir.h.in"
@ONLY
)
qt_add_executable(widget-app
set(WIDGET_APP widget-app_${PROJECT_NAME_LOWER})
qt_add_executable(${WIDGET_APP}
widgetapp.cpp widgetapp.h widgetapp.ui
main.cpp
)
target_link_libraries(widget-app
PRIVATE Qt::Widgets widget-plugin-library
target_link_libraries(${WIDGET_APP}
PRIVATE Qt::Widgets ${WIDGET_PLUGIN_LIBRARY}
)

View File

@@ -1,6 +1,6 @@
#ifndef APPDIR_H_IN
#define APPDIR_H_IN
#define APP_DIR "/home/kapper/Code/klips/cpp/qt/designer-plugin-collection"
#define APP_DIR "/media/shaun/Storage/Code/klips/cpp/qt/designer-plugin-collection"
#endif // APPDIR_H_IN

View File

@@ -1,6 +1,6 @@
################################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## Legal: All Content (c) 2025 Shaun Reed, all rights reserved ##
## About: Example of making widget plugins for Qt Designer ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
@@ -14,6 +14,9 @@ project(
DESCRIPTION "Example of a widget plugin for Qt Designer"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
# Lowercase string to use as a slug for executable names for identification.
string(TOLOWER ${PROJECT_NAME} PROJECT_NAME_LOWER)
include(GenerateExportHeader)
@@ -42,35 +45,44 @@ endif()
set(QT_INSTALL_DIR "${QT_DIR}/6.3.1/gcc_64/" CACHE PATH "Path to Qt6 install")
list(APPEND CMAKE_PREFIX_PATH "${QT_INSTALL_DIR}")
find_package(Qt6 REQUIRED COMPONENTS UiPlugin Core Gui Widgets)
find_package(Qt6 COMPONENTS UiPlugin Core Gui Widgets)
if (NOT Qt6_FOUND)
message(
FATAL_ERROR
"[Klips] Error: CMake was unable to find Qt6 libraries.\n"
"The example will not be built until the build is configured with these packages installed.\n"
"On Ubuntu 24.04 Qt6 can be installed using apt:\n"
" sudo apt-get install qt6-base-dev qt6-tools-dev\n"
)
endif()
# Creating the plugin
qt_add_library(widget-plugin)
target_sources(widget-plugin PRIVATE
set(WIDGET_PLUGIN widget-plugin_${PROJECT_NAME_LOWER})
qt_add_library(${WIDGET_PLUGIN})
target_sources(${WIDGET_PLUGIN} PRIVATE
text-view.cpp text-view.h
widget-plugin.cpp widget-plugin.h
)
set_target_properties(widget-plugin PROPERTIES
set_target_properties(${WIDGET_PLUGIN} PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
target_link_libraries(widget-plugin PUBLIC
target_link_libraries(${WIDGET_PLUGIN} PUBLIC
Qt::UiPlugin Qt::Core Qt::Gui Qt::Widgets
)
install(TARGETS widget-plugin
install(TARGETS ${WIDGET_PLUGIN}
RUNTIME DESTINATION "${QT_PLUGIN_INSTALL_DIR}"
BUNDLE DESTINATION "${QT_PLUGIN_INSTALL_DIR}"
LIBRARY DESTINATION "${QT_PLUGIN_INSTALL_DIR}"
)
# Application that will use the widget plugin
qt_add_executable(widget-app
set(WIDGET_APP widget-app_${PROJECT_NAME_LOWER})
qt_add_executable(${WIDGET_APP}
widget-app.cpp widget-app.h widget-app.ui
main.cpp
)
target_link_libraries(widget-app PRIVATE
Qt::Widgets widget-plugin
target_link_libraries(${WIDGET_APP} PRIVATE
Qt::Widgets ${WIDGET_PLUGIN}
)

View File

@@ -1,6 +1,6 @@
################################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## Legal: All Content (c) 2025 Shaun Reed, all rights reserved ##
## About: Practice project for using Qt Designer with custom C++ widgets ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
@@ -14,6 +14,7 @@ project(
DESCRIPTION "Practice using Qt designer for desktop applications"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_compile_options(-Wall)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
@@ -28,7 +29,16 @@ set(QT_DIR "$ENV{HOME}/Code/Clones/Qt/6.3.1/gcc_64/" CACHE PATH "Path to Qt6")
list(APPEND CMAKE_PREFIX_PATH "${QT_DIR}")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
find_package(Qt6 COMPONENTS Core Gui Widgets)
if (NOT Qt6_FOUND)
message(
FATAL_ERROR
"[Klips] Error: CMake was unable to find Qt6 libraries.\n"
"The example will not be built until the build is configured with these packages installed.\n"
"On Ubuntu 24.04 Qt6 can be installed using apt:\n"
" sudo apt-get install qt6-base-dev qt6-tools-dev\n"
)
endif()
qt_add_executable(designer
designer.cpp designer.h designer.ui

View File

@@ -1,6 +1,6 @@
################################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2022 Shaun Reed, all rights reserved ##
## Legal: All Content (c) 2025 Shaun Reed, all rights reserved ##
## About: Practice project for using signals and slots in Qt ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com | GitHub: shaunrd0 ##
@@ -14,6 +14,7 @@ project(
DESCRIPTION "Practice using signals and slots in Qt 6"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
add_compile_options(-Wall)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
@@ -28,7 +29,16 @@ set(QT_DIR "$ENV{HOME}/Code/Clones/Qt/6.3.1/gcc_64/" CACHE PATH "Path to Qt6")
list(APPEND CMAKE_PREFIX_PATH "${QT_DIR}")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
find_package(Qt6 COMPONENTS Core Gui Widgets)
if (NOT Qt6_FOUND)
message(
FATAL_ERROR
"[Klips] Error: CMake was unable to find Qt6 libraries.\n"
"The example will not be built until the build is configured with these packages installed.\n"
"On Ubuntu 24.04 Qt6 can be installed using apt:\n"
" sudo apt-get install qt6-base-dev qt6-tools-dev\n"
)
endif()
qt_add_executable(slots
text-view.cpp text-view.h

View File

@@ -22,7 +22,7 @@ public:
public:
signals:
void sendTest()QWidget;
void sendTest();
private:
signals:

8
esp/README.md Normal file
View File

@@ -0,0 +1,8 @@
# esp
```bash
shaunrd0/klips/esp/
├── cpp # Examples of ESP32 projects written in C++
├── rust # Examples of ESP32 projects written in Rust
└── README.md
```

View File

@@ -12,6 +12,7 @@ project(
DESCRIPTION "Example ESP-IDF cmake project"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
# For writing pure cmake components, see the documentation
# https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-guides/build-system.html#writing-pure-cmake-components

View File

@@ -12,6 +12,7 @@ project(
DESCRIPTION "Temperature and humidity from DHT sensor served on a web page"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
# For writing pure cmake components, see the documentation
# https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-guides/build-system.html#writing-pure-cmake-components

View File

@@ -12,6 +12,7 @@ project(
DESCRIPTION "Simple I2C device scanner"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
# For writing pure cmake components, see the documentation
# https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-guides/build-system.html#writing-pure-cmake-components
idf_build_set_property(COMPILE_OPTIONS "-Wno-error" APPEND)

View File

@@ -2,7 +2,7 @@
dependencies:
## Required IDF version
idf:
version: '>=4.1.0'
version: '>=5.3.0'
# # Put list of dependencies here
# # For components maintained by Espressif:
# component: "~1.0.0"

View File

@@ -2091,6 +2091,15 @@ CONFIG_MDNS_TASK_STACK_SIZE=4096
CONFIG_MDNS_TASK_AFFINITY_CPU0=y
# CONFIG_MDNS_TASK_AFFINITY_CPU1 is not set
CONFIG_MDNS_TASK_AFFINITY=0x0
#
# MDNS Memory Configuration
#
CONFIG_MDNS_TASK_CREATE_FROM_INTERNAL=y
CONFIG_MDNS_MEMORY_ALLOC_INTERNAL=y
# CONFIG_MDNS_MEMORY_CUSTOM_IMPL is not set
# end of MDNS Memory Configuration
CONFIG_MDNS_SERVICE_ADD_TIMEOUT_MS=2000
CONFIG_MDNS_TIMER_PERIOD_MS=100
# CONFIG_MDNS_NETWORKING_SOCKET is not set

View File

@@ -12,6 +12,7 @@ project(
DESCRIPTION "Using the SSD1306 LCD display with ESP-IDF and LVGL over I2C"
LANGUAGES CXX
)
message(STATUS "[Klips] Configuring example: ${PROJECT_NAME}")
# For writing pure cmake components, see the documentation
# https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-guides/build-system.html#writing-pure-cmake-components
idf_build_set_property(COMPILE_OPTIONS "-Wno-error" APPEND)

View File

@@ -2,8 +2,6 @@
Using the ESP IDF for drawing to a LCD screen over I2C.
For instructions on setting up the ESP-IDF see [04_-esp-idf-arduino](./../04_esp-idf-arduino)
[ESP IDF - I2C](https://docs.espressif.com/projects/esp-idf/en/v5.3.2/esp32/api-reference/peripherals/i2c.html)
[ESP IDF - LCD](https://docs.espressif.com/projects/esp-idf/en/v5.3.2/esp32/api-reference/peripherals/lcd/index.html)
@@ -12,10 +10,10 @@ For instructions on setting up the ESP-IDF see [04_-esp-idf-arduino](./../04_esp
![schematic](./schematic.png)
Temperature and humidity sensor served on a web page within the local network.
![example](./example.gif)
For instructions on setting up the ESP-IDF see [04_-esp-idf-arduino](./../04_esp-idf-arduino)
To build this example run the following commands.
```bash

View File

@@ -1,5 +1,7 @@
idf_component_register(
SRCS main.cpp display.cpp ssd1306.cpp i2c.h time_keeper.h panel.h panel_device.h
SRCS
main.cpp display.cpp panel_device.cpp scoped_lock.cpp
i2c.h time_keeper.h panel.h ssd1306.h
INCLUDE_DIRS .
REQUIRES driver
)

View File

@@ -1,28 +1,19 @@
#include <esp_timer.h>
/*#############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2025 Shaun Reed, all rights reserved ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com ##
##############################################################################
*/
#include <lv_init.h>
#include <mutex>
#include "display.h"
// TODO: Remove this dependency by relocating SSD1306::oledb_buffer_
#include "ssd1306.h"
// LVGL library is not thread-safe, this example calls LVGL APIs from tasks.
// We must use a mutex to protect it.
_lock_t Display::ScopedLock::lv_lock_;
// Static TimeKeeper for managing ESP timers across all displays.
TimeKeeper Display::timers_;
/// Tag used for ESP logging.
const char * TAG = "Display";
Display::Display(IPanelDevice &device) :
panel_(device),
lv_buf_(nullptr)
panel_(device)
{
if (!lv_is_initialized()) {
ESP_LOGI(TAG, "Initialize LVGL");
@@ -34,12 +25,7 @@ Display::Display(IPanelDevice &device) :
// associate the i2c panel handle to the display
lv_display_set_user_data(lv_display_, panel_.esp_panel_);
register_draw_buffer();
register_lvgl_tick_timer();
ESP_LOGI(TAG, "Create LVGL FreeRTOS task");
xTaskCreate(Display::lvgl_port_task, "LVGL", LVGL_TASK_STACK_SIZE,
nullptr, LVGL_TASK_PRIORITY, nullptr);
panel_.register_display_callbacks(lv_display_);
}
void Display::set_text(const char *text,
@@ -59,7 +45,7 @@ void Display::set_text(const char *text,
}
auto obj = lv_objects_[name];
// Circular scroll.
// Set text and long mode.
lv_label_set_long_mode(obj, long_mode);
lv_label_set_text(obj, text);
@@ -68,110 +54,3 @@ void Display::set_text(const char *text,
lv_obj_set_width(obj, lv_display_get_horizontal_resolution(lv_display_));
lv_obj_align(obj, align, 0, 0);
}
bool Display::lvgl_flush_ready_cb(esp_lcd_panel_io_handle_t,
esp_lcd_panel_io_event_data_t *,
void *user_ctx)
{
auto *disp = (lv_display_t *) user_ctx;
lv_display_flush_ready(disp);
return false;
}
void Display::lvgl_flush_cb(lv_display_t *display, const lv_area_t *area,
uint8_t *px_map) // NOLINT(*-non-const-parameter)
{
auto panel_handle =
(esp_lcd_panel_handle_t) lv_display_get_user_data(display);
// Necessary because LVGL reserves 2x4 bytes in the buffer for a palette.
// For more information about the monochrome, please refer to:
// https://docs.lvgl.io/9.2/porting/display.html#monochrome-displays
// Skip the palette here.
px_map += LVGL_PALETTE_SIZE;
uint16_t hor_res = lv_display_get_physical_horizontal_resolution(display);
int32_t x1 = area->x1;
int32_t x2 = area->x2;
int32_t y1 = area->y1;
int32_t y2 = area->y2;
for (int32_t y = y1; y <= y2; y++) {
for (int32_t x = x1; x <= x2; x++) {
/* The order of bits is MSB first.
MSB LSB
bits 7 6 5 4 3 2 1 0
pixels 0 1 2 3 4 5 6 7
Left Right
*/
bool chroma_color = (px_map[(hor_res >> 3) * y + (x >> 3)] &
1 << (7 - x % 8));
// Write to the buffer as required for the display.
// It writes only 1-bit for monochrome displays mapped vertically.
uint8_t *buf = SSD1306::oled_buffer_ + hor_res * (y >> 3) + (x);
if (chroma_color) {
(*buf) &= ~(1 << (y % 8));
} else {
(*buf) |= (1 << (y % 8));
}
}
}
// Pass the draw buffer to the driver.
ESP_ERROR_CHECK(
esp_lcd_panel_draw_bitmap(panel_handle, x1, y1, x2 + 1, y2 + 1,
SSD1306::oled_buffer_));
}
void Display::lvgl_increase_tick_cb(void *)
{
// Tell LVGL how many milliseconds has elapsed
lv_tick_inc(LVGL_TICK_PERIOD_MS);
}
[[noreturn]] void Display::lvgl_port_task(void *)
{
ESP_LOGI(TAG, "Starting LVGL task");
for (uint32_t time_to_next_ms = 0; true; usleep(1000 * time_to_next_ms)) {
ScopedLock lock;
time_to_next_ms = lv_timer_handler();
}
}
void Display::register_draw_buffer()
{
// Create draw buffer.
ESP_LOGI(TAG, "Allocate separate LVGL draw buffers");
lv_buf_ = heap_caps_calloc(1, panel_.device_->lv_buf_size_,
MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
assert(lv_buf_);
ESP_LOGI(TAG, "Set LVGL draw buffers");
// Color format must be set first, LVGL9 suooprt new monochromatic format.
lv_display_set_color_format(lv_display_, LV_COLOR_FORMAT_I1);
lv_display_set_buffers(lv_display_, lv_buf_, nullptr,
panel_.device_->lv_buf_size_,
LV_DISPLAY_RENDER_MODE_FULL);
lv_display_set_rotation(lv_display_, LV_DISPLAY_ROTATION_0);
ESP_LOGI(TAG, "Set LVGL callback for flushing to the display");
lv_display_set_flush_cb(lv_display_, Display::lvgl_flush_cb);
ESP_LOGI(TAG, "Register io panel callback for LVGL flush ready notification");
const esp_lcd_panel_io_callbacks_t cbs = {
.on_color_trans_done = Display::lvgl_flush_ready_cb,
};
ESP_ERROR_CHECK(
esp_lcd_panel_io_register_event_callbacks(panel_.esp_io_, &cbs,
lv_display_));
}
void Display::register_lvgl_tick_timer()
{
ESP_LOGI(TAG, "Use esp_timer to increase LVGL tick");
const esp_timer_create_args_t esp_timer_args = {
.callback = &Display::lvgl_increase_tick_cb,
.name = "lvgl_tick"
};
timers_.start_new_timer_periodic(esp_timer_args, LVGL_TICK_PERIOD_MS * 1000);
}

View File

@@ -1,17 +1,20 @@
/*#############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2025 Shaun Reed, all rights reserved ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com ##
##############################################################################
*/
#ifndef DISPLAY_H
#define DISPLAY_H
#include <widgets/label/lv_label.h>
#include <unordered_map>
#include "time_keeper.h"
#include "panel.h"
#define LVGL_TICK_PERIOD_MS 5
#define LVGL_TASK_STACK_SIZE (4 * 1024)
#define LVGL_TASK_PRIORITY 2
#include "scoped_lock.h"
/**
* Encapsulates lv_display handle and related LVGL operations.
@@ -37,6 +40,8 @@ public:
Display &operator=(Display &) = delete;
using lv_display_handle_t = lv_display_t *;
//
// GETTERS
@@ -45,20 +50,20 @@ public:
*
* @sa ScopedLock for calling custom LVGL API's not implemented by Display.
*/
[[nodiscard]] inline const lv_display_t *get() const { return lv_display_; }
[[nodiscard]] inline lv_display_handle_t get() const { return lv_display_; }
/**
* Getter for accessing LVGL display handle.
*
* @sa ScopedLock for calling custom LVGL API's not implemented by Display.
*/
[[nodiscard]] inline lv_display_t *get() { return lv_display_; }
[[nodiscard]] inline lv_display_handle_t get() { return lv_display_; }
/// Dereference operator for accessing LVGL display handle.
[[nodiscard]] inline const lv_display_t *operator*() const { return get(); }
[[nodiscard]] inline lv_display_handle_t operator*() const { return get(); }
/// Dereference operator for accessing LVGL display handle.
[[nodiscard]] inline lv_display_t *operator*() { return get(); }
[[nodiscard]] inline lv_display_handle_t operator*() { return get(); }
//
// LVGL OPERATIONS
@@ -77,23 +82,6 @@ public:
lv_label_long_mode_t long_mode = LV_LABEL_LONG_SCROLL_CIRCULAR,
lv_align_t align = LV_ALIGN_TOP_MID);
//
// TYPE DEFINITIONS
/**
* Obtains LVGL API mutex lock for the duration of local scope.
*
* LVGL library is not thread-safe, this example calls LVGL APIs from tasks.
*/
struct ScopedLock {
explicit ScopedLock() { _lock_acquire(&lv_lock_); }
~ScopedLock() { _lock_release(&lv_lock_); }
/// Mutex used to protect LVGL API calls.
static _lock_t lv_lock_;
};
//
// PUBLIC STATIC MEMBERS
@@ -101,23 +89,6 @@ public:
static TimeKeeper timers_;
private:
/// Registers LVGL draw buffers for this display.
void register_draw_buffer();
/// Registers LVGL ticker timer callback for rendering this display.
static void register_lvgl_tick_timer();
static bool lvgl_flush_ready_cb(esp_lcd_panel_io_handle_t panel,
esp_lcd_panel_io_event_data_t *data,
void *user_ctx);
static void lvgl_flush_cb(lv_display_t *display,
const lv_area_t *area,
uint8_t *px_map);
static void lvgl_increase_tick_cb(void *arg);
[[noreturn]] static void lvgl_port_task(void *arg);
//
// PRIVATE MEMBERS
@@ -126,10 +97,7 @@ private:
Panel panel_;
/// LVGL display handle.
lv_display_t *lv_display_;
/// LVGL draw buffer associated with this Display's lv_display_t.
void *lv_buf_;
lv_display_handle_t lv_display_;
/**
* LVGL object handles stored in the LVGL screen associated with this Display.
@@ -138,6 +106,9 @@ private:
* @sa lv_display_get_screen_active
*/
std::unordered_map<const char *, lv_obj_t *> lv_objects_;
/// Tag used for ESP logging.
constexpr static const char *TAG = "Display";
};
#endif // DISPLAY_H

View File

@@ -1,3 +1,10 @@
/*#############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2025 Shaun Reed, all rights reserved ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com ##
##############################################################################
*/
#ifndef I2C_H
#define I2C_H
@@ -49,6 +56,9 @@ struct I2C {
~I2C() = default;
//
// GETTERS
/**
* ESP I2C master bus handle getter.
* This will fail if an I2C instance was never constructed.
@@ -60,6 +70,9 @@ struct I2C {
return i2c;
}
//
// PUBLIC MEMBERS
/// ESP I2C master bus configuration used during initialization.
i2c_master_bus_config_t esp_bus_config_;
@@ -67,8 +80,12 @@ struct I2C {
int rst_num_;
private:
//
// PRIVATE MEMBERS
/// Tag used for ESP logging.
const char * TAG = "I2C";
constexpr static const char *TAG = "I2C";
};
#endif //I2C_H

View File

@@ -1,5 +1,5 @@
## IDF Component Manager Manifest File
dependencies:
espressif/arduino-esp32: ^3.1.1
idf: '>=5.3.0'
lvgl/lvgl: "9.2.0"
esp_lcd_sh1107: "^1"

View File

@@ -1,3 +1,10 @@
/*#############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2025 Shaun Reed, all rights reserved ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com ##
##############################################################################
*/
#include "display.h"
#include "ssd1306.h"
@@ -8,7 +15,7 @@
I2C i2c(PIN_SDA, PIN_SCL, PIN_RST);
void setup()
extern "C" void app_main(void)
{
SSD1306 ssd1306(i2c);
Display d(ssd1306);
@@ -30,5 +37,3 @@ void setup()
LV_LABEL_LONG_CLIP,
LV_ALIGN_BOTTOM_MID);
}
void loop() { }

View File

@@ -1,3 +1,10 @@
/*#############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2025 Shaun Reed, all rights reserved ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com ##
##############################################################################
*/
#ifndef PANEL_H
#define PANEL_H
@@ -42,6 +49,9 @@ struct Panel {
~Panel() = default;
//
// PUBLIC MEMBERS
/// Pointer to object using known interface for IPanelDevice.
IPanelDevice *device_;
@@ -54,9 +64,24 @@ struct Panel {
/// ESP LCD panel configuration structure.
esp_lcd_panel_dev_config_t esp_panel_config_;
/**
* Registers LVGL draw buffers and callbacks for rendering the display.
*
* @param display_handle Pointer to the LVGL display to use for rendering.
*/
inline void register_display_callbacks(lv_display_t *display_handle) const
{
device_->register_rendering_data(display_handle, esp_io_);
device_->register_lvgl_tick_timer();
}
private:
//
// PRIVATE MEMBERS
/// Tag used for ESP logging.
const char * TAG = "Panel";
constexpr static const char *TAG = "Panel";
};
#endif //PANEL_H

View File

@@ -0,0 +1,145 @@
/*#############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2025 Shaun Reed, all rights reserved ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com ##
##############################################################################
*/
#include "panel_device.h"
#include "display.h"
bool IPanelDevice::lvgl_flush_ready_cb(esp_lcd_panel_io_handle_t,
esp_lcd_panel_io_event_data_t *,
void *user_ctx)
{
auto *disp = (lv_display_t *) user_ctx;
lv_display_flush_ready(disp);
return false;
}
void IPanelDevice::lvgl_flush_cb(lv_display_t *display, const lv_area_t *area,
uint8_t *px_map) // NOLINT(*-non-const-parameter)
{
auto panel_handle =
(esp_lcd_panel_handle_t) lv_display_get_user_data(display);
// Necessary because LVGL reserves 2x4 bytes in the buffer for a palette.
// Since we are monochrome, we don't need these additional bytes.
// For more information about the monochrome, please refer to:
// https://docs.lvgl.io/9.2/porting/display.html#monochrome-displays
// Skip the palette here.
px_map += LVGL_PALETTE_SIZE;
uint16_t hor_res = lv_display_get_physical_horizontal_resolution(display);
int32_t x1 = area->x1;
int32_t x2 = area->x2;
int32_t y1 = area->y1;
int32_t y2 = area->y2;
// As of 03/01/2025 master branch of LVGL contains this helper for the same.
// https://docs.lvgl.io/master/API/draw/sw/lv_draw_sw_utils.html
// lv_draw_sw_i1_convert_to_vtiled()
for (int32_t y = y1; y <= y2; y++) {
for (int32_t x = x1; x <= x2; x++) {
// Get the byte offset of the pixel coordinates using horizontal-mapping.
int h_offset = Pixel::horizontal_byte_offset(x, y, hor_res);
// True if the pixel is lit, else false.
bool chroma_color = px_map[h_offset] & Pixel::msb_mask(x);
// We need an additional buffer for transposing the pixel data to the
// vertical format required by the display driver.
uint8_t *buf = IPanelDevice::get_additional_draw_buffer();
// Move to the position in the auxillary buffer where the pixel is stored.
buf += Pixel::vertical_byte_offset(x, y, hor_res);
// Write the single bit to the monochrome display mapped vertically.
// Take the Least Significant Bit mask of the Y coordinate to select the
// bit representing a pixel at position y in a vertically-mapped display.
if (chroma_color) {
// Set the vertically-mapped pixel to on.
*buf &= ~Pixel::lsb_mask(y);
} else {
// Set the vertically-mapped pixel to off.
*buf |= Pixel::lsb_mask(y);
}
}
}
// Pass the draw buffer to the driver.
ESP_ERROR_CHECK(
esp_lcd_panel_draw_bitmap(panel_handle, x1, y1, x2 + 1, y2 + 1,
IPanelDevice::get_additional_draw_buffer()));
}
void IPanelDevice::lvgl_increase_tick_cb(void *)
{
// Tell LVGL how many milliseconds has elapsed
lv_tick_inc(LVGL_TICK_PERIOD_MS);
}
[[noreturn]] void IPanelDevice::lvgl_port_task(void *)
{
// Optionally initialize some LVGL objects here before entering loop below.
ESP_LOGI(TAG, "Starting LVGL task");
for (uint32_t time_to_next_ms = 0; true; usleep(1000 * time_to_next_ms)) {
// Obtain LVGL API lock before calling any LVGL methods.
ScopedLock lock;
// Optionally handle LVGL input or event logic here.
// Update LVGL periodic timers.
time_to_next_ms = lv_timer_handler();
}
}
void IPanelDevice::register_rendering_data(lv_display_t *display_handle,
esp_lcd_panel_io_handle_t io_handle)
{
// Create draw buffer.
ESP_LOGI(TAG, "Allocate separate LVGL draw buffers");
lv_buf_ = heap_caps_calloc(1, lv_buf_size_,
MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
assert(lv_buf_);
ESP_LOGI(TAG, "Set LVGL draw buffers");
// Color format must be set first, LVGL9 suooprt new monochromatic format.
lv_display_set_color_format(display_handle, LV_COLOR_FORMAT_I1);
lv_display_set_buffers(display_handle, lv_buf_, nullptr,
lv_buf_size_,
LV_DISPLAY_RENDER_MODE_FULL);
lv_display_set_rotation(display_handle, LV_DISPLAY_ROTATION_0);
ESP_LOGI(TAG, "Set LVGL callback for flushing to the display");
lv_display_set_flush_cb(display_handle, IPanelDevice::lvgl_flush_cb);
ESP_LOGI(TAG, "Register io panel callback for LVGL flush ready notification");
const esp_lcd_panel_io_callbacks_t cbs = {
.on_color_trans_done = IPanelDevice::lvgl_flush_ready_cb,
};
ESP_ERROR_CHECK(
esp_lcd_panel_io_register_event_callbacks(io_handle, &cbs,
display_handle));
}
void IPanelDevice::register_lvgl_tick_timer()
{
ESP_LOGI(TAG, "Use esp_timer to increase LVGL tick");
const esp_timer_create_args_t esp_timer_args = {
.callback = &IPanelDevice::lvgl_increase_tick_cb,
// Data to pass to the IPanelDevice::lvgl_port_task callback.
.arg = nullptr,
.name = "lvgl_tick",
};
Display::timers_.start_new_timer_periodic(esp_timer_args,
LVGL_TICK_PERIOD_MS * 1000);
// LVGL requires a FreeRTOS task for running it's event loop.
// The lvgl_port_task callback can update the UI or handle input logic.
// For this basic example we don't do either of these things.
ESP_LOGI(TAG, "Create LVGL FreeRTOS task");
// Optionally set user data to pass to LVGL's FreeRTOS task callback here.
void *user_data = nullptr;
xTaskCreate(lvgl_port_task, "LVGL", LVGL_TASK_STACK_SIZE,
user_data, LVGL_TASK_PRIORITY, nullptr);
}

View File

@@ -1,3 +1,10 @@
/*#############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2025 Shaun Reed, all rights reserved ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com ##
##############################################################################
*/
#ifndef PANEL_DEVICE_H
#define PANEL_DEVICE_H
@@ -13,11 +20,120 @@
// LVGL reserves 2x4 bytes in the buffer to be used as a palette.
// This additional space must be added to the IPanelDevice::buf_size_.
#define LVGL_PALETTE_SIZE 8
#define LVGL_TICK_PERIOD_MS 5
#define LVGL_TASK_STACK_SIZE (4 * 1024)
#define LVGL_TASK_PRIORITY 2
#define LCD_H_RES 128
#define LCD_V_RES 64
/**
* Encapsulates vendor specific ESP LCD pabel initialization logic.
* Wraps some foundational operations performed on pixel coordinates when
* dealing with pointer arithmetic. Most of these could be done ad-hoc as needed
* but using this helper reduces the risk of errors.
*/
struct Pixel {
/**
* Calculate byte offset for the pixel at [x,y] within a horizontally-mapped
* monochrome uint8 draw buffer, using the initialized horizontal resolution.
*
* We use `>> 3` because each pixel requires 1 bit, but each uint8 in the draw
* buffer can hold 8 bits. To find the uint8 value in our draw buffer that
* stores this pixel's value we must compensate for this when using pixel
* coordinates in byte math.
*
* Therefore, each uint8 in the draw buffer stores the state of 8 pixels.
* Below is an example of calculating for [x, y] pixel coordinates [20, 10].
* The example uses a horizontal resolution of 128.
*
* For the horizontal case, each row (y) of the image is represented by
* `hor_res >> 3` bytes (16). The byte-offset of the first pixel in the 10th
* row for example is `16 * 10` = 160.
*
* Since the pixels are stored horizontally we must calculate the 20th pixel
* column (x) as `160 + (20 >> 3)`, or `160 + (20 / 8)` to get a final offset
* of 162.
*
* @param x X pixel coordinate to find byte offset.
* @param y Y pixel coordinate to find byte offset.
* @param hor_res horizontal resolution of the display.
* @return byte offset for a single-byte monochrome pixel at [x,y].
*/
[[maybe_unused]] [[nodiscard]] static ptrdiff_t
horizontal_byte_offset(const int32_t &x, const int32_t &y,
const int32_t &hor_res = LCD_V_RES)
{
// Convert pixel (bit) coordinates to byte coordinates in the draw buffer.
return (hor_res >> 3) * y + (x >> 3);
}
/**
* Calculate byte offset for the pixel at [x,y] within a vertically-mapped
* monochrome uint8 draw buffer, using the initialized horizontal resolution.
*
* We use `>> 3` because each pixel requires 1 bit, but each uint8 in the draw
* buffer can hold 8 bits. To find the uint8 value in our draw buffer that
* stores this pixel's value we must compensate for this when using pixel
* coordinates in byte math.
*
* Therefore, each uint8 in the draw buffer stores the state of 8 pixels.
* Below is an example of calculating for [x, y] pixel coordinates [20, 10].
* The example uses a horizontal resolution of 128.
*
* For the vertical case, each row (y) of the image is represented by
* `hor_res` bytes (128) - one for each column (x). Because the pixels are
* stored vertically, the byte-offset of the first pixel in the 10th row is
* `128 * (10 >> 3)` or * `128 * (10 / 8)` = 128.
*
* From this location we can simply calculate the 20th pixel column (x) as
* `128 + 20` to get a final offset of 148, because the pixels are stored in a
* columnar format.
*
* @param x X pixel coordinate to find byte offset.
* @param y Y pixel coordinate to find byte offset.
* @param hor_res horizontal resolution of the display.
* @return byte offset for a single-byte monochrome pixel at [x,y].
*/
[[maybe_unused]] [[nodiscard]] static ptrdiff_t
vertical_byte_offset(const int32_t &x, const int32_t &y,
const int32_t &hor_res = LCD_V_RES)
{
// Convert pixel (bit) coordinates to byte coordinates in the draw buffer.
return hor_res * (y >> 3) + x;
}
/**
* Finds the Most Significant Bit location of bit `i` in a byte.
*
* MSB LSB
* bits 7 6 5 4 3 2 1 0
* data 8 7 6 5 4 3 2 1
* Left Right
*
* @return bitmask for MSB location of `i`.
*/
[[maybe_unused]] [[nodiscard]] static uint8_t
msb_mask(const int32_t &i) { return 1 << (7 - i % 8); }
/**
* Finds the Least Significant Bit location of bit `i` in a byte.
*
* LSB MSB
* bits 0 1 2 3 4 5 6 7
* data 1 2 3 4 5 6 7 8
* Left Right
*
* @return bitmask for LSB location of `i`.
*/
[[maybe_unused]] [[nodiscard]] static uint8_t
lsb_mask(const int32_t &i) { return 1 << (i % 8); }
};
/**
* Encapsulates vendor specific ESP LCD panel initialization logic.
* This pure virtual interface can be inherited from for using new LCD devices.
* See the SSD1306 example for implementing a PanelDevice for NT35510 or ST7789.
* See SSD1306 as an example to implement IPanelDevice for NT35510 or ST7789.
*
* At this time only I2C is supported.
* Classes that inherit from this interface should likely be marked final.
@@ -57,10 +173,14 @@ public:
height_(height),
rst_num_(i2c.rst_num_),
lv_buf_size_(draw_buf_size),
esp_io_config_(io_config) { }
esp_io_config_(io_config),
lv_buf_(nullptr) { }
virtual ~IPanelDevice() = default;
//
// PUBLIC METHODS
/**
* Create an LVGL display using the width and height of this device.
*
@@ -105,10 +225,9 @@ public:
esp_lcd_panel_del(panel);
}
ESP_LOGI(TAG, "Install SSD1306 panel driver");
ESP_LOGI(TAG, "Installing vendor panel driver");
// Call pure virtual method responsible for initializing the panel handle.
init_panel(config, io, panel);
}
/**
@@ -119,6 +238,29 @@ public:
*/
virtual void *vendor_config() = 0;
/**
* Registers LVGL draw buffers and callbacks for this display.
*
* An implementation of the interface can optionally override this method to
* provide custom LVGL callbacks and display configurations.
*
* @param display_handle LVGL display handle to use for rendering.
* @param io_handle IO handle for the ESP LCD panel.
*/
virtual void register_rendering_data(lv_display_t *display_handle,
esp_lcd_panel_io_handle_t io_handle);
/**
* Registers LVGL ticker timer callback for rendering this display.
*
* An implementation of the interface can optionally override this method to
* provide custom LVGL callbacks and tick configurations.
*/
virtual void register_lvgl_tick_timer();
//
// PUBLIC MEMBERS
/// Width of the device screen in pixels.
int32_t width_;
@@ -134,21 +276,160 @@ public:
/// ESP LCD panel IO configuration.
esp_lcd_panel_io_i2c_config_t esp_io_config_;
private:
protected:
/**
* Initializes the ESP panel using vendor specific APIs and configurations.
* This method should implement any setup logic specific to the device.
* Static accessor to a static buffer to store draw buffer data for the panel.
*
* @param config ESP LCD panel configuration.
* @param io ESP LCD panel IO handle.
* @param [out] panel ESP LCD panel handle output pointer location.
* This method is protected to allow an implementation to provide a custom
* callback method similar to IPanelDevice::lvgl_flush_cb.
*
* The buffer is allocated statically within the scope of this function to
* allow creating multiple panels that _each_ manage their own statically
* allocated draw buffer data. This simplifies implementing the interface by
* taking this responsibility off of the implementor. The buffer will only be
* allocated if this method is called, so the memory is only used if required.
*
* @return Pointer to uint8 draw buffer data.
* @sa register_rendering_data for overriding LVGL rendering callbacks.
*/
static uint8_t *get_additional_draw_buffer()
{
// Static to the scope of this function, not the compilation unit.
// For LV_COLOR_FORMAT_I1 we need an extra buffer to hold converted data.
static uint8_t oled_buffer[LCD_H_RES * LCD_V_RES / 8];
return oled_buffer;
}
private:
//
// PRIVATE METHODS
/**
* Initializes the ESP panel using vendor specific APIs and configurations.
* This method should implement any setup logic specific to the device.
*
* @param config ESP LCD panel configuration.
* @param io ESP LCD panel IO handle.
* @param [out] panel ESP LCD panel handle output pointer location.
*/
virtual void init_panel(esp_lcd_panel_dev_config_t &config,
esp_lcd_panel_io_handle_t io,
esp_lcd_panel_handle_t &panel) = 0;
//
// PRIVATE STATIC METHODS
/**
* The callback invoked when panel IO finishes transferring color data.
* This signals that the panel is ready to flush image data to the display.
*
* @param panel LCD panel IO handles.
* @param data Panel IO event data, fed by driver.
* @param user_ctx User data, passed from `esp_lcd_panel_io_xxx_config_t`.
* @return Whether a high priority task has been waken up by this function.
* @sa SSD1306::SSD1306 for setting user_ctx data passed to the callback.
* @sa register_rendering_data for overriding this callback.
*/
static bool lvgl_flush_ready_cb(esp_lcd_panel_io_handle_t panel,
esp_lcd_panel_io_event_data_t *data,
void *user_ctx);
/**
* The callback invoked for flushing the rendered image to the display.
*
* `px_map` contains the rendered image as raw pixel map and it should be
* copied to `area` on the display.
*
* The following details are crucial for understanding the logic surrounding
* flushing to the display in this example.
*
* The order of bits within the px_map from _LVGL_ is MSB first.
* MSB LSB
* bits 7 6 5 4 3 2 1 0
* pixels 0 1 2 3 4 5 6 7
* Left Right
*
* The bytes from _LVGL_ are mapped to pixel rows of the display
* 8 bits (pixels) per byte -
* [0, 0, 0, 0, 0, 0, 0, 0]
* [0, 0, 0, 0, 0, 0, 0, 0]
* [0, 0, 0, 0, 0, 0, 0, 0]
*
* The order of bits expected by the _display driver_ is LSB first.
* We must preserve pairing of each bit and pixel when writing to the display.
* LSB MSB
* bits 0 1 2 3 4 5 6 7
* pixels 7 6 5 4 3 2 1 0
* Left Right
*
* Bytes expected by the _display driver_ map to pixel columns of the display.
* 8 bits (pixels) per byte -
* [0, [0, [0, [0,
* 0, 0, 0, 0,
* 0, 0, 0, 0,
* 0, 0, 0, 0,
* 0, 0, 0, 0,
* 0, 0, 0, 0,
* 0, 0, 0, 0,
* 0] 0] 0] 0]
*
* These layouts in memory have no opinion on the shape of the image. The
* beginning and end of a row or a column for example is entirely dependent
* on how the data is accessed. The vertical and horitzontal resolution may
* vary between displays.
*
* For the LV_COLOR_FORMAT_I1 color format we are using, an additional buffer
* is needed for transposing the bits to the vertical arrangement required by
* the display driver that is outlined above.
*
* This callback implementation is an example of handling this transposition
* and flushing the data to the display in the expected format.
*
* @param display LVGL display handle to use for rendering.
* @param area Area of the display being flushed.
* @param px_map Rendered image data for writing to the display area.
* @sa register_rendering_data for overriding this callback.
* @sa get_additional_draw_buffer
*/
static void lvgl_flush_cb(lv_display_t *display,
const lv_area_t *area,
uint8_t *px_map);
/**
* Callback invoked for every period of the timer.
*
* This callback _must_ call lv_tick_inc to inform LVGL how much time has
* elapsed since the last period of the timer.
*
* @param data User data passed to the callback.
* @sa register_lvgl_tick_timer for setting user data and the tick period of
* the timer, or overriding this callback entirely.
*/
static void lvgl_increase_tick_cb(void *data);
/**
* FreeRTOS task callback invoked for handling LVGL events or updating the UI.
*
* This function is intentionally an endless loop and should never return.
* LVGL initialization logic can optionally be added before entering the loop.
* Input logic can optionally be handled within the loop.
*
* This callback _must_ call lv_timer_handler to handle LVGL periodic timers.
*
* @param data User data passed to the callback.
* @sa register_lvgl_tick_timer for overriding this callback.
*/
[[noreturn]] static void lvgl_port_task(void *data);
//
// PRIVATE MEMBERS
/// LVGL draw buffer associated with this Display's lv_display_t.
void *lv_buf_;
/// Tag used for ESP logging.
const char * TAG = "IPanelDevice";
constexpr static const char *TAG = "IPanelDevice";
};
#endif // PANEL_DEVICE_H

View File

@@ -0,0 +1,12 @@
/*#############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2025 Shaun Reed, all rights reserved ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com ##
##############################################################################
*/
#include "scoped_lock.h"
// LVGL library is not thread-safe, this example calls LVGL APIs from tasks.
// We must use a mutex to protect it.
_lock_t ScopedLock::lv_lock_;

View File

@@ -0,0 +1,28 @@
/*#############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2025 Shaun Reed, all rights reserved ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com ##
##############################################################################
*/
#ifndef SCOPED_LOCK_H
#define SCOPED_LOCK_H
#include <mutex>
/**
* Obtains LVGL API mutex lock for the duration of local scope.
*
* LVGL library is not thread-safe, this lock should be held when making calls
* to the LVGL API, and released as soon as possible when finished.
*/
struct ScopedLock {
explicit ScopedLock() { _lock_acquire(&lv_lock_); }
~ScopedLock() { _lock_release(&lv_lock_); }
/// Mutex used to protect LVGL API calls.
static _lock_t lv_lock_;
};
#endif // SCOPED_LOCK_H

View File

@@ -1,6 +0,0 @@
#include "ssd1306.h"
// To use LV_COLOR_FORMAT_I1 we need an extra buffer to hold the converted data.
// TODO: Remove this and SSD1306 can be header only.
uint8_t SSD1306::oled_buffer_[LCD_H_RES * LCD_V_RES / 8];

View File

@@ -1,16 +1,10 @@
/*
* https://docs.espressif.com/projects/esp-idf/en/v5.3.2/esp32/api-reference/peripherals/lcd/index.html#functional-overview
*
* Implementing the interface draw to an LCD using various interface modes.
* I2C interface mode is SSD1306
* SPI interface mode is ST7789
* I80 interface mode is NT35510 or ST7789
*
* Actually, I think any driver can be used with any interface mode
* Along with additional third party drivers via the component manager
* https://github.com/espressif/esp-idf/tree/0d6099ec533c4b647fb7a7b0b8942bc7aeb82f90/examples/peripherals/lcd/spi_lcd_touch#spi-lcd-and-touch-panel-example
*/
/*#############################################################################
## Author: Shaun Reed ##
## Legal: All Content (c) 2025 Shaun Reed, all rights reserved ##
## ##
## Contact: shaunrd0@gmail.com | URL: www.shaunreed.com ##
##############################################################################
*/
#ifndef SSD1306_H
#define SSD1306_H
@@ -25,8 +19,6 @@
// According to SSD1306 datasheet.
// https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf
#define LCD_H_RES 128
#define LCD_V_RES 64
#define I2C_HW_ADDR 0x3C
#define LCD_PIXEL_CLOCK_HZ (400 * 1000)
// Bit number used to represent command and parameter
@@ -62,6 +54,9 @@ public:
IPanelDevice(i2c,
(esp_lcd_panel_io_i2c_config_t) {
.dev_addr = I2C_HW_ADDR,
// User data to pass to the LVGL flush_ready callback.
// See IPanelDevice::lvgl_flush_ready_cb
.user_ctx = nullptr,
.control_phase_bytes = 1,
.dc_bit_offset = 6,
.lcd_cmd_bits = LCD_CMD_BITS,
@@ -75,6 +70,9 @@ public:
~SSD1306() final = default;
//
// PUBLIC METHODS
/**
* Provides the SSD1306 vendor configuration to IPanelDevice consumers.
*
@@ -85,16 +83,17 @@ public:
return &ssd1306_config_;
}
//
// PUBLIC MEMBERS
/// SSD1306 configuration structure.
esp_lcd_panel_ssd1306_config_t ssd1306_config_;
/**
* Draw buffer for this panel device.
* For LV_COLOR_FORMAT_I1 we need an extra buffer to hold the converted data.
*/
static uint8_t oled_buffer_[LCD_H_RES * LCD_V_RES / 8];
private:
//
// PRIVATE METHODS
/// Initializes the ESP LCD panel handle for the SSD1306 device.
void init_panel(esp_lcd_panel_dev_config_t &config,
esp_lcd_panel_io_handle_t io,
@@ -102,9 +101,6 @@ private:
{
ESP_ERROR_CHECK(esp_lcd_new_panel_ssd1306(io, &config, &panel));
}
/// Tag used for ESP logging.
const char * TAG = "SSD1306";
};
#endif // SSD1306_H

Some files were not shown because too many files have changed in this diff Show More