AirGradient Prometheus exporter.

Overview

AirGradient Prometheus Exporter

CI

AirGradient has a DIY air sensor. I built one (actually, more than one). I want to integrate sensor data into my in-home Prometheus instance and graph the data in Grafana.

So I built this.

How it Works

If you're using the official AirGradient Arduino sketch (C02_PM_SHT_OLED_WIFI), you can configure it to enable WiFi and send data to a remote server every 9 seconds (as it cycles through the display of PM2.5, CO2, temperature, and humidity values).

By default, it sends a small JSON payload to AirGradient's servers, and you can monitor the data via their service.

But this exporter runs a Docker container that 'catches' that data (by pointing your AirGradient sensor at it), and then it reports it through a /metrics endpoint that Prometheus can scrape to ingest sensor data at whatever interval Prometheus is configured to scrape it.

I've included the sketch I deploy to my sensors for use with this repository: AirGradient-DIY.ino.

How to Use

This thing is a couple PHP scripts that run in a Docker container. That's it.

You could even run the thing without using Docker, it doesn't care, it's just PHP.

Run the Docker container

Run the PHP script inside Docker like so:

docker run -d -p 9925:80 --name airgradient \
  -v "$PWD":/var/www/html \
  php:8-apache \
  /bin/bash -c 'chown -R 33:33 html; a2enmod rewrite; apache2-foreground'

Or you can set it up inside a docker-compose file like so:

---
version: "3"

services:
  shelly-plug:
    container_name: airgradient
    image: php:8-apache
    command: "/bin/bash -c 'mkdir /sensors; chown -R 33:33 /sensors; a2enmod rewrite; apache2-foreground'"
    ports:
      - "9925:80"
    volumes:
      - './:/var/www/html'
    restart: unless-stopped

Point your AirGradient at the service

In your AirGradient's sketch, modify the APIROOT to point to your server, e.g.:

// change if you want to send the data to another server
String APIROOT = "http://my-own-server.net:9925/";

Upload the sketch to the AirGradient sensor, make sure you have it connected to your network, then you can test that the exporter has data available to it by running this curl command:

$ curl localhost:9925/metrics
# HELP instance The ID of the AirGradient sensor.
instance 1995c6
# HELP wifi Current WiFi signal strength, in dB
# TYPE wifi gauge
wifi -52
# HELP pm02 Particulat Matter PM2.5 value
# TYPE pm02 gauge
pm02 6
# HELP rc02 CO2 value, in ppm
# TYPE rc02 gauge
rco2 862
# HELP atmp Temperature, in degrees Celsius
# TYPE atmp gauge
atmp 31.6
# HELP rhum Relative humidity, in percent
# TYPE rhum gauge
rhum 38

If you get an error, make sure the Docker container (and the host it's on) is able to be reached over HTTP!

Known issues

This project currently only works with one AirGradient Sensor. See this issue for details on getting it working with more than one sensor.

License

MIT.

Author

Jeff Geerling.

Comments
  • Implement Prometheus Scrape Server

    Implement Prometheus Scrape Server

    Hello there 👋 I don't usually work with Arduino, but something about your project has encouraged me to contribute.

    Here's what is possible now:

    • You can now change the deviceId per arduino to use multiple sensors.
    • You no longer need a docker container to act as a temporary influx database.
    • Your prometheus can now fetch the data in real time from each ESP.
    • Updated metrics to be more "promethestic".
    • Screen is now updated and is no longer dependent on IOWait.
    • Removed CI.yml as deemed no longer valid.

    Please test this out and let me know what you think.

    planned 
    opened by Kashalls 45
  • jitter from the CO2 sensor

    jitter from the CO2 sensor

    hey, I have assembled it 3 days ago and as the title said, my CO2 sensor (senseair s8) began the have some false measurements. So two afternoons in a row I got some measurements of 514 und -1 ppm yesterday and 516 und -1 today, which are false according to the course of the graph, and I only had the idea of unplugging it. Jeff and the datasheet of the sensor said that it might take a while till it's "calibrated" but has anyone else had such an experience? I don't want to unplug it every day ^^

    image

    stale 
    opened by franz134 12
  • Complete rework of the Sketch

    Complete rework of the Sketch

    Hey @geerlingguy,

    Firstly, let me thank you for introducing me to the world of Arduino, I always wanted a microcontroller project and AIrGradient and your video was the push I needed. Especially when you paired the sensor with a full ansible setup for prometheus and grafana \o/ Just awesome.

    I've dived into the code, and realized ... I hate arduino IDE, so I've taken the time to rewrite the sketch using VS Code with PlatformIO plugin. Such a breeze to use with proper library management, a one file to setup the board that I'm using. It made me like C++ again.

    So here we go, here is my fork: https://github.com/Belphemur/AirGradient/

    Changes

    1. I've broken the code into multiple files and classes
      1. Metrics::Gatherer takes care of pulling the metrics from the sensor at different interval and save them in memory.
      2. Prometheus::Server takes care of providing the HTTP server and rely on the Gatherer to get the metrics.
    2. After reading the forum of AirGradient, I changed the way the board interact with the PMS5003 sensor to increase its lifespan (source: forum)
      1. Basically, I wake up the sensor every 120 sec for 30 secs. Do the reading, and ask it to go back to sleep. The forum post says that this could increase the lifespan from 3 years to 18 years 👍
    3. All the different configuration got moved into a Configuration folder to be easily modified.
    4. I've added a couple of mitigation against getting wrong data
      1. When getting -1 or 65k for CO2, I retry the reading with a 10ms delay. Works 99% of the time.
      2. Only accept 0 as PM2 when the previous reading was under 10. To avoid the weird 0 spike in the graph. (Usually, it mean there was a a problem getting the value from the sensor).

    I let your decide if you'd like me to make a PR for it.

    stale 
    opened by Belphemur 10
  • Issue 21 | Add support for SGP30

    Issue 21 | Add support for SGP30

    What was changed?

    Added in support for SGP30 sensor based on the following AirGradient doc:

    https://www.airgradient.com/resources/tvoc-on-airgradient-diy-sensor/

    Sensor can be enabled from line 27 setting the flag to True. As well changing the Serial speed from 9600 to 115200

    Requires the additional install of the SGP30 Arduino library by RobTillaart

    stale 
    opened by IAm-ThePat 5
  • Investigate use of ESPHome instead of a custom sketch

    Investigate use of ESPHome instead of a custom sketch

    After seeing @MallocArray's airgradient_esphome project, I'm kinda wanting to see if I can not only replicate the Prometheus setup from this repo (see ESPHome's Prometheus Component), but also get the thing integrated with my Home Assistant setup (video on that coming Wednesday).

    That way you could use ESPHome and choose whether to use Prometheus or Home Assistant—or both!

    opened by geerlingguy 5
  • [Improvement Suggestion] Temperature sensor offset management

    [Improvement Suggestion] Temperature sensor offset management

    Hello,

    SHT30/SHT31 as well as BME280 & other sensors of that kind typically have an offset of +/- 1°C, added to that the sensor enclosure & the proximity of other electronic component, the offset can easily rack up to a couple of °C.

    Hence, in order to avoid recompiling and be able to simply tweak the offset to get a value as close to the real temperature as possible, I added some logic to the code available on my BME280 fork to store the Temperature Offset in the EEPROM, setup a route to get/set the value, and each time the temperature is fetched (on screen display or scrapped by Prometheus) the offset is applied.

    Feel free to add my code to yours, or to ask me to do a specific PR, if you want it without the BME280 extension.

    stale 
    opened by Thelvaen 5
  • Adding option to flip screen and corrected

    Adding option to flip screen and corrected "define" names

    improvement based on #15

    added FLIP_SCREEN toggle to the config, also changed below vars for consistency with the rest of the code and to match best practices (Upper Snake Case for #define) #define staticip => #define STATIC_IP and: IPAddress static_ip => IPAddress staticIp

    stale 
    opened by Thelvaen 4
  • screen might be flipped when it should not be?

    screen might be flipped when it should not be?

    considering the design of the case in the STLs provided by airgradient, it seems that the screw mounts are not in an orientation that would allow the screen to be read properly (it ends up on the top left instead of the bottom right corner.

    Can I PR the following code:

    // AirGradient case has its screws behind the screen with the fixation reverse compared
    // to Jeff G. box, so flipping the screen is not necessary
    // uncomment if you need/prefer having the screen on the bottom.
    //#define flipScreen
    

    and

      #ifdef flipScreen
      display.flipScreenVertically();
      #endif
    
    stale 
    opened by Thelvaen 4
  • [Improvement Suggestion] replace SHT31 with BME280

    [Improvement Suggestion] replace SHT31 with BME280

    I've tinkered a bit with the code to replace the SHT30/SHT31 with a BME280 (I prefer those sensors as they are a bit more reliable, and also because they provide atmospheric pressure data along with the Temperature/Humidity).

    BME280 can work on I²C bus, hence it's almost a drop in replacement for the SHT30/SHT31 in terms of wiring, but as the board is a bit bigger, an external enclosure will be needed for said sensor (it's almost perfect as it also brings the temperature sensor away from the PM2.5/CO² sensor that do generate a couple of °C of heat, making the temp reading false.

    My code is available at https://github.com/Thelvaen/airgradient-prometheus/tree/BME280 for those interested in :)

    stale 
    opened by Thelvaen 3
  • Docker container not accepting POST request

    Docker container not accepting POST request

    First of all, thanks to Jeff for your amazing work and all the documentation work you do, it's fantastic and deeply appreciated as I learn a lot from it!

    I have the same problem as #6 I think.

    I logged into the serial port of the device and noticed that the POST request got 404'ed too.

    I managed to get the error code to change to 403 by hopping into the container and creating the folder /var/www/html/sensors/airgradient:d024a3/measures/ as well as changing the permissions with chown -R 33:33 . but for some reason the container do not 'accept' the POST request:

    {"wifi":-69,"pm02":2,"rco2":1151,"atmp":23.20,"rhum":35}
    http://192.168.1.15:9926/sensors/airgradient:d024a3/measures
    301
    <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
    <html><head>
    <title>301 Moved Permanently</title>
    </head><body>
    <h1>Moved Permanently</h1>
    <p>The document has moved <a href="http://192.168.0.9:9925/sensors/airgradient:d024a3/measures/">here</a>.</p>
    <hr>
    <address>Apache/2.4.51 (Debian) Server at 192.168.0.9 Port 9925</address>
    </body></html>
    

    My guess is that this has to do with the changes done in the last pull request to support multiple airgradient devices, but that the docker run command needs to be updated. Is this accurate? How could I go into debugging my issue?

    opened by ArnaudKunzi 3
  • Metric names

    Metric names

    Awesome project 🙂 Just that it (others too, like shelly exporter) doesn't seem to follow a naming convention when it comes to the metrics.

    Is this something you would care about? This could be checked in CI via promtool if needed.

    stale 
    opened by cityofships 3
  • OLED burn-in potential?

    OLED burn-in potential?

    I noticed that you seem to be using a single page to display all the data at once in the sensor package's OLED screen in your ESPHone sketch.

    I wanted to do the same thing, but ended up going with a rotation of pages (one for each sensor), similar to what you had in your original sketch, due to fears of OLED burn-in of the static elements/labels.

    Do you feel those fears are unwarranted or overblown? If so, I'd love to have a single page like yours!

    opened by m-reiner 1
  • Added function to convertTemps

    Added function to convertTemps

    This pull request provides a unified location to handle the conversion between temperature units. Currently, the metrics endpoint remained in Celcius even if the display showed Fahrenheit by user choice.

    It's been a while since I've used C++, so let me know if the standards for naming things are off, but it seems like functions and variables are Lower Camel Case, while Types (classes, structs) are Upper Camel Case.

    See: https://google.github.io/styleguide/cppguide.html

    opened by MCDELTAT 0
  • Consistent screen update frequency if any sensors disabled

    Consistent screen update frequency if any sensors disabled

    I noticed that if any of the sensor flags are disabled then the screen no longer displays each stat page for the same period. I've fixed this by introducing a loop so it can fall back to the next sensor type if one or more are disabled.

    Also, thanks for sharing your Prometheus support here, works great!

    opened by NathanBaulch 2
  • Some improvements to AirGradient-DIY.ino

    Some improvements to AirGradient-DIY.ino

    • use preprocessor variable to reduce binary size
    • add optional display
    • reduce sensor polling due to global caching
    • auto update values on display if forced update from webserver occured
    • independend sensor update and display change intervall
    • fix time problem between displayed values intervalls on display due to disabled sensors
    opened by Cutyno 14
  • Device becomes unreachable after a while

    Device becomes unreachable after a while

    First of all, thanks for everyone's efforts around this sketch.

    The issue I've encountered is that while things seem to be working fine for a while, at some point the device stops responding to network queries, such as a ping or the curl directed to get the metrics. Sometimes this happens after an hour or two of normal operations, but other times as soon as 10-15min after the device was powered on. At the same time, sensor values are being updated on the screen, so this only seems to affect the networking side of things. One additional interesting aspect is that when the device is in this unreachable state and I look at the router's client list, it is still listed as connected.

    This is my first Arduino project so I'm not quite sure how I would go about troubleshooting this issue.

    bug 
    opened by pheetr 18
Owner
Jeff Geerling
Father, author, developer, maker. Sometimes called "an inflammatory enigma". #stl #drupal #ansible #k8s #raspberrypi #crohns
Jeff Geerling
Grafana/Prometheus exporter for EPEVER/EPSOLAR Tracer solar charge controllers

epever_exporter epever_exporter is a standalone program written in C that can query Epever/Epsolar Tracer solar charge controllers and output metrics

Cédric Félizard 1 Nov 23, 2021
Get air quality & CO2 data from SM300D2 & Senseair S8 with ESP32, and export as OpenMetrics (Prometheus exporter) via WiFi.

ESP Air Sensor Get air quality & CO2 data from SM300D2 & Senseair S8 with ESP32, and export as OpenMetrics (Prometheus exporter) via WiFi. I used to h

Shell Chen 4 Feb 6, 2022
A fork of Wraith Cyborg, the Call of Duty: Online Asset Exporter

NOTE: No support is provided for this, it was updated for a friend who works on a CoD OL Mod and it working for him was all that mattered, it is provi

Philip 6 Jan 26, 2022
Prometheus exporter for PostgreSQL

pgexporter pgexporter is a Prometheus exporter for PostgreSQL. pgexporter will connect to one or more PostgreSQL instances and let you monitor their o

null 18 Oct 2, 2022
prometheus exporter using workflow HTTP server

wfprometheus This is a light prometheus exporter using workflow HTTP server. This project is currently in the development stage, and the first version

Sogou Open Source 9 Oct 23, 2021
Prometheus exporter for ARM® Hardware components using HWCPipe.

ARM® HWCPipe Exporter ARM® HWCPipe Exporter is a Prometheus exporter written in Java and C++ that retrieves metrics from Android devices running on AR

Jinesi Yelizati 4 Oct 5, 2022
Grafana/Prometheus exporter for EPEVER/EPSOLAR Tracer solar charge controllers

epever_exporter epever_exporter is a standalone program written in C that can query Epever/Epsolar Tracer solar charge controllers and output metrics

Cédric Félizard 1 Nov 23, 2021
Get air quality & CO2 data from SM300D2 & Senseair S8 with ESP32, and export as OpenMetrics (Prometheus exporter) via WiFi.

ESP Air Sensor Get air quality & CO2 data from SM300D2 & Senseair S8 with ESP32, and export as OpenMetrics (Prometheus exporter) via WiFi. I used to h

Shell Chen 4 Feb 6, 2022
prometheus exporter using workflow HTTP server

wf-prometheus This is a light prometheus exporter using workflow HTTP server. This project is currently in the development stage, and the first versio

C++ Workflow Project and Ecosystem 9 Oct 23, 2021
AirGradient Pushing to InfluxDB or MQTT

Custom AirGradient Sketch AirGradient’s sensor set up is a good base and reasonably affordable. This Arduino sketch allows you to integrate with a loc

null 8 Apr 6, 2022
🎨 Modern 2D/3D - Importer • Exporter • Util - Library, also called (AssetIO)

Brand-new modern 3D asset importer, exporter library. This library will include common 3D utils funcs. It is written with C99 but C++ wrappers or othe

Recep Aslantas 160 Nov 22, 2022
🎨 Modern 2D/3D - Importer • Exporter • Util - Library, also called (AssetIO)

Brand-new modern 3D asset importer, exporter library. This library will include common 3D utils funcs. It is written with C99 but C++ wrappers or othe

Recep Aslantas 160 Nov 22, 2022
A fork of Wraith Cyborg, the Call of Duty: Online Asset Exporter

NOTE: No support is provided for this, it was updated for a friend who works on a CoD OL Mod and it working for him was all that mattered, it is provi

Philip 6 Jan 26, 2022