一、项目名称:
车载氛围灯
二、项目概述:
项目,旨在为车辆内部营造一个独特的氛围。通过ESP32和触摸屏,RGB LED灯条,可以在车内创造出多彩的灯光效果。这些灯光可以实现各种模式和效果,如渐变、闪烁、呼吸等。通过连接麦克风,采样麦克风数据,灯光可以根据音乐的节奏和音量进行调整,实现与音乐的同步效果。此外,还可以通过触摸屏来控制灯光的开关、颜色和模式等。
三、作品实物图
四、演示视频
视频.zip (29.3 MB)
五、代码
5.1 触屏驱动移植代码
#include "cst816.h"
#include <driver/i2c.h>
#include <esp_log.h>
#ifdef LV_LVGL_H_INCLUDE_SIMPLE
#include <lvgl.h>
#else
#include <lvgl/lvgl.h>
#endif
#include "../lvgl_i2c_conf.h"
#include "tp_i2c.h"
#define TAG "CST816T"
static cst816t_status_t cst816t_status;
uint8_t cst816t_read_len(uint16_t reg_addr, uint8_t *data, uint8_t len) {
uint8_t res = 0;
res = i2c_master_write_read_device(TOUCH_I2C_PORT, CST816T_ADDR, ®_addr, 1,
data, len, 1000 / portTICK_PERIOD_MS);
return res;
}
uint8_t cst816t_chipId(void) { return 0; }
static esp_err_t cst816t_get_touch_points_num(uint8_t *touch_points_num) {
uint8_t res = 0;
res = cst816t_read_len(0x02, touch_points_num, 1);
return res;
}
esp_err_t cst816t_read_pos(uint8_t *touch_points_num, uint16_t *x,
uint16_t *y) {
uint8_t data[4];
cst816t_get_touch_points_num(touch_points_num);
if (0 == *touch_points_num) {
*x = 0;
*y = 0;
return 1;
} else {
cst816t_read_len(0x03, data, 4);
*x = ((data[0] & 0x0f) << 8) | data[1];
*y = ((data[2] & 0x0f) << 8) | data[3];
}
return ESP_OK;
}
esp_err_t cst816t_i2c_read(uint8_t slave_addr, uint16_t register_addr,
uint8_t *data_buf, uint8_t len) {
i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create();
i2c_master_start(i2c_cmd);
i2c_master_write_byte(i2c_cmd, (slave_addr << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(i2c_cmd, register_addr, I2C_MASTER_ACK);
i2c_master_start(i2c_cmd);
i2c_master_write_byte(i2c_cmd, (slave_addr << 1) | I2C_MASTER_READ, true);
i2c_master_read_byte(i2c_cmd, data_buf, I2C_MASTER_NACK);
i2c_master_stop(i2c_cmd);
esp_err_t ret =
i2c_master_cmd_begin(TOUCH_I2C_PORT, i2c_cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(i2c_cmd);
return ret;
}
esp_err_t cst816t_i2c_write8(uint8_t slave_addr, uint16_t register_addr,
uint8_t data) {
i2c_cmd_handle_t i2c_cmd = i2c_cmd_link_create();
i2c_master_start(i2c_cmd);
i2c_master_write_byte(i2c_cmd, (slave_addr << 1) | I2C_MASTER_WRITE, true);
i2c_master_write_byte(i2c_cmd, register_addr, I2C_MASTER_ACK);
i2c_master_start(i2c_cmd);
i2c_master_write_byte(i2c_cmd, data, true);
i2c_master_stop(i2c_cmd);
esp_err_t ret =
i2c_master_cmd_begin(TOUCH_I2C_PORT, i2c_cmd, 1000 / portTICK_PERIOD_MS);
i2c_cmd_link_delete(i2c_cmd);
return ret;
}
void cst816t_init(uint16_t dev_addr) {
if (!cst816t_status.inited) {
cst816t_status.i2c_dev_addr = dev_addr;
uint8_t data_buf[10];
esp_err_t ret;
ESP_LOGI(TAG, "Checking for CST816T Touch Controller ");
if ((ret = cst816t_i2c_read(dev_addr, 0x15, &data_buf, 1) != ESP_OK)) {
vTaskDelay(pdMS_TO_TICKS(10));
ESP_LOGE(TAG, "Error reading from device: %s",
esp_err_to_name(ret)); // Only show error the first time
// return;
}
if ((ret = cst816t_i2c_read(dev_addr, 0xa7, &data_buf, 1) != ESP_OK)) {
ESP_LOGE(TAG, "Error reading from device: %s",
esp_err_to_name(ret)); // Only show error the first time
ESP_LOGE(TAG, "device ID: %02x", data_buf[0]);
// return;
}
cst816t_status.inited = true;
}
}
bool cst816t_read(lv_indev_drv_t *drv, lv_indev_data_t *data) {
uint8_t touch_points_num = 0;
uint16_t x = 0;
uint16_t y = 0;
cst816t_read_pos(&touch_points_num, &x, &y);
#if CONFIG_LV_TOUCH_INVERT_X
x = LV_HOR_RES_MAX - x;
#endif
#if 1
y = 280 - y;
#endif
#if 1
int16_t swap_buf = x;
x = y;
y = swap_buf;
#endif
data->point.x = x;
data->point.y = y;
if (touch_points_num > 0) {
data->state = LV_INDEV_STATE_PR;
ESP_LOGI(TAG, "X=%u Y=%u", data->point.x, data->point.y);
ESP_LOGV(TAG, "X=%u Y=%u", data->point.x, data->point.y);
} else {
data->state = LV_INDEV_STATE_REL;
}
return false;
}
5.2 测试用的LVGL代码
/* LVGL Example project
*
* Basic project to test LVGL on ESP32 based projects.
*
* This example code is in the Public Domain (or CC0 licensed, at your option.)
*
* Unless required by applicable law or agreed to in writing, this
* software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_freertos_hooks.h"
#include "freertos/semphr.h"
#include "esp_timer.h"
#include "esp_system.h"
#include "driver/gpio.h"
/* Littlevgl specific */
#ifdef LV_LVGL_H_INCLUDE_SIMPLE
#include "lvgl.h"
#else
#include "lvgl/lvgl.h"
#endif
#include "lvgl_helpers.h"
#ifndef CONFIG_LV_TFT_DISPLAY_MONOCHROME
#if defined CONFIG_LV_USE_DEMO_WIDGETS
#include "lv_examples/src/lv_demo_widgets/lv_demo_widgets.h"
#elif defined CONFIG_LV_USE_DEMO_KEYPAD_AND_ENCODER
#include "lv_examples/src/lv_demo_keypad_encoder/lv_demo_keypad_encoder.h"
#elif defined CONFIG_LV_USE_DEMO_BENCHMARK
#include "lv_examples/src/lv_demo_benchmark/lv_demo_benchmark.h"
#elif defined CONFIG_LV_USE_DEMO_STRESS
#include "lv_examples/src/lv_demo_stress/lv_demo_stress.h"
#else
#error "No demo application selected."
#endif
#endif
/*********************
* DEFINES
*********************/
#define TAG "demo"
#define LV_TICK_PERIOD_MS 1
/**********************
* STATIC PROTOTYPES
**********************/
static void lv_tick_task(void *arg);
static void guiTask(void *pvParameter);
static void create_demo_application(void);
/**********************
* STATIC VARIABLES
**********************/
static void event_handler(lv_obj_t *obj, lv_event_t event) {
if (event == LV_EVENT_VALUE_CHANGED) {
int n = lv_dropdown_get_selected(obj);
printf("num: %dn", n);
}
}
void lv_sport(void) {
/*Create a normal drop down list*/
// lv_obj_t *ddlist = lv_dropdown_create(lv_scr_act(), NULL);
// lv_dropdown_set_options(ddlist, "1n2n3n4");
// lv_obj_align(ddlist, NULL, LV_ALIGN_IN_TOP_MID, 0, 20);
// lv_obj_set_event_cb(ddlist, event_handler);
/*Create a Tab view object*/
lv_obj_t *tabview;
tabview = lv_tabview_create(lv_scr_act(), NULL);
/*Add 3 tabs (the tabs are page (lv_page) and can be scrolled*/
lv_obj_t *tab1 = lv_tabview_add_tab(tabview, "计划健身");
/*Add content to the tabs*/
lv_obj_t *dr_sport = lv_dropdown_create(tab1, NULL);
lv_obj_set_width(
dr_sport,
lv_obj_get_width_grid(tab1, 3, 1));
lv_dropdown_set_options(dr_sport, "Rainbown"
"Cometn"
"Sparkle");
lv_obj_align(dr_sport, NULL, LV_ALIGN_IN_TOP_LEFT, 0, 10);
}
/**********************
* APPLICATION MAIN
**********************/
void app_main() {
/* If you want to use a task to create the graphic, you NEED to create a Pinned task
* Otherwise there can be problem such as memory corruption and so on.
* NOTE: When not using Wi-Fi nor Bluetooth you can pin the guiTask to core 0 */
xTaskCreatePinnedToCore(guiTask, "gui", 4096*2, NULL, 0, NULL, 1);
}
/* Creates a semaphore to handle concurrent call to lvgl stuff
* If you wish to call *any* lvgl function from other threads/tasks
* you should lock on the very same semaphore! */
SemaphoreHandle_t xGuiSemaphore;
static void guiTask(void *pvParameter) {
(void) pvParameter;
xGuiSemaphore = xSemaphoreCreateMutex();
lv_init();
/* Initialize SPI or I2C bus used by the drivers */
lvgl_driver_init();
lv_color_t* buf1 = heap_caps_malloc(DISP_BUF_SIZE * sizeof(lv_color_t), MALLOC_CAP_DMA);
assert(buf1 != NULL);
/* Use double buffered when not working with monochrome displays */
#ifndef CONFIG_LV_TFT_DISPLAY_MONOCHROME
lv_color_t* buf2 = heap_caps_malloc(DISP_BUF_SIZE * sizeof(lv_color_t), MALLOC_CAP_DMA);
assert(buf2 != NULL);
#else
static lv_color_t *buf2 = NULL;
#endif
static lv_disp_buf_t disp_buf;
uint32_t size_in_px = DISP_BUF_SIZE;
#if defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_IL3820
|| defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_JD79653A
|| defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_UC8151D
|| defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_SSD1306
/* Actual size in pixels, not bytes. */
size_in_px *= 8;
#endif
/* Initialize the working buffer depending on the selected display.
* NOTE: buf2 == NULL when using monochrome displays. */
lv_disp_buf_init(&disp_buf, buf1, buf2, size_in_px);
lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.flush_cb = disp_driver_flush;
#if defined CONFIG_DISPLAY_ORIENTATION_PORTRAIT || defined CONFIG_DISPLAY_ORIENTATION_PORTRAIT_INVERTED
disp_drv.rotated = 1;
#endif
/* When using a monochrome display we need to register the callbacks:
* - rounder_cb
* - set_px_cb */
#ifdef CONFIG_LV_TFT_DISPLAY_MONOCHROME
disp_drv.rounder_cb = disp_driver_rounder;
disp_drv.set_px_cb = disp_driver_set_px;
#endif
disp_drv.buffer = &disp_buf;
lv_disp_drv_register(&disp_drv);
/* Register an input device when enabled on the menuconfig */
#if CONFIG_LV_TOUCH_CONTROLLER != TOUCH_CONTROLLER_NONE
lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv);
indev_drv.read_cb = touch_driver_read;
indev_drv.type = LV_INDEV_TYPE_POINTER;
lv_indev_drv_register(&indev_drv);
#endif
/* Create and start a periodic timer interrupt to call lv_tick_inc */
const esp_timer_create_args_t periodic_timer_args = {
.callback = &lv_tick_task,
.name = "periodic_gui"
};
esp_timer_handle_t periodic_timer;
ESP_ERROR_CHECK(esp_timer_create(&periodic_timer_args, &periodic_timer));
ESP_ERROR_CHECK(esp_timer_start_periodic(periodic_timer, LV_TICK_PERIOD_MS * 1000));
/* Create the demo application */
lv_sport();
while (1) {
/* Delay 1 tick (assumes FreeRTOS tick is 10ms */
vTaskDelay(pdMS_TO_TICKS(10));
/* Try to take the semaphore, call lvgl related function on success */
if (pdTRUE == xSemaphoreTake(xGuiSemaphore, portMAX_DELAY)) {
lv_task_handler();
xSemaphoreGive(xGuiSemaphore);
}
}
/* A task should NEVER return */
free(buf1);
#ifndef CONFIG_LV_TFT_DISPLAY_MONOCHROME
free(buf2);
#endif
vTaskDelete(NULL);
}
static void lv_tick_task(void *arg) {
(void) arg;
lv_tick_inc(LV_TICK_PERIOD_MS);
}
5.3 测试RGB灯的Python代码
# SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries
# SPDX-License-Identifier: MIT
"""
This example repeatedly displays all available animations, at a five second interval.
For NeoPixel FeatherWing. Update pixel_pin and pixel_num to match your wiring if using
a different form of NeoPixels.
This example does not work on SAMD21 (M0) boards.
"""
import board
import neopixel
from adafruit_led_animation.animation.blink import Blink
from adafruit_led_animation.animation.comet import Comet
from adafruit_led_animation.animation.sparkle import Sparkle
from adafruit_led_animation.animation.rainbow import Rainbow
from adafruit_led_animation.color import PURPLE, WHITE, AMBER, JADE, MAGENTA, ORANGE
# Update to match the pin connected to your NeoPixels
pixel_pin = board.D6
# Update to match the number of NeoPixels you have connected
pixel_num = 16
pixels = neopixel.NeoPixel(pixel_pin, pixel_num, brightness=0.5, auto_write=False)
blink = Blink(pixels, speed=0.5, color=JADE)
colorcycle = ColorCycle(pixels, speed=0.4, colors=[MAGENTA, ORANGE])
comet = Comet(pixels, speed=0.01, color=PURPLE, tail_length=10, bounce=True)
sparkle = Sparkle(pixels, speed=0.1, color=PURPLE, num_sparkles=10)
rainbow = Rainbow(pixels, speed=0.1, period=2)
animations = AnimationSequence(
comet,
blink,
sparkle,
rainbow,
advance_interval=5,
auto_clear=True,
)
while True:
animations.animate()
由于时间的原因,做完板子就没多少时间完成项目工程了,所以只能简单设计了个LVGL界面,移植触屏驱动,并且使用CircuitPython进行测试WS2812,实现一些简单的LED动画。
六、文档
文档.zip (1.35 MB)
阅读全文