Giter VIP home page Giter VIP logo

aevoc's Introduction

AeVOC

Sensor measuring particles and aerosoles down to PM2.5 and VOCs and warn if concentrations get too high.

About the project

AeVOC is an combined air quality sensor which helps to monitor aerosole concentrations and smelly organic compounds. You can use it to give warning if the air quality degrgades and it gets time to open the windows.

Wire the components like this.

The sensor contains:

  • An SDS011 particle sensor, connected to a serial port. This sensor measures dust particles and aerosoles in the size categsries 10μm and 2,5μm. This measurement is the most important value to determine if the windows need to be opened. Aerosoles can contain viruses an bacteria and an infected person will exhale aerosoles with infections viruses, especially if speaking. If the values measured by the SDS011 rise it is time to exchange the air in the room.
  • An CCS811 VOC sensor, connected via i2c. This sensor detects volatile organic compounds which tend to be smelly and can be a health hazard in itself in very high concentrations. As VOCs are mostly emitted by humans the measured level help to detect degrading air quality and the need for fresh air.
  • An DHT22 (serial connetion with one wire) which reliably measures temperature and humidity.
  • All the sensors are driven by a NodeMCU with an ESP8266. Using this firmware the device publishes all its measurements via MQTT in addition to displaying them on a LED matrix and a small OLED.
  • The 0.96" OLED is connected via i2c. It displays all the measurements, a warning summary and for each measurement a small diagram.
  • A matrix of 8x8 WS2812-RGB-LEDs displays the most crucial measurements as a color coded diagram.

There are plans for a nice case made of wood. The document (viewable in the browser) contains technical drawings and the whole CAD design: https://cad.onshape.com/documents/d1397a8c60d8cf10bc632b52/w/d572a7dd96a755f54efafc1b/e/793fdbc24d1e0b242f0b46c7

There is also a step-by-step guide in german at Make Projects: https://makeprojects.com/de/project/aevoc

Watch the build on YouTube: https://youtu.be/r93HGVTbWig

A comprehensive description in german is published in c't 03/21.

Interpreting the LEDs

The LED matrix displays 8 bar diagrams in its 8 columns. After 3 minutes of measuring all the columns get shifted to the right so the matrix gives an impression of the values in the last 21 to 24 minutes.

8 Pixels in the height of each column are no fine resolution so the sensor makes use of colors and brightness. At first the column shows a simple bar diagram using the 8 pixels. If more measurements come in it darkens the existing dtiagram and adds the new measurement in such a way that it never exceeds the maximum brightness for the filled bottom part of the columns. If the column gets faded out in the upper part the size of the fadeout gives an impression on the variance of the measured data.

The matrix uses 3 color channels to display two values. The blue channel displays the VOC levels. Particles and aerosoles are displayed with the red and green channel. If the newest leftmost measurement features high values the aerosole bar gets red. Middle values mix red and green to produce yellow bars, small values and bars light up in green. As the color channels are overlapping there may ba mixed colors. If VOCs and aerosoles are both in midrange the matrix displays white in the lower part because blue and yellow mix up to a cold white.

The color for the aerosole amount is only determined for the most recent bar (up to 3 minutes). If there were red bars for high values in the past but the current value is low the matrix colors the high bars in green and the matrix glows bright and green.

The intuition behind all this is this:

  • If the sensor shows green the values are low.
  • High aerosole values generate a red warning color and the LEDs are brighter.
  • It the VOCs are high too the color gets more white but the bright LEDs indicate a warning.

The Baseline of the CCS811

The VOC sensor calibrates its internal baseline on its own using the measurements it has seen. Do not expect to get accurate readings after powering the device up. Usually you should expose the sensor to fresh air without VOCs which usually makes it lower its baseline.

However the sensor semms to calibrate its baseline wrongly from time to time. A wrong baseline may produce values which are much too high (up to factor 100) which make the sensor show much blue. The baseline recalbrates automatically after some hours of use. We experienced the best measurements by just ignoring phases with a wrongly calibrated baseline and simply letting the sensor run. You can reset the baseline with a restart which usually leads to low but not yet calbrated values.

Code structure

Please build the code using PlatformIO. The platformio.ini contains all the dependencies and specifies working upload speeds etc. All the sources are in src/. The code is C++ so there are some classes with a definition in the header file and the implementation in the .cpp with the same name. Your starting point should be main.cpp.

In configHelpers.h you find maximum and minimum values which scale the diagrams. Please adjust these values if you get to know better maximim and minimum values for your region or because of new scientific insights which aerosol levels may be harmful. We tried to initialize these values with defaults which made sense in december 2020.

aevoc's People

Contributors

pinae avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

aevoc's Issues

Buffersize in configHelpers

Hi Pina,
I think the buffer for deviceName in configHelpers.h is too small:
char deviceName[23] = {'A', 'E', 'V', 'O', 'C', '-', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
It should be:
char deviceName[25] = {'A', 'E', 'V', 'O', 'C', '-', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
becaus in configHelpers.cpp the zero's are overwritten with an 18-character long string,
but there is only space for 17, and no space for a trailing \0:
DeviceName::DeviceName() { byte mac[6]; WiFi.macAddress(mac); char macStr[18]; sprintf(&macStr[0], "%x:%x:%x:%x:%x:%x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); strncpy(&deviceName[6], macStr, 18); }

Second issue here. Shouldn't it be:
sprintf(&macStr[0], "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
so the string will always have 18 characters, independent from the mac?

Uwe

mqtt.cpp createFullTopicStr()

Hi Pina,

mqtt.cpp line 78: (fullTopicStr + topicLen+1) = 0; // <-- the 0 is OUT OF ALLOCATED MEMORY!
see allocation at line 74: char
fullTopicStr = (char*) calloc(topicLen+1, sizeof(char));

proposal for line 78: fullTopicStr[topicLen] = '\0';

Best regards
Walter

Keyestudio CCS811 needs GND at the WAKE pin?

Hello Pina,

at first thank you very much for AeVOC. Works great and had a lot of fun building it.
However I have to admit I used a 3D Model for FDM printing as a housing.

Now to the topic.
At first I got no reading from the CCS811 and I was wondering if this has something to do with the Burn In mentioned by you and the Datasheet (https://cdn.sparkfun.com/assets/learn_tutorials/1/4/3/CCS811_Datasheet-DS000459.pdf) from sparkfun.

I found a specific page (https://wiki.keyestudio.com/KS0457_keyestudio_CCS811_Carbon_Dioxide_Air_Quality_Sensor) for my sensor pcb and it seems like the WAKE pin needs GND in order for CCS811 to work. At least now I get measurements.

Now I'm wondering if this is necessarily just for my CCS811 or for all of them?

Best wishes,

Sebastian

mqtt.cpp subscribeToTopic() memory leak

Hi Pina,
sorry for my code reading ;-)

Pointer char* fullTopicStr is only valid in this function and points to allocated memory that is to be released again in this function with free(fullTopicStr).

void subscribeToTopic(const char* topic, void (*callback)(char*)) {
    if (!wifiIsConnected || !mqttClient.connected()) return;
    char* fullTopicStr = createFullTopicStr(topic); // <--- pointer *fullTopicStr is valid only in this function/method
    mqttClient.subscribe(fullTopicStr);
    SubscribedMqttTopic* newTopic = new SubscribedMqttTopic(fullTopicStr, callback);
    SubscribedMqttTopicList* newTopicListElem = new SubscribedMqttTopicList(
        newTopic, subscribedTopicsList);
    subscribedTopicsList = newTopicListElem;
    logger.printf("Subscribed to MQTT-topic: %s\n", fullTopicStr); /* <--- after this line, add */ free(fullTopicStr);
}

Aevoc: measurement Interpretation & addon: MH-Z19

Dear Pina,

thanks for this great project, I really enjoyed it!
So, I kept going on and, by now, my AEVOC is still not mounted in a box but I added a real CO2 sensor and changed the code to work for me and also followed your suggestion and set up an mqtt/node red on my "NASPI" (raspi 4), so I can follow the evolution of the recorded data over time.

Now, I just wanted to comment on my findings of those: unfortunately I find the VOC sensor quite sensitive to any kind of organic molecules, odor.. we have a cat, but also cleaning the flat, drying loundry in the same room, cooking..makes the VOC shoot up. But since CO2 is only guessed, based on the VOC value, this CO2 value is not really reliable. So I added an MH-Z19 CO2 sensor and now I would say this is a really good help to judge about aerosol concenration indoors.
A problem with the particle sensor is, that it detects any particles with a certain size. That means, since a while it is a really good pollen detector ;) Which is actually good for me, because if during mornings room venting the particle count shoots up while CO2 goes down, I know I should take some anti-allergy medication before leaving the house.

Altogether, this is a real fun project with much more functionality - but the interpretation of measured values requires always some thinking, too.

Just a comment about the MH-Z19: I'm a crappy programmer and didn't work much with arduino like devices, so far.. first, I didn't manage to make it run when connected to the free pins D0 as RX and D1 as TX using softwareserial, so I took the pins D9 and D10 using softwareserial and a driver i found on the arduino website. This made debugging my code a bit tricky because this blocks communication with my pc.. If you had an advice here? Are there alternative pins allowing to communicate via UART without blocking the communication via the usb-rs232 interface?
Another problem is, that sometimes communication with the CO2 sensor is lost, so I needed to add another check-if-alive-otherwise-reinitialize to make it continously working.
Still, it was worth it: the sensor works quite excellent!

Well, thanks for all this!
I'll keep following you all on c't and c't uplink.

best
Chris

SDS011 in continuous operation will only last ~1 year

Hi Pinae,
just one remark/idea, wouldn't it make sense to improve the life time of the SDS011 sensor.
Up to it's specification it will only last 1 year if it's running in continuous operations.
Maybe it would make sense to only wake up the sensor every 2-3min read the values and put it to sleep again?
Just an idea to make the setup more sustainable.
Didn't had a chance to adjust the code by myself to meet that requirements.
Cheers /
Andreas

mqtt.cpp createFullTopicStr() (closed issue #6)

Hi Pina,
sorry, i don't know how to reopen a closed issue (#6).

The ERROR IS NOT FIXED!

The code "(fullTopicStr + topicLen+1) = '\0' writes 1 Byte behind the allocated memory!
Allocation code: "char* fullTopicStr = (char*) calloc(topicLen+1, sizeof(char));"
The management data of the pool or other important data may already be stored there.

Example: topicLen = 20:
-> 21 Bytes are allocated (from Byte *(fullTopicStr+0) till *(fullTopicStr+20) or *(fullTopicStr + topicLen))
-> "(fullTopicStr + topicLen+1) = '\0' writes at Byte 21 behind memory at pointer *fullTopicStr
-> my proposal for line 78: fullTopicStr[topicLen] = '\0'; or *(fullTopicStr + topicLen) = '\0';

Best regards Walter

PS: calloc() clears all bits in the allocated memory. The ending '\0' does not have to be written ;) ... behind the allocated memory

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.