This commit is contained in:
2026-09-25 16:21:37 +02:00
parent b2d268c5a0
commit 73f4e0d1c6
8 changed files with 5666 additions and 10 deletions
+1 -1
View File
@@ -1,2 +1,2 @@
idf_component_register(SRCS "thermo_sensor.c" "ygg_http_routes.c" "ygg_bmp280.c" "ygg_http_req.c" "ygg_state.c" "../common_esp32/ygg_wifi.c" "../common_esp32/ygg_http_server.c"
idf_component_register(SRCS "thermo_sensor.c" "ygg_http_routes.c" "ygg_bmp280.c" "ygg_http_req.c" "ygg_state.c" "ygg_sh1106.c" "../common_esp32/ygg_wifi.c" "../common_esp32/ygg_http_server.c"
INCLUDE_DIRS "." "../common_esp32")
+2
View File
@@ -16,3 +16,5 @@ dependencies:
# public: true
esp-idf-lib/bmp280: '*'
espressif/cjson: '*'
lvgl/lvgl: ^9
tny-robotics/sh1106-esp-idf: ^1.0.1
+12
View File
@@ -12,20 +12,26 @@
#include "ygg_bmp280.h"
#include "ygg_http_req.h"
#include "ygg_state.h"
#include "ygg_sh1106.h"
#include "lvgl.h"
static const char *TAG = "thermo_sensor";
static lv_display_t *sh_display = NULL;
static lv_obj_t *label = NULL;
bmp280_t dev_bmp_280;
void main_loop(void *pvParameters) {
// Wait 1s, to avoid corrupted first sensor readout
vTaskDelay(pdMS_TO_TICKS(1000));
while (1) {
lv_timer_handler();
ygg_bmp280_read_sensor(&dev_bmp_280);
ygg_send_sensor_readouts(BASE_URL,
SENSOR_NAME,
CURR_READOUT);
vTaskDelay(pdMS_TO_TICKS(10000));
ygg_set_display_label(sh_display, label, "ANOTHER");
}
}
@@ -57,6 +63,12 @@ void app_main()
httpd_register_uri_handler(server, &ygg_hum_uri);
ESP_ERROR_CHECK(i2cdev_init());
sh_display = ygg_sh1106_init();
lv_obj_t *scr = lv_display_get_screen_active(sh_display);
label = lv_label_create(scr);
ygg_set_display_label(sh_display, label, "FIRST");
ygg_bmp280_init(&dev_bmp_280);
xTaskCreatePinnedToCore(main_loop, "bmp280_test", configMINIMAL_STACK_SIZE * 8, NULL, 5, NULL, APP_CPU_NUM);
}
+147
View File
@@ -0,0 +1,147 @@
#include "ygg_sh1106.h"
#include "lvgl.h"
#include "i2cdev.h"
#include "esp_log.h"
#include "esp_lcd_io_i2c.h"
#include "esp_lcd_panel_ops.h"
#include "esp_timer.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_sh1106.h"
#define I2C_HOST I2C_NUM_0
#define EXAMPLE_I2C_MASTER_FREQ_HZ 400000
#define EXAMPLE_LCD_V_RES 64
#define EXAMPLE_LCD_H_RES 128
#define EXAMPLE_LVGL_PALETTE_SIZE 8
#define SH1106_I2C_SDA_GPIO 19
#define SH1106_I2C_SCL_GPIO 23
static uint8_t oled_buffer[EXAMPLE_LCD_H_RES * EXAMPLE_LCD_V_RES / 8];
static void example_lvgl_flush_cb(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map)
{
esp_lcd_panel_handle_t panel_handle = lv_display_get_user_data(disp);
// This is necessary because LVGL reserves 2 x 4 bytes in the buffer, as these are assumed to be used as a palette. Skip the palette here
// More information about the monochrome, please refer to https://docs.lvgl.io/9.2/porting/display.html#monochrome-displays
px_map += EXAMPLE_LVGL_PALETTE_SIZE;
uint16_t hor_res = lv_display_get_physical_horizontal_resolution(disp);
int x1 = area->x1;
int x2 = area->x2;
int y1 = area->y1;
int y2 = area->y2;
for (int y = y1; y <= y2; y++) {
for (int 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 = 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_LOGI("FLUSH",
"area: (%d,%d)-(%d,%d), display: %dx%d",
x1, y1, x2, y2,
lv_display_get_horizontal_resolution(disp),
lv_display_get_vertical_resolution(disp));
esp_lcd_panel_draw_bitmap(panel_handle, x1, y1, x2 + 1, y2 + 1, oled_buffer);
}
lv_display_t* ygg_sh1106_init() {
//ESP_ERROR_CHECK(i2cdev_init());
static i2c_dev_t oled_i2c_dev;
memset(&oled_i2c_dev, 0, sizeof(oled_i2c_dev));
oled_i2c_dev.port = I2C_HOST; // numer portu I2C
oled_i2c_dev.addr = SH1106_I2C_ADDR; // adres OLED-a (0x3C)
oled_i2c_dev.cfg.sda_io_num = SH1106_I2C_SDA_GPIO; // SDA z "idf menuconfig"
oled_i2c_dev.cfg.scl_io_num = SH1106_I2C_SCL_GPIO; // SCL z "idf menuconfig"
oled_i2c_dev.cfg.master.clk_speed = EXAMPLE_I2C_MASTER_FREQ_HZ;
oled_i2c_dev.cfg.sda_pullup_en = true; // OLED zwykle nie ma pull-upów
oled_i2c_dev.cfg.scl_pullup_en = true;
// Rejestracja urządzenia + utworzenie mutexu.
// Przy pierwszym urządzeniu na danym porcie i2cdev tworzy magistralę
// (i2c_new_master_bus) z podanymi pinami SDA/SCL.
ESP_ERROR_CHECK(i2c_dev_create_mutex(&oled_i2c_dev));
// Opcjonalnie: sprawdź, czy OLED odpowiada na magistrali.
ESP_ERROR_CHECK(i2c_dev_check_present(&oled_i2c_dev));
// Pobierz współdzielony uchwyt magistrali zarządzanej przez i2cdev,
// żeby przekazać go do natywnego komponentu esp_lcd.
// Dzięki temu inne sensory na i2cdev i wyświetlacz na esp_lcd
// bezpiecznie dzielą tę samą magistralę I2C.
// UWAGA: uchwytem zarządza i2cdev — NIE wołaj na nim i2c_del_master_bus().
i2c_master_bus_handle_t i2c_bus_handle = NULL;
ESP_ERROR_CHECK(i2cdev_get_shared_handle(I2C_HOST, (void **)&i2c_bus_handle));
// Create the i2c io handle
esp_lcd_panel_io_handle_t io_handle = NULL;
esp_lcd_panel_io_i2c_config_t io_config = ESP_SH1106_DEFAULT_IO_CONFIG;
ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c(i2c_bus_handle, &io_config, &io_handle));
/* SCREEN CONFIGURATION */
// sh1106 panel configuration (most of the values are not used, but must be set to avoid cpp warnings)
esp_lcd_panel_dev_config_t panel_config = {
.reset_gpio_num = -1, // sh1106 does not have a reset pin, so set to -1
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, // not even used, but must be set to avoid cpp warnings
.data_endian = LCD_RGB_DATA_ENDIAN_LITTLE, // not even used, but must be set to avoid cpp warnings
.bits_per_pixel = SH1106_PIXELS_PER_BYTE / 8, // bpp = 1 (monochrome, that's important)
.flags = {
.reset_active_high = false, // not even used, but must be set to avoid cpp warnings
},
.vendor_config = NULL, // no need for custom vendor config, not implemented
};
// Create the panel handle from the sh1106 driver
esp_lcd_panel_handle_t panel_handle = NULL;
ESP_ERROR_CHECK(esp_lcd_new_panel_sh1106(io_handle, &panel_config, &panel_handle));
// Reset the screen (no reset pin, so it's a no-op here, optional)
ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));
// Initialize the screen (this one isn't optional at all!)
ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));
// Turn on the screen (Easier to see something, right?)
ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true));
void *buf = NULL;
size_t draw_buffer_sz = EXAMPLE_LCD_H_RES * EXAMPLE_LCD_V_RES / 8 + EXAMPLE_LVGL_PALETTE_SIZE;
buf = heap_caps_calloc(1, draw_buffer_sz, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
lv_init();
lv_display_t *display = lv_display_create(EXAMPLE_LCD_H_RES, EXAMPLE_LCD_V_RES);
lv_display_set_user_data(display, panel_handle);
lv_display_set_color_format(display, LV_COLOR_FORMAT_I1);
lv_display_set_buffers(display, buf, NULL, draw_buffer_sz, LV_DISPLAY_RENDER_MODE_FULL);
lv_display_set_flush_cb(display, example_lvgl_flush_cb);
lv_obj_t *scr = lv_display_get_screen_active(display);
return display;
}
void ygg_set_display_label(lv_display_t *display, lv_obj_t *label, char *str) {
lv_label_set_text(label, str);
/* Size of the screen (if you use rotation 90 or 270, please use lv_display_get_vertical_resolution) */
lv_obj_set_width(label, lv_display_get_horizontal_resolution(display));
lv_obj_align(label, LV_ALIGN_TOP_MID, 0, 0);
}
+9
View File
@@ -0,0 +1,9 @@
#ifndef YGG_SH1106
#define YGG_SH1106
#include "lvgl.h"
lv_display_t* ygg_sh1106_init();
void ygg_set_display_label(lv_display_t *display, lv_obj_t *label, char *str);
#endif