i2c fixes + server improvements
This commit is contained in:
@@ -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" "ygg_sh1106.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" "ygg_lock.c" "../common_esp32/ygg_wifi.c" "../common_esp32/ygg_http_server.c"
|
||||
INCLUDE_DIRS "." "../common_esp32")
|
||||
|
||||
@@ -3,9 +3,11 @@
|
||||
#include <freertos/task.h>
|
||||
#include <esp_system.h>
|
||||
#include <esp_timer.h>
|
||||
#include <esp_log.h>
|
||||
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_http_server.h"
|
||||
#include "i2cdev.h"
|
||||
|
||||
#include "ygg_http_server.h"
|
||||
#include "ygg_http_routes.h"
|
||||
@@ -15,6 +17,7 @@
|
||||
#include "ygg_http_req.h"
|
||||
#include "ygg_state.h"
|
||||
#include "ygg_sh1106.h"
|
||||
#include "ygg_lock.h"
|
||||
#include "lvgl.h"
|
||||
|
||||
|
||||
@@ -25,7 +28,10 @@ bmp280_t dev_bmp_280;
|
||||
|
||||
void lvgl_task(void *pvParameters) {
|
||||
while (1) {
|
||||
lv_timer_handler();
|
||||
if (ygg_lvgl_lock_take(pdMS_TO_TICKS(100))) {
|
||||
lv_timer_handler();
|
||||
ygg_lvgl_lock_give();
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
}
|
||||
@@ -34,19 +40,31 @@ void sensor_task(void *pvParameters) {
|
||||
// Wait 1s, to avoid corrupted first sensor readout
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
while (1) {
|
||||
ygg_bmp280_read_sensor(&dev_bmp_280);
|
||||
esp_err_t err = ygg_bmp280_read_sensor(&dev_bmp_280);
|
||||
// Najpierw OLED, potem (blokujący) HTTP — żeby ekran
|
||||
// odświeżył się nawet gdy serwer nie odpowiada.
|
||||
// ygg_bmp280_read_sensor() aktualizuje CURR_READOUT TYLKO przy
|
||||
// sukcesie, więc przy błędzie pokazujemy ostatnie dobre + ERR
|
||||
// i NIE wysyłamy śmieci do bazy.
|
||||
char buf[256];
|
||||
snprintf(buf, sizeof(buf), "Temp: %.1f C\nHumid: %.1f %%\nPress: %.1f hPa",
|
||||
CURR_READOUT.temperature,
|
||||
CURR_READOUT.humidity,
|
||||
CURR_READOUT.pressure / 100.);
|
||||
ygg_set_display_label(sh_display, label, buf);
|
||||
ygg_send_sensor_readouts(BASE_URL,
|
||||
SENSOR_NAME,
|
||||
CURR_READOUT);
|
||||
vTaskDelay(pdMS_TO_TICKS(10000));
|
||||
if (err == ESP_OK) {
|
||||
snprintf(buf, sizeof(buf), "Temp: %.1f C\nHumid: %.1f %%\nPress: %.1f hPa",
|
||||
CURR_READOUT.temperature,
|
||||
CURR_READOUT.humidity,
|
||||
CURR_READOUT.pressure / 100.);
|
||||
ygg_set_display_label(sh_display, label, buf);
|
||||
ygg_send_sensor_readouts(BASE_URL,
|
||||
SENSOR_NAME,
|
||||
CURR_READOUT);
|
||||
} else {
|
||||
snprintf(buf, sizeof(buf), "SENSOR ERR\nTemp: %.1f C\nHumid: %.1f %%\nPress: %.1f hPa",
|
||||
CURR_READOUT.temperature,
|
||||
CURR_READOUT.humidity,
|
||||
CURR_READOUT.pressure / 100.);
|
||||
ygg_set_display_label(sh_display, label, buf);
|
||||
ESP_LOGW(TAG, "skipping HTTP POST, keeping last good readout");
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(60 * 1000));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,6 +95,12 @@ void app_main()
|
||||
httpd_register_uri_handler(server, &ygg_press_uri);
|
||||
httpd_register_uri_handler(server, &ygg_hum_uri);
|
||||
|
||||
// Jedna centralna inicjalizacja: mutexy + magistrala I2C.
|
||||
// Wcześniej i2cdev_init() był wołany w obu ygg_*_init() + bus był
|
||||
// tworzony przez dummy-device OLED, co mieszało.
|
||||
ygg_lock_init();
|
||||
ESP_ERROR_CHECK(i2cdev_init());
|
||||
|
||||
sh_display = ygg_sh1106_init();
|
||||
|
||||
lv_obj_t *scr = lv_display_get_screen_active(sh_display);
|
||||
|
||||
@@ -1,29 +1,52 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <bmp280.h>
|
||||
#include <esp_log.h>
|
||||
#include "ygg_bmp280.h"
|
||||
#include "ygg_state.h"
|
||||
#include "ygg_lock.h"
|
||||
|
||||
static const char *TAG = "ygg_bmp280";
|
||||
|
||||
void ygg_bmp280_init(bmp280_t *dev) {
|
||||
ESP_ERROR_CHECK(i2cdev_init());
|
||||
// UWAGA: i2cdev_init() + ygg_lock_init() robi app_main() raz przed
|
||||
// wszystkimi initami. Tutaj już nie tworzymy magistrali.
|
||||
bmp280_params_t params;
|
||||
bmp280_init_default_params(¶ms);
|
||||
memset(dev, 0, sizeof(bmp280_t));
|
||||
|
||||
ESP_ERROR_CHECK(bmp280_init_desc(dev, BMP280_I2C_ADDRESS_0, 0, I2C_MASTER_SDA, I2C_MASTER_SCL));
|
||||
// bmp280_init_desc() ustawia 1MHz - za szybko na pająka + dzieloną
|
||||
// magistralę z OLED. Zjeżdżamy do 100kHz przed pierwszym użyciem
|
||||
// (device jest dopinane do busa leniwie przy pierwszej transakcji).
|
||||
dev->i2c_dev.cfg.master.clk_speed = YGG_I2C_FREQ_HZ;
|
||||
ESP_ERROR_CHECK(bmp280_init(dev, ¶ms));
|
||||
|
||||
bool bme280p = dev->id == BME280_CHIP_ID;
|
||||
printf("BMP280: found %s\n", bme280p ? "BME280" : "BMP280");
|
||||
}
|
||||
|
||||
void ygg_bmp280_read_sensor(bmp280_t *dev) {
|
||||
float pressure, temperature, humidity;
|
||||
esp_err_t ygg_bmp280_read_sensor(bmp280_t *dev) {
|
||||
float pressure = 0, temperature = 0, humidity = 0;
|
||||
bool bme280p = dev->id == BME280_CHIP_ID;
|
||||
|
||||
if (bmp280_read_float(dev, &temperature, &pressure, &humidity) != ESP_OK)
|
||||
printf("Temperature/pressure reading failed\n");
|
||||
// Cały odczyt pod wspólnym mutexem I2C - OLED (esp_lcd) używa
|
||||
// tego samego busa bez wiedzy o mutexach i2cdev.
|
||||
// Bez tego flush LVGL potrafi wejść w środek odczytu -> timeout.
|
||||
if (!ygg_i2c_lock_take(pdMS_TO_TICKS(1000))) {
|
||||
ESP_LOGW(TAG, "I2C busy, skipping readout");
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
esp_err_t err = bmp280_read_float(dev, &temperature, &pressure, &humidity);
|
||||
ygg_i2c_lock_give();
|
||||
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "Temperature/pressure reading failed: %s - keeping last good readout",
|
||||
esp_err_to_name(err));
|
||||
return err;
|
||||
}
|
||||
|
||||
// Aktualizuj globalny stan TYLKO przy sukcesie.
|
||||
CURR_READOUT.temperature = temperature;
|
||||
CURR_READOUT.pressure = pressure;
|
||||
CURR_READOUT.humidity = humidity;
|
||||
@@ -32,4 +55,5 @@ void ygg_bmp280_read_sensor(bmp280_t *dev) {
|
||||
printf(", Humidity: %.2f\n", humidity);
|
||||
else
|
||||
printf("\n");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
#define YGG_BMP280_H
|
||||
|
||||
#include <bmp280.h>
|
||||
#include <esp_err.h>
|
||||
|
||||
#define I2C_MASTER_SDA 19
|
||||
#define I2C_MASTER_SCL 23
|
||||
#define YGG_I2C_FREQ_HZ 100000
|
||||
|
||||
void ygg_bmp280_init(bmp280_t *dev);
|
||||
void ygg_bmp280_read_sensor(bmp280_t *dev);
|
||||
esp_err_t ygg_bmp280_read_sensor(bmp280_t *dev);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
#include "ygg_lock.h"
|
||||
#include <freertos/semphr.h>
|
||||
|
||||
static SemaphoreHandle_t s_i2c_mutex = NULL;
|
||||
static SemaphoreHandle_t s_lvgl_mutex = NULL;
|
||||
|
||||
void ygg_lock_init(void) {
|
||||
if (s_i2c_mutex == NULL) {
|
||||
s_i2c_mutex = xSemaphoreCreateMutex();
|
||||
}
|
||||
if (s_lvgl_mutex == NULL) {
|
||||
s_lvgl_mutex = xSemaphoreCreateMutex();
|
||||
}
|
||||
}
|
||||
|
||||
bool ygg_i2c_lock_take(TickType_t timeout) {
|
||||
if (s_i2c_mutex == NULL) {
|
||||
return false;
|
||||
}
|
||||
return xSemaphoreTake(s_i2c_mutex, timeout) == pdTRUE;
|
||||
}
|
||||
|
||||
void ygg_i2c_lock_give(void) {
|
||||
if (s_i2c_mutex != NULL) {
|
||||
xSemaphoreGive(s_i2c_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
bool ygg_lvgl_lock_take(TickType_t timeout) {
|
||||
if (s_lvgl_mutex == NULL) {
|
||||
return false;
|
||||
}
|
||||
return xSemaphoreTake(s_lvgl_mutex, timeout) == pdTRUE;
|
||||
}
|
||||
|
||||
void ygg_lvgl_lock_give(void) {
|
||||
if (s_lvgl_mutex != NULL) {
|
||||
xSemaphoreGive(s_lvgl_mutex);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef YGG_LOCK_H
|
||||
#define YGG_LOCK_H
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
void ygg_lock_init(void);
|
||||
|
||||
bool ygg_i2c_lock_take(TickType_t timeout);
|
||||
void ygg_i2c_lock_give(void);
|
||||
|
||||
bool ygg_lvgl_lock_take(TickType_t timeout);
|
||||
void ygg_lvgl_lock_give(void);
|
||||
|
||||
#endif
|
||||
@@ -6,14 +6,19 @@
|
||||
#include "esp_lcd_panel_ops.h"
|
||||
#include "esp_timer.h"
|
||||
#include "esp_lcd_panel_sh1106.h"
|
||||
#include "esp_log.h"
|
||||
#include "ygg_bmp280.h"
|
||||
#include "ygg_lock.h"
|
||||
|
||||
#define I2C_HOST I2C_NUM_0
|
||||
#define EXAMPLE_I2C_MASTER_FREQ_HZ 400000
|
||||
#define EXAMPLE_I2C_MASTER_FREQ_HZ YGG_I2C_FREQ_HZ
|
||||
#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
|
||||
#define SH1106_I2C_SDA_GPIO I2C_MASTER_SDA
|
||||
#define SH1106_I2C_SCL_GPIO I2C_MASTER_SCL
|
||||
|
||||
static const char *TAG = "ygg_sh1106";
|
||||
|
||||
static uint8_t oled_buffer[EXAMPLE_LCD_H_RES * EXAMPLE_LCD_V_RES / 8];
|
||||
|
||||
@@ -51,8 +56,21 @@ static void example_lvgl_flush_cb(lv_display_t *disp, const lv_area_t *area, uin
|
||||
}
|
||||
}
|
||||
}
|
||||
// pass the draw buffer to the driver
|
||||
esp_lcd_panel_draw_bitmap(panel_handle, x1, y1, x2 + 1, y2 + 1, oled_buffer);
|
||||
// pass the draw buffer to the driver.
|
||||
// Cały transfer pod wspólnym mutexem I2C - BMP280 (i2cdev) używa
|
||||
// tego samego busa. Bez tego odczyt sensora potrafi wejść w środek
|
||||
// rysowania -> I2C hardware timeout po obu stronach.
|
||||
// UWAGA: nie bierzemy tu mutexa LVGL - flush jest wołany z lvgl_task
|
||||
// już pod mutexem LVGL (z lv_timer_handler). Branie go tu = deadlock.
|
||||
if (ygg_i2c_lock_take(pdMS_TO_TICKS(1000))) {
|
||||
esp_err_t err = esp_lcd_panel_draw_bitmap(panel_handle, x1, y1, x2 + 1, y2 + 1, oled_buffer);
|
||||
ygg_i2c_lock_give();
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "draw_bitmap failed: %s", esp_err_to_name(err));
|
||||
}
|
||||
} else {
|
||||
ESP_LOGW(TAG, "I2C busy, skipping flush");
|
||||
}
|
||||
lv_display_flush_ready(disp);
|
||||
}
|
||||
|
||||
@@ -71,7 +89,8 @@ void ygg_init_tick_cb() {
|
||||
}
|
||||
|
||||
lv_display_t* ygg_sh1106_init() {
|
||||
ESP_ERROR_CHECK(i2cdev_init());
|
||||
// UWAGA: i2cdev_init() + ygg_lock_init() robi app_main() raz przed
|
||||
// wszystkimi initami. Tutaj już nie tworzymy magistrali.
|
||||
|
||||
static i2c_dev_t oled_i2c_dev;
|
||||
memset(&oled_i2c_dev, 0, sizeof(oled_i2c_dev));
|
||||
@@ -102,6 +121,9 @@ lv_display_t* ygg_sh1106_init() {
|
||||
// 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;
|
||||
// Zejście z domyślnych 400kHz na 100kHz - wspólna magistrala z BMP280,
|
||||
// pająk + słabe pull-upy. 400kHz/1MHz = timeouty.
|
||||
io_config.scl_speed_hz = EXAMPLE_I2C_MASTER_FREQ_HZ;
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c(i2c_bus_handle, &io_config, &io_handle));
|
||||
|
||||
|
||||
@@ -149,9 +171,16 @@ lv_display_t* ygg_sh1106_init() {
|
||||
}
|
||||
|
||||
void ygg_set_display_label(lv_display_t *display, lv_obj_t *label, const char *str) {
|
||||
// LVGL nie jest thread-safe. Ta funkcja jest wołana z sensor_task,
|
||||
// a lv_timer_handler chodzi w lvgl_task - wszystko pod jednym mutexem.
|
||||
if (!ygg_lvgl_lock_take(pdMS_TO_TICKS(500))) {
|
||||
ESP_LOGW(TAG, "LVGL busy, skipping label update");
|
||||
return;
|
||||
}
|
||||
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);
|
||||
ygg_lvgl_lock_give();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user