[ESP32-S2-Kaluga-1 Review] 5. Image display, button and RGB LED operation
[Copy link]
This week I tinkered with the onboard buttons and RGB LEDs.
Let's talk about the specific functions implemented below. Use the official routines to modify and display your own logo picture.
Next, we will operate the RGB LED and modify the official example. The code is as follows:
The meaning of changing the code is that red, blue and green are displayed alternately:
The display effect is as follows:
Next is the button control. The buttons of ESP32S2 are connected to the ADC, so the ADC needs to be used to read the corresponding voltage values. Different buttons or combination buttons have different voltage values when pressed, which need to be collected by the ADC.
The circuit diagram is as follows:
Note that the voltage value of button 1 on this schematic is not marked correctly. When I first used button K1, I used 2.41V and it was not correct. It could not be detected. Other buttons were all OK. Then I debugged it with the serial port and found that K1 was 2.3V.
As shown below:
Then use 2.3V to identify it normally.
The program I wrote uses two different tasks for RGB LED and button respectively. Pressing button K1 pauses the RGB LED task, and releasing button K1 resumes the RGB LED task.
The effect is as follows:
f((1 == key_pressed_status[0]) && (0 == Last_key_pressed_status[0])){ // pressed
vTaskSuspend(RGBLEDTask_Handler);
ESP_LOGI(TAG, "K1 activated");
}else if((0 == key_pressed_status[0]) && (1 == Last_key_pressed_status[0])){ // released
vTaskResume(RGBLEDTask_Handler);
ESP_LOGI(TAG, "K1 inactivated");
}else{
}
I'll paste the source code below.
But it should be noted that in addition to modifying the code in main.c, you also need to modify the Kconfig.probuild and CMakeLists.txt files
Source code (main.c):
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include "esp_wifi.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "esp_event.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_heap_caps.h"
#include "esp_log.h"
#include "esp_spiffs.h"
#include "lcd.h"
#include "jpeg.h"
#include "board.h"
#include "sccb.h"
#include "cam.h"
#include "ov2640.h"
#include "ov3660.h"
#include "sensor.h"
#include "driver/gpio.h"
#include "driver/adc.h"
#include "driver/rmt.h"
#include "led_strip.h"
static const char *TAG = "main";
#define IMAGE_MAX_SIZE (100 * 1024)/**< The maximum size of a single picture in the boot animation */
#define IMAGE_WIDTH (320) /*!< width of jpeg file */
#define IMAGE_HIGHT (240) /*!< height of jpeg file */
#define CAM_WIDTH (320) /* camera width */
#define CAM_HIGH (240) /* camera height */
#define DEFAULT_VREF 1100 /*!< Use adc2_vref_to_gpio() to obtain a better estimate */
#define NO_OF_SAMPLES 64
#define SAMPLE_TIME 200 /*!< Sampling time(ms) */
#define DEVIATION 0.1
static led_strip_t *strip;
static const adc_channel_t channel = ADC_CHANNEL_5;
static const adc_bits_width_t width = ADC_WIDTH_BIT_13;
static const adc_atten_t atten = ADC_ATTEN_DB_11;
static const adc_unit_t unit = ADC_UNIT_1;
static xQueueHandle adc_queue = NULL;
TaskHandle_t RGBLEDTask_Handler;
TaskHandle_t ButtonTask_Handler;
TaskHandle_t CameraTask_Handler;
static void start_display(void);
static void cam_task(void *arg);
void button_task(void *arg);
void led_task(void *arg);
esp_err_t Rmt_init(uint8_t gpio_num, int led_number, uint8_t rmt_channel);
void adc_init(void);
void button_task(void *arg);
double adc_voltage_conversion(uint32_t adc_reading);
void Button_Selection(double voltage);
void app_main()
{
start_display();
adc_queue = xQueueCreate(1, sizeof(double));
adc_init();
xTaskCreate(&button_task, "button_task", 3*1024, NULL, 4, &ButtonTask_Handler);
xTaskCreate(&led_task, "led_task", 3*1024, NULL, 5, &RGBLEDTask_Handler);
}
void adc_init(void)
{
if (unit == ADC_UNIT_1) {
adc1_config_width(width);
adc1_config_channel_atten(channel, atten);
} else {
adc2_config_channel_atten((adc2_channel_t)channel, atten);
}
}
double adc_voltage_conversion(uint32_t adc_reading)
{
double voltage = 0;
voltage = (2.60 * adc_reading) / 8191;
return voltage;
}
void button_task(void *arg)
{
while (1) {
uint32_t adc_reading = 0;
double voltage = 0;
for (int i = 0; i < NO_OF_SAMPLES; i++) {
if (unit == ADC_UNIT_1) {
adc_reading += adc1_get_raw((adc1_channel_t)channel);
} else {
int raw;
adc2_get_raw((adc2_channel_t)channel, width, &raw);
adc_reading += raw;
}
}
adc_reading /= NO_OF_SAMPLES;
voltage = adc_voltage_conversion(adc_reading);
ESP_LOGD(TAG, "ADC%d CH%d Raw: %d ; Voltage: %0.2lfV", unit, channel, adc_reading, voltage);
ESP_LOGI(TAG, "Voltage: %0.2lfV", voltage);
xQueueSend(adc_queue, (double *)&voltage, 0);
Button_Selection(voltage);
vTaskDelay(pdMS_TO_TICKS(SAMPLE_TIME));
}
vTaskDelete(NULL);
}
void Button_Selection(double voltage)
{
static bool key_pressed_status[6] = {0};
static bool Last_key_pressed_status[6] = {0};
if (voltage > 2.30 - DEVIATION && voltage <= 2.30 + DEVIATION) {
key_pressed_status[0] = 1;
ESP_LOGI(TAG, "K1 Pressed");
} else if (voltage > 1.98 - DEVIATION && voltage <= 1.98 + DEVIATION) {
key_pressed_status[1] = 1;
ESP_LOGI(TAG, "K2 Pressed");
} else if (voltage > 1.65 - DEVIATION && voltage <= 1.65 + DEVIATION) {
key_pressed_status[2] = 1;
ESP_LOGI(TAG, "K3 Pressed");
} else if (voltage > 1.11 - DEVIATION && voltage <= 1.11 + DEVIATION) {
key_pressed_status[3] = 1;
ESP_LOGI(TAG, "K4 Pressed");
} else if (voltage > 0.82 - DEVIATION && voltage <= 0.82 + DEVIATION) {
key_pressed_status[4] = 1;
ESP_LOGI(TAG, "K5 Pressed");
} else if (voltage > 0.38 - DEVIATION && voltage <= 0.38 + DEVIATION) {
key_pressed_status[5] = 1;
ESP_LOGI(TAG, "K6 Pressed");
}else{
key_pressed_status[0] = 0;
key_pressed_status[1] = 0;
key_pressed_status[2] = 0;
key_pressed_status[3] = 0;
key_pressed_status[4] = 0;
key_pressed_status[5] = 0;
}
if((1 == key_pressed_status[0]) && (0 == Last_key_pressed_status[0])){
vTaskSuspend(RGBLEDTask_Handler);
ESP_LOGI(TAG, "K1 activated");
}else if((0 == key_pressed_status[0]) && (1 == Last_key_pressed_status[0])){
vTaskResume(RGBLEDTask_Handler);
ESP_LOGI(TAG, "K1 inactivated");
}else{
}
Last_key_pressed_status[0] = key_pressed_status[0];
Last_key_pressed_status[1] = key_pressed_status[1];
Last_key_pressed_status[2] = key_pressed_status[2];
Last_key_pressed_status[3] = key_pressed_status[3];
Last_key_pressed_status[4] = key_pressed_status[4];
Last_key_pressed_status[5] = key_pressed_status[5];
}
esp_err_t Rmt_init(uint8_t gpio_num, int led_number, uint8_t rmt_channel)
{
ESP_LOGI(TAG, "Initializing WS2812");
rmt_config_t config = RMT_DEFAULT_CONFIG_TX(gpio_num, rmt_channel);
config.clk_div = 2;
ESP_ERROR_CHECK(rmt_config(&config));
ESP_ERROR_CHECK(rmt_driver_install(config.channel, 0, 0));
led_strip_config_t strip_config = LED_STRIP_DEFAULT_CONFIG(led_number, (led_strip_dev_t)config.channel);
strip = led_strip_new_rmt_ws2812(&strip_config);
if (!strip) {
ESP_LOGE(TAG, "install WS2812 driver failed");
return ESP_FAIL;
}
ESP_ERROR_CHECK(strip->clear(strip, 100));
return ESP_OK;
}
void led_task(void *arg)
{
static unsigned char LED_change_count1 = 0u;
static unsigned char LED_change_count2 = 0u;
static unsigned char LED_change_count3 = 0u;
ESP_ERROR_CHECK(Rmt_init(45, 4, RMT_CHANNEL_0));
ESP_ERROR_CHECK(strip->set_pixel(strip, 0, 0, 0, 0));
while (1) {
if((LED_change_count1 != 255)
&& (LED_change_count2 == 0)
&& (LED_change_count3 == 0))
{
LED_change_count1 ++;
}else if((LED_change_count1 == 255)
&& (LED_change_count2 != 255)
&& (LED_change_count3 == 0))
{
LED_change_count2 ++;
}else if((LED_change_count1 == 255)
&& (LED_change_count2 == 255)
&& (LED_change_count3 != 255))
{
LED_change_count3 ++;
}else if((LED_change_count1 != 0)
&& (LED_change_count2 == 255)
&& (LED_change_count3 == 255))
{
LED_change_count1 --;
}else if((LED_change_count1 == 0)
&& (LED_change_count2 != 0)
&& (LED_change_count3 == 255))
{
LED_change_count2 --;
}else if((LED_change_count1 == 0)
&& (LED_change_count2 == 0)
&& (LED_change_count3 != 0))
{
LED_change_count3 --;
}else
{
}
ESP_LOGI(TAG, "R-%d,G-%d,B-%d", LED_change_count1, LED_change_count2, LED_change_count3);
ESP_ERROR_CHECK(strip->set_pixel(strip, 0, LED_change_count1, LED_change_count2, LED_change_count3));
ESP_ERROR_CHECK(strip->refresh(strip, 0));
vTaskDelay(pdMS_TO_TICKS(10));
}
vTaskDelete(NULL);
}
static void cam_task(void *arg)
{
cam_config_t cam_config = {
.bit_width = 8,
#ifdef CONFIG_CAMERA_JPEG_MODE
.mode.jpeg = 1,
#else
.mode.jpeg = 0,
#endif
.xclk_fre = 16 * 1000 * 1000,
.pin = {
.xclk = CAM_XCLK,
.pclk = CAM_PCLK,
.vsync = CAM_VSYNC,
.hsync = CAM_HSYNC,
},
.pin_data = {CAM_D0, CAM_D1, CAM_D2, CAM_D3, CAM_D4, CAM_D5, CAM_D6, CAM_D7},
.vsync_invert = true,
.hsync_invert = false,
.size = {
.width = CAM_WIDTH,
.high = CAM_HIGH,
},
.max_buffer_size = 8 * 1024,
.task_stack = 1024,
.task_pri = configMAX_PRIORITIES
};
cam_config.frame1_buffer = (uint8_t *)heap_caps_malloc(CAM_WIDTH * CAM_HIGH * 2 * sizeof(uint8_t), MALLOC_CAP_SPIRAM);
cam_config.frame2_buffer = (uint8_t *)heap_caps_malloc(CAM_WIDTH * CAM_HIGH * 2 * sizeof(uint8_t), MALLOC_CAP_SPIRAM);
cam_init(&cam_config);
sensor_t sensor;
int camera_version = 0;
SCCB_Init(CAM_SDA, CAM_SCL);
sensor.slv_addr = SCCB_Probe();
ESP_LOGI(TAG, "sensor_id: 0x%x\n", sensor.slv_addr);
#ifdef CONFIG_CAMERA_OV2640
camera_version = 2640;
#endif
#ifdef CONFIG_CAMERA_OV3660
camera_version = 3660;
#endif
#ifdef CONFIG_CAMERA_AUTO
#endif
if (sensor.slv_addr == 0x30 || camera_version == 2640) {
ESP_LOGI(TAG, "OV2640 init start...");
if (OV2640_Init(0, 1) != 0) {
goto fail;
}
if (cam_config.mode.jpeg) {
OV2640_JPEG_Mode();
} else {
OV2640_RGB565_Mode(false);
}
OV2640_ImageSize_Set(800, 600);
OV2640_ImageWin_Set(0, 0, 800, 600);
OV2640_OutSize_Set(CAM_WIDTH, CAM_HIGH);
} else if (sensor.slv_addr == 0x3C || camera_version == 3660) {
sensor.slv_addr = 0x3C;
ESP_LOGI(TAG, "OV3660 init start...");
ov3660_init(&sensor);
sensor.init_status(&sensor);
if (sensor.reset(&sensor) != 0) {
goto fail;
}
if (cam_config.mode.jpeg) {
sensor.set_pixformat(&sensor, PIXFORMAT_JPEG);
} else {
sensor.set_pixformat(&sensor, PIXFORMAT_RGB565);
}
sensor.set_res_raw(&sensor, 0, 0, 2079, 1547, 8, 2, 1920, 800, CAM_WIDTH, CAM_HIGH, true, true);
sensor.set_vflip(&sensor, 1);
sensor.set_hmirror(&sensor, 1);
sensor.set_pll(&sensor, false, 15, 1, 0, false, 0, true, 5);
} else {
ESP_LOGE(TAG, "sensor is temporarily not supported\n");
goto fail;
}
ESP_LOGI(TAG, "camera init done\n");
cam_start();
while (1) {
uint8_t *cam_buf = NULL;
cam_take(&cam_buf);
#ifdef CONFIG_CAMERA_JPEG_MODE
int w, h;
uint8_t *img = jpeg_decode(cam_buf, &w, &h);
if (img) {
ESP_LOGI(TAG, "jpeg: w: %d, h: %d\n", w, h);
lcd_set_index(0, 0, w - 1, h - 1);
lcd_write_data(img, w * h * sizeof(uint16_t));
free(img);
}
#else
lcd_set_index(0, 0, CAM_WIDTH - 1, CAM_HIGH - 1);
lcd_write_data(cam_buf, CAM_WIDTH * CAM_HIGH * 2);
#endif
cam_give(cam_buf);
gpio_set_level(LCD_BK, 1);
gpio_set_level(LCD_BK, 0);
}
fail:
free(cam_config.frame1_buffer);
free(cam_config.frame2_buffer);
cam_deinit();
vTaskDelete(NULL);
}
static void start_display(void)
{
lcd_config_t lcd_config = {
#ifdef CONFIG_LCD_ST7789
.clk_fre = 80 * 1000 * 1000,
#endif
#ifdef CONFIG_LCD_ILI9341
.clk_fre = 40 * 1000 * 1000,
#endif
.pin_clk = LCD_CLK,
.pin_mosi = LCD_MOSI,
.pin_dc = LCD_DC,
.pin_cs = LCD_CS,
.pin_rst = LCD_RST,
.pin_bk = LCD_BK,
.max_buffer_size = 2 * 1024,
.horizontal = 2
};
lcd_init(&lcd_config);
ESP_LOGI(TAG, "My Project Start ....");
esp_vfs_spiffs_conf_t conf = {
.base_path = "/spiffs",
.partition_label = NULL,
.max_files = 5,
.format_if_mount_failed = false
};
ESP_ERROR_CHECK(esp_vfs_spiffs_register(&conf));
size_t total = 0, used = 0;
ESP_ERROR_CHECK(esp_spiffs_info(NULL, &total, &used));
uint8_t *img = NULL;
uint8_t *buf = malloc(IMAGE_MAX_SIZE);
int read_bytes = 0;
int width = 0, height = 0;
FILE *fd = fopen("/spiffs/1.jpg", "r");
read_bytes = fread(buf, 1, IMAGE_MAX_SIZE, fd);
ESP_LOGI(TAG, "spiffs:read_bytes:%d fd: %p", read_bytes, fd);
fclose(fd);
img = jpeg_decode(buf, &width, &height);
lcd_set_index(0, 0, IMAGE_WIDTH - 1, IMAGE_HIGHT - 1);
lcd_write_data(img, IMAGE_WIDTH * IMAGE_HIGHT * sizeof(uint16_t));
free(buf);
vTaskDelay(2000 / portTICK_RATE_MS);
}
Kconfig.probuild file, this file is the routine configuration file of mebuconfig:
menu "Example Configuration"
choice CAMERA_PAD_TYPE
prompt "camera pad type"
default CAMERA_PAD_ESP32_S2_KALUGA_V1_3
config CAMERA_PAD_ESP32_S2_KALUGA_V1_3
bool "ESP32-S2-KALUGA_V1.3"
config CAMERA_PAD_ESP32_S2_KALUGA_V1_2
bool "ESP32-S2-KALUGA_V1.2"
config CAMERA_PAD_ESP32_S2_KALUGA_V1_1
bool "ESP32-S2-KALUGA_V1.1"
endchoice
choice LCD_PAD_TYPE
prompt "LCD pad type"
default LCD_PAD_ESP32_S2_KALUGA_V1_3
config LCD_PAD_ESP32_S2_KALUGA_V1_3
bool "ESP32-S2-KALUGA_V1.3"
config LCD_PAD_ESP32_S2_KALUGA_V1_2
bool "ESP32-S2-KALUGA_V1.2"
config LCD_PAD_ESP32_S2_KALUGA_V1_1
bool "ESP32-S2-KALUGA_V1.1"
endchoice
choice CAMERA_VERSION
prompt "Camera version"
default CAMERA_OV2640
config CAMERA_OV2640
bool "OV2640"
config CAMERA_OV3660
bool "OV3660"
config CAMERA_AUTO
bool "Auto (If you choose this mode, Dont insert the Audio board!)"
help
Set camera version
endchoice
choice LCD_VERSION
prompt "LCD version"
default ILI9341
config LCD_ST7789
bool "ST7789"
config LCD_ILI9341
bool "ILI9341"
help
Set LCD version
endchoice
config CAMERA_JPEG_MODE
bool "jpeg mode"
endmenu
CmakeLists.txt file:
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
set(EXTRA_COMPONENT_DIRS "../../components/board"
"../../components/lcd"
"../../components/jpeg"
"../../components/cam"
"../../components/sensors"
"../../components/led_strip"
)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
add_compile_options (-fdiagnostics-color=always)
project(esp32-s2-hmi)
Then use the terminal to enter the project folder and set the environment variables: $HOME/esp/esp-idf/export.sh
Set the compilation type and enter menuconfig for configuration: idf.py set-target esp32s2 menuconfig
The setup is as follows:
Compile: idf.py build
Download: idf.py -p /dev/ttyUSB1 flash
Monitoring: idf.py -p /dev/ttyUSB1 monitor
knock off
|