Hello Karosm,
Thank you for taking the time to reply.
As I wrote, I know the code was wrong, but it’s the only way I found to change my screen’s brightness.
I searched (probably not very well) in the ESPhome documentation (SSD1306 OLED Display - ESPHome - Smart Home Made Simple) and on forums, but couldn’t find anything that helped.
AI is full of flaws, but I use it as a translator between my ideas and the electronics. Perhaps you never use Google Translate to write in a foreign language?
Thank you for the code. Unfortunately, it doesn’t work : “Unable to find action with the name ‘display.turn_on’”.
I wonder if the reason isn’t that the screen (bought on AliExpress) is too low-end.
I finally modified the code to have On-Off instead of two brightness levels.
Sincerely,
Fabien
esphome:
name: esp-c3-co2
friendly_name: ESP-C3 CO2
esp32:
board: esp32-c3-devkitm-1
framework:
type: esp-idf
logger:
level: ERROR
i2c:
sda: GPIO8
scl: GPIO9
frequency: 100kHz
sensor:
- platform: scd4x
co2:
id: co2_sensor
update_interval: 60s
globals:
- id: co2_history
type: float[120]
restore_value: false
- id: co2_index
type: int
initial_value: '0'
- id: screen_on
type: bool
initial_value: 'true'
- id: contrast_level
type: int
initial_value: '2'
- id: shift_step
type: int
initial_value: '0'
- id: shift_x
type: int
initial_value: '0'
- id: shift_y
type: int
initial_value: '0'
- id: light_mode
type: int
initial_value: '1'
interval:
- interval: 6min
then:
- lambda: |-
float v = id(co2_sensor).state;
if (isnan(v)) return;
id(co2_history)[id(co2_index)] = v;
id(co2_index) = (id(co2_index) + 1) % 120;
- interval: 1min # Plus rapide pour une meilleure répartition
then:
- lambda: |-
// On passe sur 8 positions pour décrire un carré de 2x2
id(shift_step) = (id(shift_step) + 1) % 8;
switch (id(shift_step)) {
case 0: id(shift_x) = 0; id(shift_y) = 0; break;
case 1: id(shift_x) = 2; id(shift_y) = 0; break;
case 2: id(shift_x) = 2; id(shift_y) = -2; break;
case 3: id(shift_x) = 0; id(shift_y) = -2; break;
case 4: id(shift_x) = 1; id(shift_y) = -1; break;
case 5: id(shift_x) = -1; id(shift_y) = 1; break;
case 6: id(shift_x) = -2; id(shift_y) = 2; break;
case 7: id(shift_x) = 0; id(shift_y) = 1; break;
}
binary_sensor:
- platform: gpio
id: button_touch
pin:
number: GPIO4
mode: INPUT_PULLDOWN
filters:
- delayed_on: 30ms
on_press:
then:
- lambda: |-
id(screen_on) = !id(screen_on);
if (id(screen_on)) {
id(oled).turn_on();
} else {
id(oled).turn_off();
}
- component.update: oled
display:
- platform: ssd1306_i2c
id: oled
model: "SH1106 128x64"
address: 0x3C
update_interval: 50s
lambda: |-
if (!id(screen_on)) return;
// DÉCLARATION UNIQUE
int ox = id(shift_x);
int oy = id(shift_y);
float co2 = id(co2_sensor).state;
it.fill(COLOR_OFF);
// Si la valeur CO2 n'est pas encore disponible
if (isnan(co2)) {
it.printf(64 + ox, 32 + oy, id(font_small), TextAlign::CENTER, "Capteur en chauffe...");
return; // On arrête là, on ne dessine pas le reste (smiley/graphe)
}
//----DESSIN DU CO2----
if (!isnan(co2)) {
int co2_x = (co2 >= 1000) ? 10 : 20;
it.printf(co2_x + ox, 0 + oy, id(font_big), TextAlign::TOP_LEFT, "%.0f", co2);
it.printf(65 + ox, 10 + oy, id(font_small), TextAlign::TOP_LEFT, "ppm");
// Smiley
int sx = 115 + ox;
int sy = 12 + oy;
it.circle(sx, sy, 9);
it.draw_pixel_at(sx - 3, sy - 2);
it.draw_pixel_at(sx + 3, sy - 2);
if (co2 < 910) {
// --- SOURIRE : Les coins (sy+2) sont plus HAUTS que le centre (sy+4) ---
it.line(sx - 3, sy + 4, sx + 3, sy + 4); // Centre
it.line(sx - 5, sy + 2, sx - 3, sy + 4); // Coin gauche monte
it.line(sx + 3, sy + 4, sx + 5, sy + 2); // Coin droit monte
} else if (co2 < 1200) {
// --- NEUTRE : Une ligne bien droite ---
it.line(sx - 4, sy + 4, sx + 4, sy + 4);
} else {
// --- MÉCONTENT : Les coins (sy+6) sont plus BAS que le centre (sy+4) ---
it.line(sx - 3, sy + 4, sx + 3, sy + 4); // Centre
it.line(sx - 5, sy + 6, sx - 3, sy + 4); // Coin gauche descend
it.line(sx + 3, sy + 4, sx + 5, sy + 6); // Coin droit descend
}
// GRAPHE (Remonté de 2 pixels : 30 au lieu de 32, 61 au lieu de 63)
const int g_top = 30 + oy;
const int g_bottom = 61 + oy;
const int g_left = 0 + ox;
const int g_width = 127;
auto map_co2 = [&](float v) {
if (v < 400) v = 400;
if (v > 2000) v = 2000;
return g_bottom - (int)((v - 400) * (g_bottom - g_top) / (2000 - 400));
};
// 1. DESSIN DES LIGNES DE RÉFÉRENCE (Pointillés)
int y_1000 = map_co2(1000);
int y_2000 = map_co2(2000);
for (int x = 0; x <= 127; x += 6) {
it.draw_pixel_at(x + ox, y_1000);
it.draw_pixel_at(x + ox, y_2000);
}
// 2. TRACÉ DE L'HISTORIQUE
int idx0 = id(co2_index);
float first_val = id(co2_history)[idx0];
int prev_y = map_co2(isnan(first_val) ? 400 : first_val);
for (int i = 1; i < 120; i++) {
int idx = (idx0 + i) % 120;
float val = id(co2_history)[idx];
if (isnan(val)) continue;
int x_prev = (i - 1) * 127 / 119;
int x_curr = i * 127 / 119;
int y = map_co2(val);
it.line(x_prev + ox, prev_y, x_curr + ox, y);
prev_y = y;
}
// 3. AXE X ET REPERES
it.line(0 + ox, g_bottom, 127 + ox, g_bottom); // Axe horizontal
it.line(0 + ox, g_top, 3 + ox, g_top); // Cran 2000
it.line(0 + ox, y_1000, 3 + ox, y_1000); // Cran 1000
// 4. MARQUAGE DES HEURES (Sous l'axe X)
// On place un point tous les 10 échantillons (10 * 6 min = 1 heure)
// i=0 est le point le plus vieux, i=119 le plus récent
for (int i = 9; i < 120; i += 10) {
int x_hour = i * 127 / 119;
it.draw_pixel_at(x_hour + ox, g_bottom + 2);
}
}
font:
- file: "fonts/Roboto-Bold.ttf"
id: font_big
size: 24
- file: "fonts/Roboto-Regular.ttf"
id: font_small
size: 10