У ИКЕА достаточно давно существует «сенсор качества воздуха» с труднопроизносимым названием ВИНДРИКТНИНГ. Он построен на основе сенсора твердых частиц pm1006, что это за частицы и зачем нужно знать их концентрацию можно почитать здесь. Изначально устройство не умеет точно показывать измеренные значения, ограничиваясь цветовой индикацией уровня загрязнения воздуха, так же отсутствуют какие-либо интерфейсы для получения точных данных. Используя готовые и доступные модули можно добавить расширенный функционал, не имея особых навыков в пайке или заказе печатных плат.
Яндекс маркет дает возможность приобрести устройство за очень небольшие деньгиЧто бы сделать устройство способным отображать измеренные значения и передавать их в УД понадобится докупить модуль с чипом ESP32 и LCD дисплей. В моем случае это ESP32 Lolin Lite и LCD c контроллером st7735 (доступны и на отечественных торговых площадках)
На LCD модуле необходимо удалить слот для карты памяти и контактный разъем.
Электрическая часть собирается на проводах, без использования дискретных элементов. Общая схема соединений выглядит так
Практически получается такая конструкция
На оригинальной плате все элементы сохраняются без изменений. Так как на моем варианте модуля с ESP32 нет выведенного контакта для питания платы от 5 вольт, можно подпаяться к точке на плате, как на картинке выше.
Для монтажа дисплея в корпус на лицевой панели необходимо сделать вырез любым доступным способом, дремель, лазер и т.д.
Лицевая панель вырезана лазером из акрила ТОСП 79851 толщиной 2мм. и закреплена с помощью тонкого двухстороннего скотча 3М. Модуль с ESP32 плотно встает в корпус по диагонали
Для плотного прилегания модифицированной лицевой панели необходимо удалить часть выступов
Сам сенсор pm1006 может отдавать не только значения РМ2.5 используемые в оригинальном устройстве, но и значения РМ1 и РM10. Прошивка собрана в ESPHome, код из примеров и дополнена стараниями @Constantine
esphome: name: vindriktning friendly_name: VINDRIKTNING esp32: board: esp32dev framework: type: arduino # Enable logging logger: # Enable Home Assistant API api: encryption: key: "" ota: password: "" wifi: ssid: !secret wifi_sside password: !secret wifi_password # Enable fallback hotspot (captive portal) in case wifi connection fails ap: ssid: "${devicename} Fallback Hotspot" password: !secret ap_password captive_portal: ########## Setup ####################### spi: clk_pin: GPIO18 mosi_pin: GPIO23 uart: rx_pin: GPIO16 baud_rate: 9600 debug: direction: RX dummy_receiver: true after: delimiter: "\r\n" sequence: - lambda: |- UARTDebug::log_string(direction, bytes); //Still log the data uint8_t checksum = 0; for (int i=0; i < bytes.size(); i++) { checksum+= bytes[i]; } if (checksum==0){ ESP_LOGD("UART","PM1006|PM1006k checksum validated, have %d", checksum); if (bytes.size()==20 and (bytes[0]==22 and bytes[1]==17 and bytes[2]==11)) { ESP_LOGD("UART", "Correct PM1006 response recieved. Updating sensors"); id(pm2).publish_state(bytes[5]*256+bytes[6]); id(pm1).publish_state(bytes[9]*256+bytes[10]); id(pm10).publish_state(bytes[13]*256+bytes[14]); } if (bytes.size()==16 and (bytes[0]==22 and bytes[1]==13 and bytes[2]==2)) { ESP_LOGD("UART", "Correct PM1006K response recieved. Updating sensors"); id(pm2).publish_state(bytes[5]*256+bytes[6]); id(pm1).publish_state(bytes[9]*256+bytes[10]); id(pm10).publish_state(bytes[13]*256+bytes[14]); } } else{ ESP_LOGW("UART","PM1006|PM1006k checksum is wrong: %02x, expected zero. Sensors will not be updated", checksum); } font: - file: "Roboto-Thin.ttf" id: font0 size: 15 glyphs: |- !"%()+=,-_.:°0123456789АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧЩЬЫЪЭЮЯABCDEFGHIJKLMNOPQRSTUVWXYZ абвгдеёжзийклмнопрстуфхцчщьыъэюяabcdefghijklmnopqrstuvwxyz'éèàòùç/&ôœìïöñ # gfonts://family[@weight] - file: "gfonts://Roboto" id: font3 size: 15 - file: type: gfonts family: Roboto weight: 700 id: font2 size: 30 display: - platform: st7735 model: "INITR_18BLACKTAB" reset_pin: GPIO14 cs_pin: GPIO17 dc_pin: GPIO2 rotation: 0 device_width: 128 device_height: 160 col_start: 0 row_start: 0 eight_bit_color: true update_interval: 5s id: my_display pages: - id: page1 lambda: |- it.strftime(it.get_width() / 2, 8, id(font3), my_white, TextAlign::CENTER, "%H:%M", id(ntp).now()); it.print(64, 22, id(font0), my_white, TextAlign::CENTER, "PM1 мкг/m3"); it.printf(64, 44, id(font2), my_white, TextAlign::CENTER, "%.0f", id(pm1).state); it.print(64, 70, id(font0), my_white, TextAlign::CENTER, "PM2.5 мкг/m3"); it.printf(64, 92, id(font2), my_yellow, TextAlign::CENTER, "%.0f ", id(pm2).state); it.print(64, 123, id(font0), my_white, TextAlign::CENTER, "PM10 мкг/m3"); it.printf(64, 145, id(font2), my_white, TextAlign::CENTER, "%.0f", id(pm10).state); - id: page2 lambda: |- it.strftime(it.get_width() / 2, 8, id(font3), my_white, TextAlign::CENTER, "%H:%M", id(ntp).now()); it.print(64, 22, id(font0), my_white, TextAlign::CENTER, "НА УЛИЦЕ"); it.printf(64, 44, id(font2), my_white, TextAlign::CENTER, "%.1f", id(temp_outdoor).state); it.print(64, 70, id(font0), my_white, TextAlign::CENTER, "В ДОМЕ"); it.printf(64, 92, id(font2), my_yellow, TextAlign::CENTER, "%.1f ", id(temp_hall).state); it.print(64, 123, id(font0), my_white, TextAlign::CENTER, "СО2"); it.printf(64, 145, id(font2), my_white, TextAlign::CENTER, "%.1f", id(co2).state); interval: - interval: 5s then: - display.page.show_next: my_display - component.update: my_display color: - id: my_red red: 100% green: 0% blue: 0% - id: my_yellow red: 100% green: 100% blue: 0% - id: my_green red: 0% green: 100% blue: 0% - id: my_blue red: 0% green: 0% blue: 100% - id: my_white red: 100% green: 100% blue: 100% - id: my_black red: 0% green: 0% blue: 0% sensor: - platform: template name: "PM 1.0" id: "pm1" device_class: PM1 accuracy_decimals: 0 unit_of_measurement: µg/m³ - platform: template name: "PM 2.5" id: "pm2" device_class: PM25 accuracy_decimals: 0 unit_of_measurement: µg/m³ - platform: template name: "PM 10 " id: "pm10" device_class: PM10 accuracy_decimals: 0 unit_of_measurement: µg/m³ - platform: homeassistant # Температура на улице id: temp_outdoor entity_id: sensor.93_01_temp - platform: homeassistant # Температура в доме id: temp_hall entity_id: sensor.0xa4c1387ab81dc9bd_temperature - platform: homeassistant # CO2 id: co2 entity_id: sensor.0x00124b001d3b8107_co2 switch: - platform: gpio pin: GPIO19 name: Light id: light time: - platform: homeassistant id: ntp
В Home Assistant устройство видится так
Подсветку дисплея можно включать/выключать по событиям, движение в помещении, превышение порога и т.д. Так как дисплей достаточно крупный, то на него можно выводить дополнительную информацию от сенсоров Home Assistant.
Таким образом, буквально за вечер устройство обретает вторую жизнь
Для желающих вырезать подобное файл в формате .cdr
С возвращением !
камбек детектед! Джаггер, приятно видеть, не бросайте проект, пишите еще, с удовольствием читаем Вас и паяем Ваше)
Спасибо! Отличная статья и реализация, обязательно повторим!)
Подскажите, а какие нормы на PM1.0?
Можно почитать здесь. В случае с VINDRIKTNING есть подозрение в ошибке в формуле расчета.
Где можно скачать конфиг со шрифтами?
А как там с уровнями сигналов ? ESP32 — 3.3V а этот датчик 5V TTL ?
Не хочется резать оригинальную лицевую часть корпуса. Может быть, у кого-то есть 3D-модель?
глянь здесь, может подойдет? https://www.thingiverse.com/thing:6362270
Спасибо, мил человек! Это то, что надо!
Скажите, а SMD светодиоды надо выпаивать? Не мешают они экрану и не подсвечивают ли через плату?
В статье явно написано «На оригинальной плате все элементы сохраняются без изменений». Ничего не просвечивает.
А вот еще идея возникла: отображать значения PM разным цветом в зависимости от концентрации частиц. Если норма, то зеленым, повышенная концентрация желтым, превышение ПДК — красным.
Только вот моих знаний в ESPHome не хватает, чтобы это реализовать.
Раскрасил https://github.com/ananyevgv/esphome-vindriktning-ikea/
Класс! Теперь только не хватает одной мелочи — чтобы при прикосновении к экрану он включался. А потом выключался по тайм-ауту (скажем, минута или три).
А я например так и сделел — просверлил отверстие и вставил винтик нержавеющий, и далее проводом к ножке есп:
esp32_touch:
setup_mode: False
binary_sensor:
— platform: esp32_touch
name: «Touch Pad»
id: «touch_pad»
pin: GPIO12
threshold: 20000
on_press:
then:
— display.page.show_next: my_display
— component.update: my_display
Можно использовать сенсорную кнопку на чипе TTP223. Она чувствует даже через небольшую преграду (2-3 мм пластика, что как раз соответствует толщине стенки корпуса нашего девайса). То есть, ее можно приклеить изнутри корпуса — в верхней части, например. И тогда будет реакция на прикосновение пальца к корпусу.
К вышесказанному (у вас могут быть другие пины):
binary_sensor:
— platform: gpio
pin: 4
name: «Touch Button»
id: touch_button
device_class: light
on_press:
then:
— light.turn_on: back_light
output:
— platform: ledc
pin: 19
id: gpio_19_backlight_pwm
light:
— platform: monochromatic
output: gpio_19_backlight_pwm
name: «Display Backlight»
id: back_light
Ещё б в продаже найти этот датчик…
Новый вариант оформления https://github.com/ananyevgv/esphome-vindriktning-ikea/blob/main/ikea-circle-new.yaml
Хотел воспользоваться Вашей доработкой, но к сожалению , при валидации проекта прошивки получил вот это :
Could not download font at https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:ital,wght@0,400, please check the fonts exists at google fonts (HTTPSConnectionPool(host=’fonts.googleapis.com’, port=443): Max retries exceeded with url: /css2?family=Material+Symbols+Outlined:ital,wght@0,400 (Caused by SSLError(SSLCertVerificationError(1, ‘[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: certificate is not yet valid (_ssl.c:992)’))))
Как бы это поправить ?
не достучался до гугла