Обзор AirGradient ONE Kit – открытый монитор качества воздуха для помещений

Сегодня мы рассматриваем комплект монитора качества воздуха «AirGradient ONE» – обновленную версию предыдущего монитора AirGradient . Устройство оснащено сенсорами от Sensirion и Plantower, что позволяет измерять множество параметров качества воздуха, включая CO2, PM2.5, TVOC, NOx, температуру и влажность. Это монитор качества воздуха для помещений с открытым исходным кодом и открытой аппаратной частью. Это означает, что Arduino-код, схемы, PCB и 3D-модели корпуса доступны разработчикам.

Распаковка DIY-комплекта AirGradient ONE

AirGradient ONE DIY air quality monitor kit

Устройство было упаковано в картонную коробку с приветственной карточкой от производителя. На карточке размещен QR-код, ведущий на веб-страницу с инструкцией по установке .

AirGradient ONE : Inside the box

Поскольку был выбран комплект для самостоятельной сборки (есть и полностью собранная версия), некоторые сенсоры не были установлены заранее и были аккуратно упакованы в отдельные пакеты. Основная плата уже находилась внутри корпуса, но винты не были закручены. Дополнительные компоненты в коробке включали отвертку, винты, кабель USB Type-C и адаптер 5V 2A.

AirGradient ONE : Components inside the box

AirGradient ONE : S8, SGP41, and SHT4x sensors

AirGradient ONE : Inside the enclosure

Технические характеристики

Этот комплект представляет собой AirGradient ONE на плате V9 с микроконтроллером ESP32-C3 Mini в качестве основного чипа. Также в комплекте идет OLED-дисплей размером 1,3 дюйма с разрешением 128×64 пикселей. На основной плате установлены 11 программируемых RGB-светодиодов NeoPixel для визуализации уровня качества воздуха. Подробный список компонентов приведен ниже.

  • 1x Сенсор PM Plantower PMS5003
  • 1x Сенсор CO2 Senseair S8
  • 1x Модуль температуры и влажности SHT4x
  • 1x Модуль сенсора TVOC/NOx SGP41
  • 1x Адаптер 5V 2000mA
  • 1x Кабель USB Type-C с углом 90°
  • 4x Винты Torx T6 M1.8×10
  • 1x Отвертка Torx T6

Сборка комплекта AirGradient ONE

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

SenseAir S8, SHT4x, and SGP41 sensors

Для модуля Senseair S8 в комплекте идут два ряда штыревых разъемов: один 1×4, другой 1×5. Необходимо было установить сенсор в разъем на плате с маркировкой «CO2 Sensor», соблюдая правильную ориентацию.

Модули SGP41 и SHT4x очень похожи и имеют одинаковое количество контактов. Поэтому важно было внимательно проверить маркировку на модулях: SGP41 устанавливается в разъем «I2C3», а SHT4x – в разъем «SHT4x». Стоит отметить, что цвет шелкографии на модулях может отличаться от указанного в инструкции: в данном случае SGP41 имел синюю маркировку, а SHT4x – пурпурную. При установке модулей в разъемы необходимо было убедиться, что они направлены наружу, как показано на рисунках.

Сенсор PMS использует разъем JST и в данном случае уже был подключен к плате. Оставалось только проверить надежность соединения с обеих сторон.

После установки модулей устройство можно включить, подключив кабель USB Type-C к разъему на задней панели корпуса. На задней стороне корпуса есть специальный паз для фиксации кабеля, предотвращающий его болтание и запутывание.

AirGradient One : SGP41 sensor

AirGradient One : SHT4x sensor

AirgradientONE : Plugging the USB Type-C cable

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

Использование монитора качества воздуха AirGradient ONE

При включении устройства на экране появляется запрос на выбор единиц измерения. Перейти в меню настроек можно длительным нажатием кнопки на задней панели. Кратковременные нажатия позволяют переключаться между вариантами единиц измерения PM2.5 и температуры, как указано ниже.

  • Температура: °C, PM: µg/m³
  • Температура: °C, PM: US AQI
  • Температура: °F, PM: µg/m³
  • Температура: °F, PM: US AQI

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

Airgradient ONE : measurement units configuration

При первом включении устройство запрашивает подключение к WiFi. Для этого потребовалось записать серийный номер устройства, отображаемый на OLED-экране во время загрузки. Затем с помощью мобильного телефона нужно было найти точку доступа устройства с именем «AG-xxxxxx«, где «xxxxxx» – серийный номер устройства.

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

Airgradient ONE : Setting up the WiFi

После экрана настроек RGB-светодиоды устройства начинают отображать цвет, соответствующий качеству воздуха, а OLED-экран показывает данные с сенсоров в выбранных единицах измерения. Комплект AirGradient ONE будет работать даже без подключения к WiFi.

