basic application support

This commit is contained in:
Ken Van Hoeylandt 2023-12-25 17:53:58 +01:00
parent 7886d5c2f9
commit e6525364c6
21 changed files with 341 additions and 122 deletions

View File

@ -2,8 +2,8 @@
NanoBake is a front-end application platform.
The main goals are to deliver multiple front-end applications on a single ESP32 devices,
and to simplify hardware support.
The main goals are to install and run multiple front-end applications on a single ESP32 device,
and to simplify hardware support for such apps.
**Status: pre-alpha**
@ -23,7 +23,7 @@ In practice, there are pre-configured drivers available for these boards:
# Guide
Until there is proper documentation, here are some pointers:
- [Sample application](./main/main.c)
- [Sample application](main/src/main.c)
- [NanoBake](./components/nanobake/inc)
## License

View File

@ -10,7 +10,6 @@
const char* TAG = "cst816";
static esp_err_t prv_init_io(esp_lcd_panel_io_handle_t* io_handle) {
// Init I2C
const i2c_config_t i2c_conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = GPIO_NUM_33,
@ -19,18 +18,31 @@ static esp_err_t prv_init_io(esp_lcd_panel_io_handle_t* io_handle) {
.scl_pullup_en = GPIO_PULLUP_DISABLE,
.master.clk_speed = 400000
};
ESP_RETURN_ON_ERROR(i2c_param_config(CST816_I2C_PORT, &i2c_conf), TAG, "i2c config failed");
ESP_RETURN_ON_ERROR(i2c_driver_install(CST816_I2C_PORT, i2c_conf.mode, 0, 0, 0), TAG, "i2c driver install failed");
// Configure I2C
ESP_RETURN_ON_ERROR(
i2c_param_config(CST816_I2C_PORT, &i2c_conf),
TAG,
"i2c config failed"
);
ESP_RETURN_ON_ERROR(
i2c_driver_install(CST816_I2C_PORT, i2c_conf.mode, 0, 0, 0),
TAG,
"i2c driver install failed"
);
const esp_lcd_panel_io_i2c_config_t touch_io_config = ESP_LCD_TOUCH_IO_I2C_CST816S_CONFIG();
ESP_RETURN_ON_ERROR(esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)CST816_I2C_PORT, &touch_io_config, io_handle), TAG, "esp_lcd_panel creation failed");
ESP_RETURN_ON_ERROR(
esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)CST816_I2C_PORT, &touch_io_config, io_handle),
TAG,
"esp_lcd_panel creation failed"
);
return ESP_OK;
}
static esp_err_t prv_create_touch(esp_lcd_panel_io_handle_t io_handle, esp_lcd_touch_handle_t* touch_handle) {
// Configure touch
esp_lcd_touch_config_t config = {
.x_max = 240,
.y_max = 320,
@ -48,7 +60,6 @@ static esp_err_t prv_create_touch(esp_lcd_panel_io_handle_t io_handle, esp_lcd_t
.interrupt_callback = NULL,
};
// Init touch
return esp_lcd_touch_new_i2c_cst816s(io_handle, &config, touch_handle);
}

View File

@ -1,5 +1,6 @@
idf_component_register(
SRC_DIRS "src"
"src/applications/main/system_info"
INCLUDE_DIRS "inc"
PRIV_INCLUDE_DIRS "src"
REQUIRES esp_lvgl_port esp_lcd esp_lcd_touch driver

View File

@ -2,5 +2,10 @@
#define NANOBAKE_H
#include "nb_platform.h"
#include "nb_app.h"
extern void nanobake_run(nb_platform_config_t _Nonnull * config);
//extern nb_app_config_t[32] nanobake_apps(nb_app_config_t app_configs, ...);
#endif //NANOBAKE_H

View File

@ -0,0 +1,49 @@
#ifndef NANOBAKE_NB_APP_H
#define NANOBAKE_NB_APP_H
#define NB_APP_ID_LENGTH 32
#define NB_APP_NAME_LENGTH 32
#include <stdio.h>
#include <esp_err.h>
#include <lvgl.h>
// region Forward declarations
struct nb_platform;
typedef struct nb_platform nb_platform_t;
//endregion
typedef enum nb_app_type nb_app_type_t;
enum nb_app_type {
SERVICE,
SYSTEM,
USER
};
typedef struct nb_app_config nb_app_config_t;
typedef void (*nb_app_callback_on_create) (nb_platform_t* platform, lv_obj_t* lv_parent);
typedef void (*nb_app_callback_update) (nb_platform_t* platform, lv_obj_t* lv_parent);
typedef void (*nb_app_callback_on_destroy) (nb_platform_t* platform);
struct nb_app_config {
char id[NB_APP_ID_LENGTH];
char name[NB_APP_NAME_LENGTH];
nb_app_type_t type;
nb_app_callback_on_create _Nullable on_create;
nb_app_callback_on_destroy _Nullable on_destroy;
nb_app_callback_update _Nullable on_update;
size_t update_task_stack_size;
uint32_t update_task_priority;
};
typedef struct nb_app nb_app_t;
struct nb_app {
nb_app_config_t config;
};
esp_err_t nb_app_config_validate(nb_app_config_t* _Nonnull app);
#endif //NANOBAKE_NB_APP_H

View File

@ -6,22 +6,25 @@
typedef struct nb_display nb_display_t;
struct nb_display {
bool io_initialized;
uint16_t horizontal_resolution;
uint16_t vertical_resolution;
uint16_t draw_buffer_height;
uint16_t bits_per_pixel;
esp_lcd_panel_io_handle_t io_handle;
esp_lcd_panel_handle_t display_handle;
esp_lcd_panel_io_handle_t _Nonnull io_handle;
esp_lcd_panel_handle_t _Nonnull display_handle;
};
typedef struct nb_display_driver nb_display_driver_t;
struct nb_display_driver {
const char name[32];
char name[32];
esp_err_t (*create_display)(nb_display_t* display);
};
extern esp_err_t nb_display_create(nb_display_driver_t driver, nb_display_t* display);
/**
* @param[in] driver
* @return allocated display object
*/
nb_display_t _Nonnull* nb_display_create(nb_display_driver_t _Nonnull* driver);
#endif // NANOBAKE_NB_DISPLAY_H

View File

@ -3,28 +3,42 @@
#include "nb_display.h"
#include "nb_touch.h"
#include "nb_app.h"
#include <esp_err.h>
#include <lvgl.h>
typedef nb_touch_driver_t (*create_touch_driver)();
typedef nb_display_driver_t (*create_display_driver)();
typedef nb_app_config_t (*create_app)();
typedef struct nb_platform_config nb_platform_config_t;
struct nb_platform_config {
nb_display_driver_t display_driver;
nb_touch_driver_t touch_driver;
// Required driver for display
create_display_driver _Nonnull display_driver;
// Optional driver for touch input
create_touch_driver _Nullable touch_driver;
// List of user applications
create_app apps[];
};
typedef struct nb_lvgl nb_lvgl_t;
struct nb_lvgl {
lv_disp_t* disp;
lv_indev_t* touch_indev;
lv_disp_t* _Nonnull disp;
lv_indev_t* _Nullable touch_indev;
};
typedef struct nb_platform nb_platform_t;
struct nb_platform {
nb_display_t display;
nb_touch_t touch;
nb_lvgl_t lvgl;
nb_display_t* _Nonnull display;
nb_touch_t* _Nullable touch;
nb_lvgl_t* _Nonnull lvgl;
};
esp_err_t nb_platform_create(nb_platform_config_t config, nb_platform_t* platform);
/**
* @param[in] config
* @return a newly allocated platform instance (caller takes ownership)
*/
nb_platform_t _Nonnull* nb_platform_create(nb_platform_config_t _Nonnull* config);
#endif // NANOBAKE_NB_PLATFORM_H

View File

@ -7,7 +7,7 @@
typedef struct nb_touch_driver nb_touch_driver_t;
struct nb_touch_driver {
const char name[32];
char name[32];
esp_err_t (*init_io)(esp_lcd_panel_io_handle_t* io_handle);
esp_err_t (*create_touch)(esp_lcd_panel_io_handle_t io_handle, esp_lcd_touch_handle_t* touch_handle);
};
@ -15,10 +15,14 @@ struct nb_touch_driver {
typedef struct nb_touch nb_touch_t;
struct nb_touch {
esp_lcd_panel_io_handle_t io_handle;
esp_lcd_touch_handle_t touch_handle;
esp_lcd_panel_io_handle_t _Nonnull io_handle;
esp_lcd_touch_handle_t _Nonnull touch_handle;
};
esp_err_t nb_touch_create(nb_touch_driver_t driver, nb_touch_t* touch);
/**
* @param[in] driver
* @return a newly allocated instance
*/
nb_touch_t _Nonnull* nb_touch_create(nb_touch_driver_t _Nonnull* driver);
#endif // NANOBAKE_NB_TOUCH_H

View File

@ -0,0 +1,36 @@
#include "system_info.h"
#include <esp_lvgl_port.h>
#include <nb_platform.h>
static void prv_on_create(nb_platform_t _Nonnull* platform, lv_obj_t _Nonnull* lv_parent) {
lvgl_port_lock(0);
lv_obj_t* cpu_label = lv_label_create(lv_parent);
lv_label_set_recolor(cpu_label, true);
lv_obj_set_width(cpu_label, (lv_coord_t)platform->display->horizontal_resolution);
lv_obj_set_style_text_align(cpu_label, LV_TEXT_ALIGN_LEFT, 0);
lv_label_set_text(cpu_label, "CPU usage: ?");
lv_obj_align(cpu_label, LV_ALIGN_TOP_LEFT, 0, 0);
lv_obj_t* mem_free_label = lv_label_create(lv_parent);
lv_label_set_recolor(mem_free_label, true);
lv_obj_set_width(mem_free_label, (lv_coord_t)platform->display->horizontal_resolution);
lv_obj_set_style_text_align(mem_free_label, LV_TEXT_ALIGN_LEFT, 0);
lv_label_set_text(mem_free_label, "Memory: ?");
lv_obj_align(mem_free_label, LV_ALIGN_TOP_LEFT, 0, 15);
lvgl_port_unlock();
}
nb_app_config_t system_info_app_config() {
nb_app_config_t config = {
.id = "systeminfo",
.name = "System Info",
.type = SYSTEM,
.on_create = &prv_on_create,
.on_update = NULL,
.on_destroy = NULL
};
return config;
}

View File

@ -0,0 +1,8 @@
#ifndef NANOBAKE_SYSTEM_INFO_H
#define NANOBAKE_SYSTEM_INFO_H
#include "nb_app.h"
nb_app_config_t system_info_app_config();
#endif // NANOBAKE_SYSTEM_INFO_H

View File

@ -0,0 +1,14 @@
#include "nanobake.h"
#include "applications/main/system_info/system_info.h"
void nb_app_start(nb_platform_t _Nonnull* platform, nb_app_config_t _Nonnull* config) {
lv_obj_t* scr = lv_scr_act();
ESP_ERROR_CHECK(nb_app_config_validate(config));
config->on_create(platform, scr);
}
extern void nanobake_run(nb_platform_config_t _Nonnull* config) {
nb_platform_t _Nonnull* platform = nb_platform_create(config);
nb_app_config_t app_config = system_info_app_config();
nb_app_start(platform, &app_config);
}

View File

@ -0,0 +1,32 @@
#include "nb_app.h"
#include <esp_check.h>
#include <string.h>
static const char* TAG = "nb_app";
esp_err_t nb_app_config_validate(nb_app_config_t* _Nonnull app) {
ESP_RETURN_ON_FALSE(
strlen(app->id) < NB_APP_ID_LENGTH,
ESP_FAIL,
TAG,
"app id cannot be larger than %d characters",
NB_APP_ID_LENGTH - 1
);
ESP_RETURN_ON_FALSE(
strlen(app->name) < NB_APP_NAME_LENGTH,
ESP_FAIL,
TAG,
"app name cannot be larger than %d characters",
NB_APP_NAME_LENGTH - 1
);
ESP_RETURN_ON_FALSE(
(app->on_update == NULL) == (app->update_task_priority == 0 && app->update_task_priority == 0),
ESP_FAIL,
TAG,
"app update is inconsistently configured"
);
return ESP_OK;
}

View File

@ -0,0 +1,20 @@
#ifndef NANOBAKE_NB_ASSERT_H
#define NANOBAKE_NB_ASSERT_H
#include <assert.h>
#include <esp_log.h>
#define NB_ASSERT(x, message) do { \
if (!(x)) { \
ESP_LOGE("assert", message); \
_esp_error_check_failed( \
x, \
__FILE__, \
__LINE__, \
__ASSERT_FUNC, \
#x \
); \
} \
} while(0)
#endif //NANOBAKE_NB_ASSERT_H

View File

@ -1,9 +1,8 @@
#include "nb_display.h"
#include "nb_internal.h"
#include <esp_check.h>
#include <esp_log.h>
#include "nb_assert.h"
esp_err_t nb_display_create(nb_display_driver_t driver, nb_display_t* display) {
ESP_RETURN_ON_ERROR(driver.create_display(display), nbi_tag, "failed to create driver");
return ESP_OK;
nb_display_t _Nonnull* nb_display_create(nb_display_driver_t _Nonnull* driver) {
nb_display_t _Nonnull* display = malloc(sizeof(nb_display_t));
NB_ASSERT(driver->create_display(display) == ESP_OK, "failed to create display");
return display;
}

View File

@ -1,11 +1,13 @@
#include "nb_platform.h"
#include <esp_check.h>
#include "nb_display.h"
#include "nb_touch.h"
#include "nb_internal.h"
#include <esp_check.h>
#include <esp_err.h>
#include <esp_lvgl_port.h>
#include <nb_assert.h>
static const char* TAG = "nb_platform";
static esp_err_t prv_lvgl_init(
@ -19,18 +21,19 @@ static esp_err_t prv_lvgl_init(
.timer_period_ms = 5
};
ESP_RETURN_ON_ERROR(lvgl_port_init(&lvgl_cfg), TAG, "lvgl port init failed");
nb_display_t _Nonnull* display = platform->display;
// Add display
ESP_LOGD(TAG, "lvgl add display");
const lvgl_port_display_cfg_t disp_cfg = {
.io_handle = platform->display.io_handle,
.panel_handle = platform->display.display_handle,
.buffer_size = platform->display.horizontal_resolution * platform->display.draw_buffer_height * (platform->display.bits_per_pixel / 8),
.io_handle = display->io_handle,
.panel_handle = display->display_handle,
.buffer_size = display->horizontal_resolution * display->draw_buffer_height * (display->bits_per_pixel / 8),
.double_buffer = 1,
.hres = platform->display.horizontal_resolution,
.vres = platform->display.vertical_resolution,
.hres = display->horizontal_resolution,
.vres = display->vertical_resolution,
.monochrome = false,
/* Rotation values must be same as used in esp_lcd for initial settings of the screen */
/* Rotation values must be same as defined in driver */
// TODO: expose data from driver
.rotation = {
.swap_xy = false,
.mirror_x = true,
@ -40,41 +43,39 @@ static esp_err_t prv_lvgl_init(
.buff_dma = true,
}
};
platform->lvgl.disp = lvgl_port_add_disp(&disp_cfg);
platform->lvgl->disp = lvgl_port_add_disp(&disp_cfg);
// Add touch
if (platform->touch.io_handle != NULL && platform->touch.touch_handle != NULL) {
if (platform->touch != NULL) {
const lvgl_port_touch_cfg_t touch_cfg = {
.disp = platform->lvgl.disp,
.handle = platform->touch.touch_handle,
.disp = platform->lvgl->disp,
.handle = platform->touch->touch_handle,
};
platform->lvgl.touch_indev = lvgl_port_add_touch(&touch_cfg);
ESP_RETURN_ON_FALSE(platform->lvgl.touch_indev != NULL, ESP_FAIL, TAG, "failed to add touch to lvgl");
platform->lvgl->touch_indev = lvgl_port_add_touch(&touch_cfg);
ESP_RETURN_ON_FALSE(platform->lvgl->touch_indev != NULL, ESP_FAIL, TAG, "failed to add touch to lvgl");
}
return ESP_OK;
}
esp_err_t nb_platform_create(nb_platform_config_t config, nb_platform_t* platform) {
ESP_LOGI(TAG, "display with driver %s", config.display_driver.name);
ESP_RETURN_ON_ERROR(
nb_display_create(config.display_driver, &(platform->display)),
nbi_tag,
"display driver init failed"
);
nb_platform_t _Nonnull* nb_platform_create(nb_platform_config_t _Nonnull* config) {
nb_platform_t* platform = malloc(sizeof(nb_platform_t));
ESP_LOGI(TAG, "touch with driver %s", config.touch_driver.name);
ESP_RETURN_ON_ERROR(
nb_touch_create(config.touch_driver, &(platform->touch)),
nbi_tag,
"touch driver init failed"
);
NB_ASSERT(config->display_driver != NULL, "no display driver configured");
nb_display_driver_t display_driver = config->display_driver();
ESP_LOGI(TAG, "display with driver %s", display_driver.name);
platform->display = nb_display_create(&display_driver);
ESP_RETURN_ON_ERROR(
prv_lvgl_init(platform),
nbi_tag,
"lvgl init failed"
);
return ESP_OK;
if (config->touch_driver != NULL) {
nb_touch_driver_t touch_driver = config->touch_driver();
ESP_LOGI(TAG, "touch with driver %s", touch_driver.name);
platform->touch = nb_touch_create(&touch_driver);
} else {
ESP_LOGI(TAG, "no touch configured");
platform->touch = NULL;
}
ESP_ERROR_CHECK(prv_lvgl_init(platform));
return platform;
}

View File

@ -1,9 +1,9 @@
#include "nb_touch.h"
#include <esp_check.h>
#include "nb_internal.h"
esp_err_t nb_touch_create(nb_touch_driver_t driver, nb_touch_t* touch) {
ESP_RETURN_ON_ERROR(driver.init_io(&(touch->io_handle)), nbi_tag, "failed to init io");
ESP_RETURN_ON_ERROR(driver.create_touch(touch->io_handle, &(touch->touch_handle)), nbi_tag, "failed to create driver");
return ESP_OK;
nb_touch_t _Nonnull* nb_touch_create(nb_touch_driver_t _Nonnull* driver) {
nb_touch_t _Nonnull* touch = malloc(sizeof(nb_touch_t));
assert(driver->init_io(&(touch->io_handle)) == ESP_OK);
driver->create_touch(touch->io_handle, &(touch->touch_handle));
return touch;
}

View File

@ -1,5 +1,5 @@
idf_component_register(
SRC_DIRS "."
INCLUDE_DIRS "."
SRC_DIRS "src"
"src/hello_world"
REQUIRES nanobake board_2432s024
)

View File

@ -1,48 +0,0 @@
#include <esp_err.h>
#include <esp_log.h>
#include <nanobake.h>
// Nanobake board support with drivers:
#include <board_2432s024_touch.h>
#include <board_2432s024_display.h>
#include <esp_lvgl_port.h>
static const char *TAG = "main";
static void prv_button_callback(lv_event_t* event) {
ESP_LOGI(TAG, "tap");
}
static void prv_main_vgl(nb_platform_t* platform) {
lv_obj_t* scr = lv_scr_act();
lvgl_port_lock(0);
lv_obj_t* label = lv_label_create(scr);
lv_label_set_recolor(label, true);
lv_obj_set_width(label, (lv_coord_t)platform->display.horizontal_resolution);
lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_CENTER, 0);
lv_label_set_text(label, "Hello, world!");
lv_obj_align(label, LV_ALIGN_CENTER, 0, -30);
lv_obj_t* btn = lv_btn_create(scr);
label = lv_label_create(btn);
lv_label_set_text_static(label, "Button");
lv_obj_align(btn, LV_ALIGN_CENTER, 0, 30);
lv_obj_add_event_cb(btn, prv_button_callback, LV_EVENT_CLICKED, NULL);
lvgl_port_unlock();
}
void app_main(void) {
nb_platform_config_t platform_config = {
.touch_driver = board_2432s024_create_touch_driver(),
.display_driver = board_2432s024_create_display_driver()
};
static nb_platform_t platform;
ESP_ERROR_CHECK(nb_platform_create(platform_config, &platform));
prv_main_vgl(&platform);
}

View File

@ -0,0 +1,42 @@
#include "hello_world.h"
#include "esp_lvgl_port.h"
#include <esp_log.h>
#include "nb_platform.h"
static const char* TAG = "app_helloworld";
static void prv_on_button_click(lv_event_t _Nonnull* event) {
ESP_LOGI(TAG, "button clicked");
}
static void prv_on_create(nb_platform_t _Nonnull* platform, lv_obj_t _Nonnull* lv_parent) {
lvgl_port_lock(0);
lv_obj_t* label = lv_label_create(lv_parent);
lv_label_set_recolor(label, true);
lv_obj_set_width(label, (lv_coord_t)platform->display->horizontal_resolution);
lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_CENTER, 0);
lv_label_set_text(label, "Hello, world!");
lv_obj_align(label, LV_ALIGN_CENTER, 0, -30);
lv_obj_t* btn = lv_btn_create(lv_parent);
label = lv_label_create(btn);
lv_label_set_text_static(label, "Button");
lv_obj_align(btn, LV_ALIGN_CENTER, 0, 30);
lv_obj_add_event_cb(btn, prv_on_button_click, LV_EVENT_CLICKED, NULL);
lvgl_port_unlock();
}
nb_app_config_t hello_world_app_config() {
nb_app_config_t config = {
.id = "helloworld",
.name = "Hello World",
.type = USER,
.on_create = &prv_on_create,
.on_update = NULL,
.on_destroy = NULL
};
return config;
}

View File

@ -0,0 +1,8 @@
#ifndef NANOBAKE_HELLO_WORLD_H
#define NANOBAKE_HELLO_WORLD_H
#include "nb_app.h"
nb_app_config_t hello_world_app_config();
#endif //NANOBAKE_HELLO_WORLD_H

20
main/src/main.c Normal file
View File

@ -0,0 +1,20 @@
#include <nanobake.h>
// Hardware
#include <board_2432s024_touch.h>
#include <board_2432s024_display.h>
// Apps
#include "hello_world/hello_world.h"
void app_main(void) {
static nb_platform_config_t platform_config = {
.display_driver = &board_2432s024_create_display_driver,
.touch_driver = &board_2432s024_create_touch_driver,
.apps = {
&hello_world_app_config
}
};
nanobake_run(&platform_config);
}