Monthly Archives: January 2016

Монитор качества воздуха своими руками: Часть 1 – Начало

Недавно в подарок получил ионизатор и увлажнитель воздуха, который очищает воздух, увлажняет его и ионизирует. И хотя лично я разницы не чувствую, а, учитывая зиму и работающие батареи, эффект очень кратковременный, я задумался об оценке не только качества работы этого устройства, но и такого привычного для всех действия как уборки квартиры. Позднее мысль пошла дальше и я решил, что неплохо бы иметь переносной монитор качества воздуха.

Для начала я купил датчик пыли на основе sharp GP2Y1010AU0F прменяется для обнаружения PM2.5 частиц (менее 2.5 микрон) и сигаретного дыма. Принцип действия довольно простой: фототранзистор распознает свет, отражённый от частиц, который посылает инфракрасный светодиод.

20160130_130529Для подключения к Arduino не требуется ничего особенного: чёрный провод земля, красный 5 вольт, жёлтый на цифровой Pin 7, для управления (вкл/выкл) тем самым инфракрасным светодиодом, и синий на один из аналоговых пинов, например A0, для чтения показаний датчика.

Для того, чтобы считать данные с датчика, согласно DataSheet-у нужно включить инфракрасный светодиод, подождать 280 микросекунд, и затем померить выходное напряжение датчика.

#define DUST_ILED 7
#define DUST_PIN 0
#define NO_DUST_VOLT 0.9

void setup() {
  pinMode(DUST_ILED, OUTPUT);
  digitalWrite(DUST_ILED, LOW);
 
}

void loop() {
  // put your main code here, to run repeatedly:
  digitalWrite(DUST_ILED, HIGH);
  delayMicroseconds(280);

  int outVolt = analogRead(DUST_PIN);

  digitalWrite(DUST_ILED, LOW);

  float calcVoltage = outVolt * 5.0 / 1024.0 - NO_DUST_VOLT;
  if (calcVoltage < 0) {
    calcVoltage = 0;
  }

  delay(1000);
}

Здесь мы делим значение, которое считали с датчика на 1024 – это максимальное значение, которое может считать Arduino Uno на аналоговых пинах и умножаем на 5 – напряжение, которое мы подали на датчик.

Согласно DataSheet-у типичное напряжение, которое показывает датчик при отсутствии пыли – это 0.9 вольт, так что просто вычтем это значение и будем считать, что наличие пыли начинается при напряжении > 0.9 вольт.

Второе, что нужно сделать – это отображать считанные значения. Простейший вариант – это напечатать в консоль, но когда ходишь по городу, то лучше иметь какой-нить маленький дисплей и я купил  0.96″ OLED дисплей с интерфейсом i2c, которому требуется 2 аналоговых пина (SDA на A4, а SCL на A5) плюс напряжение и земля.

20160130_174819У AdaFruit есть отличная, но “тяжёлая” библиотека для работы с подобными дисплеями. Прежде чем ею можно воспользоваться нужно поменять пару вещей в зависимости от модели дисплея. В моём случае это i2c адрес, который нужно выставить в 0x3C . Так же может понадобиться другая AdaFruit библиотека – AdaFruit GFX.

#include <Adafruit_SSD1306.h>

#define DUST_ILED 7
#define DUST_PIN 0
#define NO_DUST_VOLT 0.9

#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);

void setup() {
  pinMode(DUST_ILED, OUTPUT);
  digitalWrite(DUST_ILED, LOW);
  // put your setup code here, to run once:
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);

  display.display();
  delay(2000);

  // Clear the buffer.
  display.clearDisplay();
  display.display();
  

  
}

void loop() {
  // put your main code here, to run repeatedly:
  digitalWrite(DUST_ILED, HIGH);
  delayMicroseconds(280);

  int outVolt = analogRead(DUST_PIN);

  digitalWrite(DUST_ILED, LOW);

  float calcVoltage = outVolt * 5.0 / 1024.0 - NO_DUST_VOLT;
  if (calcVoltage < 0) {
    calcVoltage = 0;
  }
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0,0);
  display.print("Dust: ");
  display.println(calcVoltage);  
  display.display();
  delay(1000);
}

Теперь необходимо добавить датчики температуры и влажности для полноты информации. Например подойдёт DHT22 – это два датчика в одном.

20160130_130724У него 4 ножки, но одна (третья, согласно DataSheet-у) не используется, так что просто нужно подключить 5 вольт, землю и один из цифровых пинов, например 2й. Так же есть библиотека для работы с этим датчиком, так что считывание показаний можно легко добавить в предыдущий скетч:

#include <Adafruit_SSD1306.h>
#include <DHT.h>

#define DHT_TYPE DHT22
#define DHT_PIN 2

#define DUST_ILED 7
#define DUST_PIN 0
#define NO_DUST_VOLT 0.9

#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);

DHT dht(DHT_PIN,DHT_TYPE);

void setup() {
  pinMode(DUST_ILED, OUTPUT);
  digitalWrite(DUST_ILED, LOW);
  // put your setup code here, to run once:
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);

  display.display();
  delay(2000);

  // Clear the buffer.
  display.clearDisplay();
  display.display();
  

  
}

void loop() {
  // put your main code here, to run repeatedly:
  digitalWrite(DUST_ILED, HIGH);
  delayMicroseconds(280);

  int outVolt = analogRead(DUST_PIN);

  digitalWrite(DUST_ILED, LOW);

  float calcVoltage = outVolt * 5.0 / 1024.0 - NO_DUST_VOLT;
  if (calcVoltage < 0) {
    calcVoltage = 0;
  }

  float h = dht.readHumidity();
  float t = dht.readTemperature();
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(0,0);
  display.print("H: ");
  display.print(h);
  display.print(" T: ");
  display.println(t);  
  display.println("");
  display.setTextSize(2);
  display.print("Dust: ");
  display.println(calcVoltage);  
  display.display();
  delay(1000);
}

просто читаем влажность и температуру с помощью библиотечных функций dht.readHumidity() и dht.readTemperature() и выводим их на экран:

20160130_21055820160130_221420

Ещё нужен источник питания, такой, чтобы можно было легко носить “прибор” с собой. Например подойдут пальчиковые (АА) батарейки, которые можно вставить в батарейный отсек и приделать к ним разъём с клеммой для лёгкого способа подключения к Arduino

20160130_130601Сам Arduino засунуть в корпус и приклеить батарейный отсек с помощью 2х стороннего скотча:

20160130_130621 20160130_130631

Что скажите? Полезный девайс?

Управляемая RGB лампочка: Часть 1 – NeoPixel-и

Сейчас куда не глянь везде всё умное. Умные розетки, кофеварки, телевизоры и конечно же лампочки. Одной из первых таких ламп я увидел Philips HUE, и загорелся идеей сделать свою управляемую RGB лампочку. У такой лампочки масса очевидных преимуществ: задание цвета под настроение, автоматическое включение/выключение, светобудильник, плюс неочевидные, как например светомузыка. Сказано сделано – начинаем копать.

Первое, что нужно решить – это каким будет источник света. Сейчас в моде светодиоды – они компактны и потребляют мало электричества, к тому же у меня завалялось 15 NeoPixel-ей от Adafruit на основе ws2812b.

20160117_185334Это 3 светодиода в одном плюс управляющая электроника. Особенность в том, что управляются они по одному проводу (вместе с напругой и землёй – три). Грубо говоря по проводу с данными вы передаёте сигнал, представляющий массив значений цветов. А дальше первый NeoPixel “откусывает” от этого массива первый элемент, выставляет его себе и передаёт остаток дальше и так далее по цепочке.

20160117_193644Второе – это какую беспроводную технологию использовать. WiFi мне кажется более универсальной и лучше масштабируемой в отличии от Bluetooth – это позволяет добавлять огромное количество таких ламп и заодно даёт возможность управления ими с помощью любого устройства, находящегося в этой же сети (да и за её пределами). Да и радиус действия у них гораздо больше. К тому же у меня валялось 3 модуля на основе esp8266.

20160117_190110Третье – это на чем реализовать управляющую логику. В наличии был Arduino Uno к нему и решил подключить NeoPixel-и, следуя инструкции на сайте Adafruit.

20160117_195326Однако Arduino Uno слишком большой, чтобы он мог поместиться в корпус стандартной лампочки. В интернете мне попалось видео, как уменьшить свои Arduino Uno проекты используя ATTiny45/85. К тому же на instructables есть guide, как заставить ATTiny работать с NeoPixel-ями. Не долго думая я купил ATTiny45 и перенёс пример с полноценной Arduino Uno на эту мелочь.

20160117_201131 1 20160117_204324

С другой стороны esp8266 – это очень мощная штука при малых размерах. Помимо WiFi этот SoC имеет ещё и GPIO. Я взял ESP-01 у которого есть два GPIO. К тому же Arduino IDE поддерживает это семейство начиная с версии 1.6.4 и у Adafruit даже есть гайд по настройке. Единственная проблема – это то, как заливать туда свой софт. Я купил USB-Serail converter который имеет выходной сигнал на 3.3 вольта, так как 5 вольт убьет esp8266 и сделал две кнопки на макетной плате, так как для заливки нового софта требуется перевести один из пинов в Low и затем RESET-нуть esp8266, более подробно о том как заливать свои программы можно прочитать тут:

20160117_193458 20160117_193920 1

Пусть вас не смущает наличие Arduino Uno на картинке справа – его я использовал исключительно, чтобы получить 3.3 вольта и запитать esp8266, а также чтобы получить 5 вольт для NeoPixel-ей.

20160118_205216Стандартный пример от Adafruit заработал и я бросился писать простенькую программку, которая бы коннектилась к моей домашней WiFi сети, поднимала сокет и ждала число, которое бы определяло цвет светодиодов. В интернете я наткнулся на хороший пример того, как общаться с esp8266 и на его основе с небольшими изменениями, используя Adafruit API для управление светодиодами, я добился чего хотел.

Для проверки накропал простенький скрипт на Perl-е, который бы и отправлял цвет для лампы:

use strict;

use IO::Socket::INET;

my $sock = new IO::Socket::INET(PeerAddr => '192.168.1.35',
                                PeerPort => '6969',
                    Proto => 'tcp') || die "Can't bind $@\n";

$sock->send(shift."\n");

И сама прошивка

#include <Adafruit_NeoPixel.h>
#include <ESP8266WiFi.h>

#define NEOPIXEL_DATA_PIN 0
#define PIXELS_NUM 7

Adafruit_NeoPixel strip = Adafruit_NeoPixel(PIXELS_NUM,
                                            NEOPIXEL_DATA_PIN,
                                           NEO_RGB + NEO_KHZ800);

const char* ssid = "mynetworkssid";
const char* password = "mynetpass";
WiFiServer server(6969);                                           

void setup() {
  Serial.begin(115200);
  strip.begin();
  strip.show();
 
  WiFi.begin(ssid, password);

  server.begin();
  while(WiFi.status() != WL_CONNECTED)
  {
    delay(500);
  }
}

void loop() {

  WiFiClient client = server.available();  
  if (client)
  {
    Serial.println("client connected.");
    while(client.connected())
    {
      if (client.available())
      {      
        String command;
        char c = client.read();
        while(c != '\n')
        {
          command += c;
          c = client.read();
        }

        if (command != 0)
        {
          char** tmp;
          char colorcommand[10];
          command.toCharArray(colorcommand, sizeof(colorcommand));
          
          uint32_t iColor = strtoul(colorcommand, tmp, 10);
           SetStripColors(iColor);
        }
      }
    }
    Serial.println("Client disconnected.");
    client.stop();
  }
}

void SetStripColors(uint32_t color)
{
  for (uint16_t i=0; i< PIXELS_NUM; i++)
  {
    strip.setPixelColor(i, color);
  }
  strip.show();
  delay(100);
}