AirgradientONE : Ready to work

AirgradientONE : Main screen

Подключение к AirGradient Dashboard

Поскольку на задней панели корпуса есть USB-разъем, устройство было подключено к ноутбуку с помощью прилагаемого кабеля USB Type-C. В Arduino IDE была открыта Serial Monitor для просмотра выводимых сообщений. Было обнаружено, что устройство собирает данные измерений, включая RSSI подключенной WiFi-сети, и отправляет их на сервер AirGradient Dashboard в формате JSON. По умолчанию используется URL http://hw.airgradient.com/sensors/airgradient:xxxxx/measures, где xxxxx – серийный номер устройства. Однако сервер отклонил запрос с сообщением об ошибке «sensor 'airgradient:xxxxxx' unknown«.

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

AirgradientONE : Dashboard setup AirgradientONE : Dashboard setup AirgradientONE : Dashboard screen

Прошивка AirGradient

На момент обзора доступно два метода прошивки: прямое обновление через веб-браузер и ручная прошивка через Arduino IDE. В данном случае был выбран второй вариант. Для работы использовалась Arduino IDE 1.8.13. Исходный код был загружен с GitHub-репозитория производителя, после чего был открыт файл ONE_V9.ino . Как указано в инструкции, была выбрана платформа ESP32 и модель Lolin C3 Mini в качестве целевой платы.

AirgradientONE : Setting board manager URL in Arduino IDE

Список необходимых библиотек указан в заголовке файла ONE_V9.ino. В данном случае потребовались следующие шесть библиотек. Их можно установить через менеджер библиотек или вручную, загрузив и скопировав в папку libraries Arduino.

  • WifiManager от tzapu, tablatronix, тестировалась версия 2.0.11-beta
  • U8g2 от oliver, тестировалась версия 2.32.15
  • Sensirion I2C SGP41 от Sensation, версия 0.1.0
  • Sensirion Gas Index Algorithm от Sensation, версия 3.2.1
  • Arduino-SHT от Johannes Winkelmann, версия 1.2.2
  • Adafruit NeoPixel от Adafruit, версия 1.11.0

Первая попытка компиляции завершилась ошибкой из-за отсутствия некоторых библиотек. Потребовалось установить дополнительные библиотеки.

  • PMS Library от Markusz Kakl, версия 1.1.0
  • S8 UART от Josep Comas, версия 1.0.0
  • Sensirion Core от Sensirion, версия 0.6.0

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

#ifndef PMS_H
#define PMS_H
 
#include "Stream.h"
 
class PMS
{
public:
static const uint16_t SINGLE_RESPONSE_TIME = 1000;
static const uint16_t TOTAL_RESPONSE_TIME = 1000 * 10;
static const uint16_t STEADY_RESPONSE_TIME = 1000 * 30;
 
static const uint16_t BAUD_RATE = 9600;
 
struct DATA {
// Standard Particles, CF=1
uint16_t PM_SP_UG_1_0;
uint16_t PM_SP_UG_2_5;
uint16_t PM_SP_UG_10_0;
 
// Atmospheric environment
uint16_t PM_AE_UG_1_0;
uint16_t PM_AE_UG_2_5;
uint16_t PM_AE_UG_10_0;
 
// Raw particles count (number of particles in 0.1l of air
uint16_t PM_RAW_0_3;
uint16_t PM_RAW_0_5;
uint16_t PM_RAW_1_0;
uint16_t PM_RAW_2_5;
uint16_t PM_RAW_5_0;
uint16_t PM_RAW_10_0;
 
// Formaldehyde (HCHO) concentration in mg/m^3 - PMSxxxxST units only
uint16_t AMB_HCHO;
 
// Temperature & humidity - PMSxxxxST units only
int16_t AMB_TMP;
uint16_t AMB_HUM;
};
 
PMS(Stream&);
void sleep();
void wakeUp();
void activeMode();
void passiveMode();
 
void requestRead();
bool read(DATA& data);
bool readUntil(DATA& data, uint16_t timeout = SINGLE_RESPONSE_TIME);
 
private:
enum STATUS { STATUS_WAITING, STATUS_OK };
enum MODE { MODE_ACTIVE, MODE_PASSIVE };
 
uint8_t _payload[50];
Stream* _stream;
DATA* _data;
STATUS _status;
MODE _mode = MODE_ACTIVE;
 
uint8_t _index = 0;
uint16_t _frameLen;
uint16_t _checksum;
uint16_t _calculatedChecksum;
 
void loop();
char Char_PM2[10];
};
 
#endif

#include "PMS.h"
 
PMS::PMS(Stream& stream)
{
  this->_stream = &stream;
}
 
// Standby mode. For low power consumption and prolong the life of the sensor.
void PMS::sleep()
{
  uint8_t command[] = { 0x42, 0x4D, 0xE4, 0x00, 0x00, 0x01, 0x73 };
  _stream->write(command, sizeof(command));
}
 
// Operating mode. Stable data should be got at least 30 seconds after the sensor wakeup from the sleep mode because of the fan's performance.
void PMS::wakeUp()
{
  uint8_t command[] = { 0x42, 0x4D, 0xE4, 0x00, 0x01, 0x01, 0x74 };
  _stream->write(command, sizeof(command));
}
 
// Active mode. Default mode after power up. In this mode sensor would send serial data to the host automatically.
void PMS::activeMode()
{
  uint8_t command[] = { 0x42, 0x4D, 0xE1, 0x00, 0x01, 0x01, 0x71 };
  _stream->write(command, sizeof(command));
  _mode = MODE_ACTIVE;
}
 
// Passive mode. In this mode sensor would send serial data to the host only for request.
void PMS::passiveMode()
{
  uint8_t command[] = { 0x42, 0x4D, 0xE1, 0x00, 0x00, 0x01, 0x70 };
  _stream->write(command, sizeof(command));
  _mode = MODE_PASSIVE;
}
 
// Request read in Passive Mode.
void PMS::requestRead()
{
  if (_mode == MODE_PASSIVE)
  {
    uint8_t command[] = { 0x42, 0x4D, 0xE2, 0x00, 0x00, 0x01, 0x71 };
    _stream->write(command, sizeof(command));
  }
}
 
// Non-blocking function for parse response.
bool PMS::read(DATA& data)
{
  _data = &data;
  loop();
  
  return _status == STATUS_OK;
}
 
// Blocking function for parse response. Default timeout is 1s.
bool PMS::readUntil(DATA& data, uint16_t timeout)
{
  _data = &data;
  uint32_t start = millis();
  do
  {
    loop();
    if (_status == STATUS_OK) break;
  } while (millis() - start < timeout);
 
  return _status == STATUS_OK;
}
 
void PMS::loop()
{
  _status = STATUS_WAITING;
  if (_stream->available())
  {
    uint8_t ch = _stream->read();
 
    switch (_index)
    {
    case 0:
      if (ch != 0x42)
      {
        return;
      }
      _calculatedChecksum = ch;
      break;
 
    case 1:
      if (ch != 0x4D)
      {
        _index = 0;
        return;
      }
      _calculatedChecksum += ch;
      break;
 
    case 2:
      _calculatedChecksum += ch;
      _frameLen = ch << 8;
      break;
 
    case 3:
      _frameLen |= ch;
      // Unsupported sensor, different frame length, transmission error e.t.c.
      if (_frameLen != 2 * 9 + 2 && _frameLen != 2 * 13 + 2)
      {
        _index = 0;
        return;
      }
      _calculatedChecksum += ch;
      break;
 
    default:
      if (_index == _frameLen + 2)
      {
        _checksum = ch << 8;
      }
      else if (_index == _frameLen + 2 + 1)
      {
        _checksum |= ch;
 
        if (_calculatedChecksum == _checksum)
        {
          _status = STATUS_OK;
 
          // Standard Particles, CF=1.
          _data->PM_SP_UG_1_0 = makeWord(_payload[0], _payload[1]);
          _data->PM_SP_UG_2_5 = makeWord(_payload[2], _payload[3]);
          _data->PM_SP_UG_10_0 = makeWord(_payload[4], _payload[5]);
 
          // Atmospheric Environment.
          _data->PM_AE_UG_1_0 = makeWord(_payload[6], _payload[7]);
          _data->PM_AE_UG_2_5 = makeWord(_payload[8], _payload[9]);
          _data->PM_AE_UG_10_0 = makeWord(_payload[10], _payload[11]);
 
        // Total particles count per 100ml air
          _data->PM_RAW_0_3 = makeWord(_payload[12], _payload[13]);
          _data->PM_RAW_0_5 = makeWord(_payload[14], _payload[15]);
          _data->PM_RAW_1_0 = makeWord(_payload[16], _payload[17]);
          _data->PM_RAW_2_5 = makeWord(_payload[18], _payload[19]);
          _data->PM_RAW_5_0 = makeWord(_payload[20], _payload[21]);
          _data->PM_RAW_10_0 = makeWord(_payload[22], _payload[23]);
 
          // Formaldehyde concentration (PMSxxxxST units only)
          _data->AMB_HCHO = makeWord(_payload[24], _payload[25]) / 1000;
 
          // Temperature & humidity (PMSxxxxST units only)
          _data->AMB_TMP = makeWord(_payload[20], _payload[21]);
          _data->AMB_HUM = makeWord(_payload[22], _payload[23]);
        }
        _index = 0;
        return;
      }
      else
      {
        _calculatedChecksum += ch;
        uint8_t payloadIndex = _index - 4;
 
        // Payload is common to all sensors (first 2x6 bytes).
        if (payloadIndex < sizeof(_payload))
        {
          _payload[payloadIndex] = ch;
        }
      }
      break;
    }
    _index++;
  }
}

