Compare commits

..
2 Commits
Author SHA1 Message Date
mateusz b607bd8d41 oled shows temp correctly 2026-09-25 16:33:06 +02:00
mateusz 73f4e0d1c6 oled v1 2026-09-25 16:21:37 +02:00
8 changed files with 5694 additions and 13 deletions
+41 -1
View File
@@ -79,10 +79,50 @@ dependencies:
source: source:
type: idf type: idf
version: 6.1.0 version: 6.1.0
lvgl/lvgl:
component_hash: 7d82410747bbfb319531449749e940f440f8414f041f9eaba13f0d1dd7f58cdd
dependencies:
- name: espressif/freetype
registry_url: https://components.espressif.com
require: private
rules:
- if: idf_version >=5.5 && $CONFIG{LV_USE_FREETYPE} == True
version: ^2.14.3
- name: espressif/libjpeg-turbo
registry_url: https://components.espressif.com
require: private
rules:
- if: idf_version >=5.5 && $CONFIG{LV_USE_LIBJPEG_TURBO} == True
version: ^3.1.1
- name: espressif/libpng
registry_url: https://components.espressif.com
require: private
rules:
- if: idf_version >=5.5 && $CONFIG{LV_USE_LIBPNG} == True
version: ^1.6.58
- name: espressif/lz4
registry_url: https://components.espressif.com
require: private
rules:
- if: idf_version >=5.5 && $CONFIG{LV_USE_LZ4} == True && $CONFIG{LV_USE_LZ4_INTERNAL} == False
version: ^1.10.0
source:
registry_url: https://components.espressif.com/
type: service
version: 9.6.0~1
tny-robotics/sh1106-esp-idf:
component_hash: 036f5aad73224c563c3edb5db6f9833f3eb47c41b3d479740edddf49488ced69
dependencies: []
source:
registry_url: https://components.espressif.com/
type: service
version: 1.0.2
direct_dependencies: direct_dependencies:
- esp-idf-lib/bmp280 - esp-idf-lib/bmp280
- espressif/cjson - espressif/cjson
- idf - idf
manifest_hash: 2b2fa2812480ea444900bab732b40d55618df67e034421f1a347f9de48f70eeb - lvgl/lvgl
- tny-robotics/sh1106-esp-idf
manifest_hash: 35a13c57b5d80651f5e8a8efa2264804bbeec721529fc3fac1741cec178851f2
target: esp32 target: esp32
version: 3.0.0 version: 3.0.0
+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") INCLUDE_DIRS "." "../common_esp32")
+2
View File
@@ -16,3 +16,5 @@ dependencies:
# public: true # public: true
esp-idf-lib/bmp280: '*' esp-idf-lib/bmp280: '*'
espressif/cjson: '*' espressif/cjson: '*'
lvgl/lvgl: ^9
tny-robotics/sh1106-esp-idf: ^1.0.1
+43 -3
View File
@@ -1,6 +1,8 @@
#include <stdio.h>
#include <freertos/FreeRTOS.h> #include <freertos/FreeRTOS.h>
#include <freertos/task.h> #include <freertos/task.h>
#include <esp_system.h> #include <esp_system.h>
#include <esp_timer.h>
#include "nvs_flash.h" #include "nvs_flash.h"
#include "esp_http_server.h" #include "esp_http_server.h"
@@ -12,16 +14,37 @@
#include "ygg_bmp280.h" #include "ygg_bmp280.h"
#include "ygg_http_req.h" #include "ygg_http_req.h"
#include "ygg_state.h" #include "ygg_state.h"
#include "ygg_sh1106.h"
#include "lvgl.h"
static const char *TAG = "thermo_sensor"; static const char *TAG = "thermo_sensor";
static lv_display_t *sh_display = NULL;
static lv_obj_t *label = NULL;
bmp280_t dev_bmp_280; bmp280_t dev_bmp_280;
void main_loop(void *pvParameters) { static void lvgl_tick_cb(void *arg) {
lv_tick_inc(10);
}
void lvgl_task(void *pvParameters) {
while (1) {
lv_timer_handler();
vTaskDelay(pdMS_TO_TICKS(10));
}
}
void sensor_task(void *pvParameters) {
// Wait 1s, to avoid corrupted first sensor readout // Wait 1s, to avoid corrupted first sensor readout
vTaskDelay(pdMS_TO_TICKS(1000)); vTaskDelay(pdMS_TO_TICKS(1000));
while (1) { while (1) {
ygg_bmp280_read_sensor(&dev_bmp_280); ygg_bmp280_read_sensor(&dev_bmp_280);
// Najpierw OLED, potem (blokujący) HTTP — żeby ekran
// odświeżył się nawet gdy serwer nie odpowiada.
char buf[32];
snprintf(buf, sizeof(buf), "Temp: %.2f C",
CURR_READOUT.temperature);
ygg_set_display_label(sh_display, label, buf);
ygg_send_sensor_readouts(BASE_URL, ygg_send_sensor_readouts(BASE_URL,
SENSOR_NAME, SENSOR_NAME,
CURR_READOUT); CURR_READOUT);
@@ -57,7 +80,24 @@ void app_main()
httpd_register_uri_handler(server, &ygg_hum_uri); httpd_register_uri_handler(server, &ygg_hum_uri);
ESP_ERROR_CHECK(i2cdev_init()); ESP_ERROR_CHECK(i2cdev_init());
sh_display = ygg_sh1106_init();
// LVGL potrzebuje regularnego ticka, inaczej redraw nigdy się nie wykona
// (to był powód, dla którego wisiało "FIRST").
const esp_timer_create_args_t lvgl_tick_args = {
.callback = lvgl_tick_cb,
.name = "lvgl_tick",
};
esp_timer_handle_t lvgl_tick_handle = NULL;
ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_args, &lvgl_tick_handle));
ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_handle, 10 * 1000));
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); ygg_bmp280_init(&dev_bmp_280);
xTaskCreatePinnedToCore(main_loop, "bmp280_test", configMINIMAL_STACK_SIZE * 8, NULL, 5, NULL, APP_CPU_NUM); xTaskCreatePinnedToCore(lvgl_task, "lvgl", 4096, NULL, 4, NULL, APP_CPU_NUM);
xTaskCreatePinnedToCore(sensor_task, "bmp280_test", 8192, NULL, 5, NULL, APP_CPU_NUM);
} }
+144
View File
@@ -0,0 +1,144 @@
#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_lcd_panel_draw_bitmap(panel_handle, x1, y1, x2 + 1, y2 + 1, oled_buffer);
lv_display_flush_ready(disp);
}
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, const 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_set_style_text_align(label, LV_TEXT_ALIGN_CENTER, 0);
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, const char *str);
#endif
+979 -8
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff