Tym razem wpadł na kanał film z projektem czujnika parkowania.
Urządzenie działa w oparciu o kontroler ESP8266/Wemos d1 do którego podłączony jest czujnik ultradźwiękowy HC-SR04 oraz pasek LED adresowalny WS2811
Całość działa w oparciu oprogramowanie ESPHome.
Skrypt pobiera status bramy z Home Assistanta i na tej podstawie załącza pasek LED
Na poniższym obrazku widzicie schemat podłączenia wszystkich elementów, ale jak wspominam na filmie przekaźnik jest shildem dla wemosa więc jedno wpina sie w drugie.

Dodatkowo poniżej macie cały kod do ESPHome użyty w projekcie:
Aby określić punkt 0 czyli realna odległość auta od czujnika kiedy ten ma pokazywać moment zatrzymania należy edytować w tym miejscu:
- id: zero_point
type: float
initial_value: "0.40" # Punkt 0 w metrach, np. 2m
Pełny kod:
#################### ESP8266 - CZUJNIK PARKOWANIA ######################
#################### KONFIGURACJA ######################
esphome:
name: ws2811_garage_led
friendly_name: Czujnik parkowania
comment: ESP8266
esp8266:
board: d1_mini
time:
- platform: homeassistant
id: homeassistant_time
#################### standardowe wifi
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
reboot_timeout: 5min
#################### Serwer WWW
web_server:
port: 80
#################### Logi
debug:
logger:
#################### OTA/API
ota:
- platform: esphome
password: !secret ota
api:
encryption:
key: !secret api
services:
- service: update_leds
variables:
distance: float
garage_status: string
then:
- lambda: |-
ESP_LOGD("led_effect", "Aktualna odległość: %f", id(distance_sensor_value));
id(garage_status_value) = garage_status;
#################### ELEMENTY ZEWNETRZNE ######################
#################### Sterowanie LED
light:
- platform: neopixelbus
id: ws2811_strip
pin: GPIO3
num_leds: 50
type: GRB
variant: ws2811
default_transition_length: 0.5s
name: "WS2811 LED Strip"
effects:
- addressable_lambda:
name: "Reakcja na bramę (otwieranie i zamykanie)"
update_interval: 500ms
lambda: |-
std::string garage_status = id(garage_status_value);
// Sprawdzenie statusu bramy (otwieranie/zamykanie)
if (garage_status == "otwieranie" || garage_status == "zamykanie") {
// Skrajne LED-y na niebiesko
it[0] = Color(0, 0, 255);
it[1] = Color(0, 0, 255);
it[it.size() - 2] = Color(0, 0, 255);
it[it.size() - 1] = Color(0, 0, 255);
// Mruganie skrajnych LED-ów
for (int i = 0; i < it.size(); i++) {
if (i == 0 || i == 1 || i == it.size() - 2 || i == it.size() - 1) {
if (millis() % 1000 < 500) {
it[i] = Color(0, 0, 255); // Niebieskie
} else {
it[i] = Color::BLACK;
}
} else {
// Normalny efekt oparty na odległości
float distance = id(distance_sensor_value);
float distance_threshold = id(distance_threshold_value);
// Jeśli odległość jest nieznana, ustaw LED-y na zielono
if (std::isnan(distance)) {
it[i] = Color(0, 255, 0); // Zielony pasek LED
} else {
// Nowy wzór na liczbę czerwonych LED-ów, który wyklucza skrajne LED-y
int leds_to_red = (int)((id(zero_point) + id(distance_threshold_value) - distance) / (id(distance_threshold_value)) * (it.size() - 4));
leds_to_red = leds_to_red > (it.size() - 4) ? (it.size() - 4) : leds_to_red;
// Dostosowanie zakresu czerwonych diod, aby skrajne LED-y nadal działały jak wcześniej
if (i < leds_to_red / 2 + 2 || i >= it.size() - leds_to_red / 2 - 2) {
it[i] = Color(255, 0, 0); // Czerwone diody przy krawędziach
} else {
it[i] = Color(0, 255, 0); // Zielone diody w środku
}
}
}
}
return;
}
# // Jeśli brama nie jest w stanie otwierania, zamykania lub otwarta, wyłącz LED-y
# for (int i = 0; i < it.size(); i++) {
# it[i] = Color::BLACK; // Wyłącz LED-y
# }
- addressable_lambda:
name: "Reakcja na bramę i odległość"
update_interval: 200ms
lambda: |-
static bool blink_state = false;
static unsigned long last_blink_time = 0;
const unsigned long blink_interval = 100; // Czas migania (w ms)
float distance = id(distance_sensor_value);
std::string garage_status = id(garage_status_value);
// Zmienna określająca próg odległości, od której zaczynają się zmiany
float distance_threshold = id(distance_threshold_value);
// Aktualizacja stanu migania
unsigned long current_time = millis();
if (current_time - last_blink_time >= blink_interval) {
blink_state = !blink_state;
last_blink_time = current_time;
}
// Normalny efekt oparty na odległości
if (std::isnan(distance)) {
// Jeśli wartość odległości jest nieznana, ustaw LED-y na zielono
for (int i = 0; i < it.size(); i++) {
it[i] = Color(0, 255, 0); // Zielony pasek LED
}
} else if (distance <= (id(zero_point) - 0.05)) {
// Jeśli odległość jest mniejsza lub równa punktowi zero minus 5 cm, zapalić czerwony pasek
for (int i = 0; i < it.size(); i++) {
it[i] = blink_state ? Color(255, 0, 0) : Color(0, 0, 0); // Mruganie na czerwono
}
} else if (distance <= id(zero_point) + id(distance_threshold_value)) {
// Jeśli odległość jest mniejsza niż threshold (odległość od zero_point + distance_threshold_value)
int leds_to_red = (int)((id(zero_point) + id(distance_threshold_value) - distance) / (id(distance_threshold_value)) * 50);
leds_to_red = leds_to_red > 50 ? 50 : leds_to_red;
for (int i = 0; i < it.size(); i++) {
if (i < leds_to_red / 2 || i >= it.size() - leds_to_red / 2) {
it[i] = Color(255, 0, 0); // Czerwone diody przy krawędziach
} else {
it[i] = Color(0, 255, 0); // Zielone diody w środku
}
}
} else {
// Jeśli odległość jest większa niż threshold, wszystkie diody na zielono
for (int i = 0; i < it.size(); i++) {
it[i] = Color(0, 255, 0); // Wszystkie diody zielone
}
}
#################### Funkcje globalne
globals:
- id: distance_sensor_value
type: float
initial_value: "1.25" # Początkowa odległość w metrach
- id: garage_status_value
type: std::string
initial_value: '"zamknięta"'
- id: led_active
type: bool
initial_value: "false"
- id: garage_status_open_time
type: long
initial_value: "0"
- id: garage_status_change_time
type: long
initial_value: "0"
- id: zero_point
type: float
initial_value: "0.40" # Punkt 0 w metrach, np. 2m
- id: distance_threshold_value
type: float
initial_value: "1.6" # Próg, przy którym LED-y zaczynają zmieniać kolor
- id: elapsed_time_open
type: float
# initial_value: "1.6" # Próg, przy którym LED-y zaczynają zmieniać kolor
- id: garage_status_is_open
type: bool
initial_value: "false" # Początkowy stan: brama nieotwarta
- id: transistor_active
type: bool
restore_value: no
initial_value: "false"
#################### Obsługa zdrazeń z HA
text_sensor:
- platform: homeassistant
id: garage_status_sensor
entity_id: sensor.3_gar_stan_bramy
on_value:
then:
- lambda: |-
std::string new_status = x.c_str();
id(garage_status_value) = new_status;
ESP_LOGD("garage_status", "Nowy status bramy: %s", new_status.c_str());
if (new_status == "otwieranie") {
id(led_active) = true;
id(garage_status_change_time) = millis();
if (!id(transistor_active)) {
id(transistor_pin).turn_on();
id(transistor_active) = true;
delay(150);
}
auto call = id(ws2811_strip).make_call();
call.set_state(true);
call.set_brightness(1.0);
call.set_effect("Reakcja na bramę (otwieranie i zamykanie)");
call.perform();
}
if (new_status == "otwarta") {
id(garage_status_open_time) = millis();
ESP_LOGD("garage_status", "Brama otwarta, czas: %lu", id(garage_status_open_time));
if (!id(transistor_active)) {
id(transistor_pin).turn_on();
id(transistor_active) = true;
delay(150);
}
auto call = id(ws2811_strip).make_call();
call.set_state(true);
call.set_brightness(1.0);
call.set_effect("Reakcja na bramę i odległość");
call.perform();
id(garage_status_is_open) = true;
}
if (new_status == "zamykanie") {
id(led_active) = true;
id(garage_status_change_time) = millis();
if (!id(transistor_active)) {
id(transistor_pin).turn_on();
id(transistor_active) = true;
delay(150);
}
auto call = id(ws2811_strip).make_call();
call.set_state(true);
call.set_brightness(1.0);
call.set_effect("Reakcja na bramę (otwieranie i zamykanie)");
call.perform();
}
- if:
condition:
lambda: 'return id(garage_status_value) == "zamknięta";'
then:
- script.execute: handle_gate_closed
#################### Obsługa wyłączeń timerów
interval:
- interval: 1s
then:
- lambda: |-
if (id(garage_status_is_open)) {
unsigned long elapsed_time_open = millis() - id(garage_status_open_time);
ESP_LOGD("garage_status", "Czas otwarcia bramy: %lu ms", elapsed_time_open);
if (elapsed_time_open >= 300000) { // Po 5 minutach
ESP_LOGD("garage_status", "Brama otwarta za długo, wyłączam LED-y.");
auto call = id(ws2811_strip).make_call();
call.set_state(false); // Wyłącz LED-y
call.perform();
id(garage_status_is_open) = false; // Zresetuj flagę otwarcia
// Wyłączenie czujnika odległości, gdy brama jest otwarta za długo
if (id(transistor_active)) { // Sprawdzamy, czy czujnik był aktywowany
ESP_LOGD("garage_status", "Czujnik odległości wyłączony.");
id(transistor_pin).turn_off();
id(transistor_active) = false; // Użycie id() zamiast przypisania bezpośredniego
}
}
}
// Monitorowanie stanu otwierania/zamykania bramy
if ((id(garage_status_value) == "otwieranie" || id(garage_status_value) == "zamykanie") && id(led_active)) {
unsigned long elapsed_time_changing = millis() - id(garage_status_change_time);
ESP_LOGD("garage_status", "Czas otwierania/zamykania: %lu ms", elapsed_time_changing);
if (elapsed_time_changing >= 180000) { // Po 3 minutach (lub innym ustawionym czasie)
ESP_LOGD("garage_status", "Brama otwiera się/zamyka za długo, wyłączam LED-y.");
auto call = id(ws2811_strip).make_call();
call.set_state(false); // Wyłącz LED-y
call.perform();
id(garage_status_is_open) = false; // Zresetuj flagę otwarcia
id(led_active) = false; // Wyłącz aktywność LED-ów
// Wyłączenie czujnika odległości, gdy brama jest otwarta za długo
if (id(transistor_active)) { // Sprawdzamy, czy czujnik był aktywowany
ESP_LOGD("garage_status", "Czujnik odległości wyłączony.");
id(transistor_pin).turn_off();
id(transistor_active) = false; // Użycie id() zamiast przypisania bezpośredniego
}
}
}
#################### Czujnik ultradzwiękowy
sensor:
- platform: ultrasonic
id: distance_sensor
trigger_pin: D2
echo_pin: D3
timeout: 4m
update_interval: 250ms
name: "Czujnik odległości"
filters:
# Lambda filtrująca błędne odczyty (NaN i skrajne różnice)
- lambda: |-
static float last_valid_value = 0.0; // Przechowuje ostatnią poprawną wartość
static float buffer[5] = {0}; // Bufor do przechowywania ostatnich odczytów
static int index = 0; // Indeks bufora
if (isnan(x)) { // Sprawdzanie NaN
return last_valid_value; // Zwraca ostatnią poprawną wartość
}
// Aktualizacja bufora
buffer[index] = x;
index = (index + 1) % 5; // Cykl bufora
// Obliczanie średniej z bufora
float sum = 0.0;
for (int i = 0; i < 5; i++) {
sum += buffer[i];
}
float average = sum / 5;
// Odrzucanie wartości mocno odbiegających od średniej
if (abs(x - average) > 0.3) { // Próg różnicy 0.3 m (30 cm)
return last_valid_value;
}
last_valid_value = x; // Aktualizacja poprawnej wartości
return x;
# Wygładzanie odczytów za pomocą filtru wykładniczego
- exponential_moving_average:
alpha: 0.3
send_every: 1
# Działania do wykonania przy zmianie wartości
on_value:
then:
- lambda: |-
id(distance_sensor_value) = x; // Aktualizacja wartości odległości
script:
- id: handle_gate_closed
mode: restart
then:
- logger.log: "Brama zamknięta, czekam 10 sekund..."
- delay: 10s
- logger.log: "Wyłączam LED-y"
- light.turn_off:
id: ws2811_strip
transition_length: 0.5s
- delay: 1s
- if:
condition:
lambda: 'return id(transistor_active);'
then:
- logger.log: "Wyłączam czujnik odległości"
- switch.turn_off: transistor_pin
- lambda: 'id(transistor_active) = false;'
switch:
- platform: gpio
pin: D1
name: "Przekaźnik"
id: transistor_pin
Oczywiście nie jestem ekspertem w kodowaniu. Jak znajdziesz jakiś błąd lub wiesz jak lepiej zoptymalizować działanie, podziel się w komentarzu lub pisząc na kontakt@bartekbuduje.pl