Компиляция и загрузка завершились успешно. Однако обнаружилось, что устройство не подключается к WiFi. Проблема была решена путем жесткого прописывания SSID и пароля в коде. После этого устройство заработало нормально.

AirGradientONE : Configuring SSID and password in Arduino IDE

Открытый исходный код

Настройка исходного кода Arduino

Как упоминалось выше, исходный код доступен на GitHub, включая не только прошивку для этого набора, но и версии для других устройств, а также примеры.

Исходный код (ONE_V9.ino) был изменен для отправки данных на собственный сервер. Для этого значение переменной APIROOT в начале кода было заменено на URL сервера. Затем функция sendToServer() была модифицирована для добавления серийного номера устройства в HTTP-запрос. После обновления значения строки POSTURL в соответствии с требованиями, новая прошивка была скомпилирована и загружена на устройство. Через несколько секунд устройство начало успешно отправлять данные на сервер.

AirGradient ONE : Setting custom URL AirGradient ONE : Setting custom HTTP parameters in Arduino

AirGradient ONE : Data in the custom database
Данные в пользовательской базе данных

Поскольку 11 RGB-светодиодов программируемы, исходный код был дополнительно изменен для настройки их поведения. В стандартной конфигурации эти светодиоды выключены при загрузке и включаются после перехода устройства на главный экран.

Так как устройство использует библиотеку NeoPixel от Adafruit, цвет каждого светодиода можно управлять, вызывая функцию setPixelColor с указанием индекса светодиода и значений RGB-компонентов. Поэтому в конец функции setup был добавлен код, заставляющий светодиоды случайно мигать в течение нескольких секунд перед переходом в нормальный режим.

void setup()
{
  ...
  int i;
  int j;
  int r;
  int g;
  int b;
  for(i = 0; i < 11; i++) {
      pixels.setPixelColor(i, pixels.Color(0, 0, 0));
      delay(1);
  }
  pixels.show();
  for(i = 0; i < 32; i++) {
    for(j = 0; j < 11; j++) {
      r = random(0, 256);
      g = random(0, 256);
      b = random(0, 256);

      pixels.setPixelColor(
        j,
        pixels.Color(r, g, b));
    }
    delay(50);
    pixels.show();
  }
  for(i = 0; i < 11; i++) {
      pixels.setPixelColor(i, pixels.Color(0, 0, 0));
  }
  pixels.show();
}

AirGradient ONE : Changing LEDs color

Файлы аппаратного дизайна

Доступны не только исходные коды, но и схемы, а также файлы печатных плат для разработчиков на этой странице . Эти файлы представлены в формате KiCad и успешно открываются в KiCad 7.0 без каких-либо проблем.

AirGradientONE : Schematic diagram AirGradientONE : PCB's top and bottom layers AirGradientONE : PCB's bottom layer AirGradientONE : PCB's top layer

3D-печать собственного корпуса

Производитель также предоставляет разработчикам 3D-модели корпуса. Эти модели в формате STL можно загрузить на этой странице , где доступны два ZIP-файла: один для корпуса, другой для подставки.

3D-модель корпуса состоит из трех частей: нижняя часть, верхняя часть и модель области кабеля. Эти модели были визуализированы в программе MeshLab после открытия STL-файлов, как показано на следующих изображениях. Также была протестирована печать модели области кабеля: STL-файл был обработан в PrusaSlicer 2.6.0 и напечатан с использованием Printrun 2.1. Модель печаталась легко, результат показан на изображении ниже.

AirGradient ONE 3D enclosure model in MeshLab AirGradient ONE viewing 3D enclosure model in MeshLab AirGradient ONE : 3D printed model

Заключение

Монитор качества воздуха AirGradient ONE работает очень хорошо, хотя при наблюдении за сообщениями через последовательный порт заметно, что значения измерений иногда нестабильны после перезапуска устройства. К счастью, они стабилизируются в течение нескольких секунд. Открытый исходный код устройства также позволяет настраивать прошивку, электронику и корпус.

Благодарность AirGradient за предоставление набора One Kit для этого обзора. Полностью собранную и протестированную версию AirGradient ONE можно заказать за $195, а версию набора, рассмотренную здесь, — за $138 на сайте производителя .

Выражаем свою благодарность источнику, с которого взята и переведена статья, сайту cnx-software.com.

Оригинал статьи вы можете прочитать здесь.

0 0 votes
Article Rating
Подписаться
Уведомление о
guest

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.

0 Комментарий
Oldest
Newest Most Voted
Inline Feedbacks
View all comments