У ИКЕА достаточно давно существует «сенсор качества воздуха» с труднопроизносимым названием ВИНДРИКТНИНГ. Он построен на основе сенсора твердых частиц 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 ?