Теперь дело за спецэфектами!

Цветомузыка в машину: Част 2 – Оцифровка

После того, как заработал прототип, я начал изучать варианты для захвата звука. На Nexus 7 есть выход на наушники, который я подключаю к AUX входу в машине. В целом работает неплохо, но есть посторонние шумы. Кто именно виноват не понятно, а вот что делать – есть варианты.

  • Первый и самый простой – это забить.
  • Второй – это использовать внешнюю звуковух вместе с Nexus 7
  • третий – это отказаться от использования планшета и собрать мультимедиа систему на базе одноплатного компьютера типа Raspberry Pi 2 или ODROID C1+. Вместе с HiFi Shield-ом (Для Raspberry Pi вот и Odroid C1 вот)

Пока что решено забить. Тогда нужно каким-то образом оцифровывать звук и разложить на частоты, чтобы принять решение о том, какой канал светодиодной ленты нужно включить.

  • Первый, очевидный способ – это взять уже имеющийся EasyCap, по сколько он не только оцифровывает видео, но и аудио.
  • Второй – взять USB звуковуху специально для этой задачи.

20151103_220604

Решил попробовать оба. EasyCap на основе utv007 у меня уже был, а вот звуковуху пришлось купить, к счастью стоит она ~500р.

20151211_202757Lightshowpi – cофт, который я использовал в прошлый раз для управления светодиодной лентой на основе проигрываемой композиции, так же может работать и в режиме аудио захвата. Для этого нужно сделать конфиг, который укажет, что нужно работать именно с внешним аудио сигналом и какую звуковуху использовать для этих целей.

Для того, чтобы указать название звуковой карты нужно запустить скрипт (audio_in_cards.py), который идёт в комплекте – он то и выдаст список подключенных звуковых устройств. В моем случае правда он ничего не принтит и мне пришлось сделать небольшую модификацию в нём, чтобы он заработал:

cd lightshowpi/tools
vim audio_in_cards.py

надо на последнюю строчку добавить print

import alsaaudio as aa

if __name__ == "__main__":
    print aa.cards()

теперь всё как надо – запускаем:

pi@rpitestmusic ~/lightshowpi/tools $ sudo python audio_in_cards.py
[u'ALSA', u'usbtv']

Тут видно, что для EasyCap – это usbtv. Соответственно просто пропишем это в конфиг.

vim /home/pi/.lights.cfg

там уже есть секция hardware, которая переопределяет какие пины использовать для управления лентой. Теперь нужно поменять, чтобы скрипт работал в режиме захвата звука со звуковухи и указать с какой:

[hardware]
gpio_pins = 0,1,2

[lightshow]
mode = audio-in
audio_in_card = usbtv

Собственно, надо попробовать – как это работает. Запускаем скрипт в режиме захвата

sudo python py/synchronized_lights.py

Первое, чтоб бросается в глаза – это “шум”. Даже если нет никаких звуков лента всё равно моргает.

Теперь заменим EasyCap на купленную USB звуковуху. Честно взял некую рандомную. Вот dmesg если вдруг интересно:

cmediadmesgСнова запустив скрипт, который показывает название аудио устройств в системе, увидим, что имя, которое нужно поместить в конфиг – это просто Device.

pi@rpitestmusic ~/lightshowpi/tools $ sudo python audio_in_cards.py
[u'ALSA', u'Device']

Заменим usbtv на Device, используя любимый редактор vim /home/pi/.lights.cfg

[hardware]
gpio_pins = 0,1,2

[lightshow]
mode = audio-in
#audio_in_card = usbtv
audio_in_card = Device

И ребутнем Raspberry для профилактики возможных USB глитчей. После чего запустим скрипт syncronized_lights.py теперь уже с USB звуковой картой.

Заодно прикинул как это будет выглядеть если прилепить светодиодную ленту за телевизором:

Выглядет неплохо, хотя лучше прикрепить за картиной или зеркалом.

Что скажите?