Introduction: AtmoScan

About: Full time father and husband, engineer at heart, lifelong tinkerer in my spare time



Go to my GitHub for:

- Some small hardware changes improve the design, including the ability to turn off itself from software, remediating one of the biggest drawbacks of the design - how to handle low battery.

- A PCB v2 design is now published together with a guide to easily apply the change to boards V1.0.

- CAD files for complete enclosure

The new enclosure looks like the picture above... well, without the rubber band


ATMOSCAN is a multisensor device aimed at monitoring indoor air quality.
While many projects have been published that have similar purpose, this one is a complete system in a compact, self-contained package that summarises them all. It has an LCD color display, it is time & location aware, it is gesture controlled and it posts to ThingSpeak (or others) via MQTT, but can properly handle disconnected operations and reconnection. With its embedded rechargeable battery it lasts a full day when disconnected from power.

It uses a multitasking cooperative framework and is very responsive to user input while sampling sensors, handling UI, posting to MQTT. In fact it squeezes quite a bit out of the tiny ESP8266. It does so by integrating a number of open source libraries and leveraging internet web services.

Credits for libraries go to a number of contributors, see later.

Music in video can be found HERE

Step 1: Sensors

Atmoscan measures a number of variables:

  • Temperature
  • Humidity
  • Pressure
  • CO2
  • CO
  • NO2
  • VOC (Volatile organic compounds, an Air Quality indicator)
  • PM 01
  • PM25
  • PM10
  • Radiation

To do so it integrates a number of discrete sensors

  • BME280 (e.g. Link)
  • PMS7003 (e.g. Link)
  • MH-Z19 (e.g. Link)
  • HDC1080 (e.g. Link)
  • MiCS6814 (Link)
  • MP503 (Link)
  • LND-712 Geiger tube (Link, i found it in Europe here Link or here Link) with high voltage module (Link)

Data Sheets are HERE.

Step 2: Electronics

Atmoscan can be easily built with a NodeMCU or any other ESP8266 board and some readily available components, such as level shifters and voltage regulators, if you give up on the integrated battery charger.

While I did prototype with separate components, for the final version I designed a specific board that integrates all functions and provides neat connectors for sensors, LEDs for status (Blue= power supply connected; Red = charging).

Eagle PCB files available HERE.

Specifically, the board integrates:

  • Charging circuitry based on MAX8903A (Link)
  • One-button on/off logic
  • ESP12E module
  • Programming logic
  • Level shifter
  • LCD Backlight driver
  • 3.3V Step-Up/Step-Down Voltage Regulator based on Pololu S7V8F3 (Link)
  • 5V Step-Up Voltage Regulator based on Pololu U1V10F5 (Link)
  • LiPo Fuel Gauge based on SparkFun TOL10617 (Link)

The display is a 2.8" TFT 320x240 based on an ILI9341 chip (Link).

The gesture sensor is based on the PAJ7620U2 chip (Link), way better than the cheap APDS9960 that generate continuous interrupts and cannot work through plexiglas.

The sensors are rather power hungry, so to guarantee at least 24h autonomy I made a pack with 3 x 5000mAh LiPo 105575 batteries (Link). In fact, 2 could have been enough. The MAX8903 charger struggles to charge the resulting 15,000mAh pack.


  • Connectors positions are shown
  • The SD card slot needs to be desoldered from the display if you want it to fit in the enclosure
  • You need to make a small notch in the PCB in order not to interfere with the fan (notch are in fashion after iPhone X). Corrected in PCB V2

Connectors abbreviations on PCB are as follows:

  • PRS : Barometric Pressure Sensor (based on BME280) NOTE: to be mounted directly on the PCB
  • VOC : Grove - Air quality sensor v1.3 (based on MP503)
  • TMP : High Accuracy Digital Humidity and Temperature Sensor (based on HDC1080)
  • PMS : PMS7003 Digital particle concentration sensor
  • GAS : Grove - Multichannel Gas Sensor (based on MiCS6814)
  • GES : Grove – Gesture sensor(based on PAJ7620U2)
  • RAD : Geiger tube (via High Voltage Geiger Probe Driver Power Supply Module 400V / 500V with TTL Digitized Pulse Output)
  • CO2 : MH-Z19 infrared CO2 gas sensor
  • U1V10F : 5V Step-Up Voltage Regulator based on Pololu
  • U1V10F5 S7V8V3 : 3.3V Step-Up/Step-Down Voltage Regulator based on Pololu S7V8F3
  • TOL10617 : Sparkfun LiPo Fuel Gauge
  • LCD : ILI9341 display

Step 3: Enclosure

The enclosure is derived from a plexiglas 10x10x10 cm cube container that i bought on ebay and was meant for an entirely different use. It had nice ventilation slots that were exactly what was needed. The volume was in principle sufficient to pack the whole set, except that it wasn't easy... some early attempts based on cardboards mockups failed miserably so I gave up and wasted some hours with a 3D CAD and I had the internal supports laser cut. The internal space is divided in compartments so that the temperature sensor is as far away as possible from internal heat sources. While the external enclosure is made of 3mm material, the top is made of 2+1mm sheets. This trick allowed having the gesture sensor covered with only 1mm acrylic and this is sufficient to make it work.

Some modifications had to be done with hand tools on the original enclosure, such as the fan, switch and USB holes. The result was nevertheless decent!

CAD files are HERE.

Step 4: Mechanical Assembly

The package is very dense but thanks to the 3D cad design I had few surprises when assembling it.

Air circulation (from top to bottom) is ensured by a small fan. After buying a fair number on Aliexpress / eBay, i realised that the noise of cheap fans was unbearable for an indoor device. I ended up buying a rather expensive, slow turning Papst 255M (Link) and I fed it with less than 5V via a couple of diodes. The result is rather good and is silent enough to be unnoticed (it is even wife-approved, the hardest certification).

Step 5: Software

The software architecture is based on an Object Oriented framework that runs multiple (cooperative) processes that handle UI, sensors and MQTT. It is location and time-aware but can handle disconnection / reconnection to WiFI.

The framework is open and can manage any number of screens, as long as their code and resources fit in Flash memory. The application framework handles the gestures and passes it on to the screens, for further handling or cancellation if needed. Gestures managed by the framework are:

  • Swipe left / right - Change screen
  • (Finger) Clockwise swirl - Turn screen
  • (Finger) Counterclockwise swirl - Invoke setup screen
  • (Hand) From far to close - Turn off display

Screens are inheriting from a base class and are managed via the following event model:

  • activate - fired once, when screen is create
  • update - called periodically to update the screen
  • deactivate - called once, before screen is dismissed
  • onUserEvent - called when gesture sensor is triggered. Allows to respond and also override the default event handling, e.g. abort swipe to change screen

Each screen declares its capabilities by providing the following information:

  • getRefreshPeriod - how often the screen needs refresh
  • getRefreshWithScreenOff - if the screen wants to be refreshed even when backlit is off. e.g. for charts
  • getScreenName - name of the screen
  • isFullScreen - take full control of the display, or allow the top bar with date/time/location/battery gauge/wifi gauge

The framework is able to instantiate and deallocate the screens via a declarative class factory. The dynamic allocation saves RAM and makes the device easily expandable. The overall application framework is also reusable for other projects.

Screens currently implemented in Atmoscan are:

  • Sensors values
  • Geiger meter / semilog chart
  • System status
  • Error log
  • Weather Station
  • Plane Spotter
  • Setup
  • Low battery

The Setup screens allow setting Wifi credentials, MQTT channels, Syslog server.

NEW in v2.0: all web services keys are now configurable via the configuration portal. The only value that is still hardcoded is the OTA password (uppercase ATMOSCAN).

NOTE 1: First programming must be done with a USB-Serial cable connected to the programming connector. As the serial port is occupied by a sensor, debugging and programming that way is impractical after assembly as it would require detaching the sensor. Therefore the software supports SYSLOG debugging and OTA updates.

NOTE 2: The ATMOSCAN binary is over 700Kb and ArduinoOTA requires the program space to be at least twice the image size, which rules out the "4M (3M SPIFFS)" option. However, the standard "4M (1M SPIFFS)" option is also unsuitable as the SPIFFS partition would be insufficient for the graphical resources related to weather station, plane spotter and for the confing file. Therefore a custom configuration "4M (2M SPIFFS)" has been created to solve the issue. Explanation here.

Documentation and full source code are available here.


  • Adafruit
  • Arcao
  • Bblanchon
  • Bodmer
  • ClosedCube
  • Gmag11
  • Knolleary
  • Lucadentella
  • Seeed
  • Squix78
  • Tzapu
  • Wizard97



Step 6: Make It Better!

Result is not bad at all! Software looks good and is reliable, while it could be expanded with new features and perhaps cleaned up a bit to make the application framework truly reusable for other projects. Calibration of some sensors is not great, but test lab equipment would be needed. Time is precious and I don't have much, so progress was slow. By the time I was done, decent support for the ESP32 became available. If I started it now, I would use it and integrate external sensors via bluetooth.


NOTE: I still have a handful of PCBs so if anyone is interested they are available at nominal / postage price.

Step 7: Questions & Answers

First of all, THANK YOU for your overwhelmingly positive comments. I honestly did not expect that much interest.

I received a number of questions either via comments or private messages, so I thought of collecting the answers here. Should more come, I will add.

I found in the back of a drawer the 8 available PCBs - and they are on their way to Belgium, Germany, India, USA, Canada, UK, Australia. Wow, 3 continents! Amazing.

What shall I put in the ATMOSCAN configuration page?

The Atmoscan configuration page requires the following parameters:

  • SSID and password of the WiFi network you want it to connect to
  • MQTT server you use. For example, I use
  • Connection string for MQTT topics used. For example, Thingspeak MQTT topics are in the format: channels/CHANNEL-ID/publish/WRITE-API (EXAMPLE: channels/123456/publish/567890)
  • Syslog server: the IP of the syslog server you use for logging
  • Google key for Maps Static API. Get a key from Create a project; The API that Atmoscan uses is . Create a key for this API on the google project you just created, use it here
  • Weather Underground key. Create an account on , go to WEATHER API (link in the bottom of home page, go to KEY SETTINGS, generate a key, use it here
  • Geonames account. Create an account on enable it to use the free web services and put the username here
  • TimeZoneDB key. Create an account on, create a key, put it here

How do I configure Thingspeak?

You need 3 Thingspeak channels. Fields are used as follows:

CHANNEL 1 fields

  4. PM01
  5. PM2.5
  6. PM10
  7. CPM

CHANNEL 2 fields

  1. CO
  2. CO2
  3. NO2
  4. VOC

CHANNEL 3 fields (System channel)



  5. LINEAR SOC (BATTERY STATE OF CHARGE % - linear calculation, proportional to voltage)

  6. NATIVE SOC (BATTERY STATE OF CHARGE % - as reported by gauge. as read from the gauge. NOTE: the gauge says 0% when reaching 3.6v while the batteries can be discharged a bit further, lets say above 3v. The lower limit, at which ATMOSCAN turns itself off, is a #define in globaldefinitions.h file)

  7. SYSTEM TEMPERATURE (from the bme280, mounted directly to the board)

  8. SYSTEM HUMIDITY (from the bme280, mounted directly to the board)

The PCB is very compact. How do I solder the SMD devices, especially the MAX8903A IC?

First, I suggest you ask yourself if you want to get into SMD or if it is a one-off- If the latter, perhaps ask someone to do it for you. If you want to take the SMD challenge, invest a bit and get the proper tools (solder, flux, isopropylic alcohol small iron, hot gun, tweezers, a cheap USB camera, a PCB holder). Nowadays this is cheap stuff. Then watch a YouTube video – there are half a million – and spend some time with an old PCB that you can sacrifice and de-solder / clean / solder some components. You would not believe how instructive this is, to learn what to expect, get temperature right etc. Speaking from experience… I started SMD changing the display connector in an iPod touch and I killed the first one!

Indeed the Atmoscan PCB is compact and that IC is not an easy one. Again, I don’t recommend you do this as your first SMD soldering. The QFN is not a friendly package even though I soldered a number by now. You are never sure you got it right…

On Atmoscan I soldered it first, then its surrounding components so I could test that the charging portion of the board was working, then I completed all the rest. From the pictures attached you should be able to infer the orientation of the components. I used public domain component libraries and the orientation is not very evident in the silkscreen.

My way: I first put some solder on the pads with the iron. Then a lot of flux (SMD specific) and I carefully positioned the IC with tweezers. Then heated up the whole thing to around 200/220C (below melting point) to avoid tensions due to uneven heating. Then I boosted the temperature to 290C or so on and around the IC. If you put a bit of solder on a nearby pad you will see when the temperature is at melting point, as it will shine.

After that I cleaned it with isopropylic alcohol and carefully inspected it with a cheap USB cam. Typical issues are alignment and quantity of solder, as some pins might not be connected. In some cases I had to go back to it with a small soldering iron to add some more solder to some pins, as this IC has a thermal pad underneath that needs to be soldered too. This makes it a bit tricky to guess the amount of solder and it might happen that too much solder underneath might raise it so that the pins do not touch the PCB.

Having said so, I don’t want to scare you. I completed 3 boards and I never killed these ICs… Once I even had to remove it, clean up and restart from scratch but it worked in the end. Again, not super easy but doable.

Where did you buy the components?

Mostly on eBay and Aliexpress. However, the branded ones are original (Seeed, Pololu, Sparkfun).

Some INDICATIVE links follow. Note: look around, you might find even cheaper deals...

First programming
The Atmoscan board includes a programming circuitry that is in line with the NodeMCU. Serial connection is normally used for the first programming. After that, OTA programming via wifi is the preferred option, as it can be done with the unit fully assembled. Don't forget that the serial port is normally used by the particle sensor!

To program the board with serial, a USB-Serial adapter (e.g. FTDI232 or similar) must be connected to the J7 connector (next to the reset button) following the pinout in the schematic. Program can be uploaded without sensors connected, except that the interrupt line of the geiger sensor should be connected to GND, otherwise the board will not boot (to do so, connect pins 1 and 3 in in the RAD connector). The easiest way to test the board without using the main sketch - hence without the complexity of the sensors - is to upload THIS simple program via serial cable. It creates a wifi access point that allow further flashing with the main program.

IMPORTANT: Dont forget to use the 4M/2M SPIFFS configuration as per the instructable, otherwise the main program will not fit. The board must be initialised via serial programming with that configuration, otherwise you might have issues with OTA later.

Unfortunately some sensors initialisation is blocking if sensors are not present (depends on the provider of the library). One example is the multigas sensor library. To make sure Atmoscan boots properly with the full firmware, you could disable the related process, see the related Q&A point. A simple way to disable ALL sensors for testing is to comment out the line #define ENABLE_SENSORS in the GlobalDefinitions.h file.

When the board boots the main sketch for the first time, it should recognise that it is not configured and should open a wifi hotspot, to which you can connect and set it up. Among the settings, there is a syslog server that helps greatly debugging. You can also increase the logging level by uncommenting the #define DEBUG_SYSLOG in the GlobalDefinitions.h file. Please note that in the same file there is also a #define DEBUG_SERIAL that was used during initial debugging. If uncommented it outputs _some_ residual logging, but minimal. A ToDo item was always to make logging uniform and selectable but i never had the time to clean it up.

Have you modified the libraries you used, is there any configuration needed? (as opposed to download & compile)

Good question, I forgot to mention that point. Indeed there are a few mods / configs needed:

As far as I remember that should be it. Let me know if any problems arise.

NOTE: Please refer to comments in the latest source code - contains links to all needed libraries and is kept up to date

Why are some sensors reading red and some green in the video/pictures?

Color indicates trend. It starts white and if going up is red, if going down is green.

How do you handle drift of the sensors over time? How good are these sensors? What can I see with these sensors?

Honestly this is not a scientific measurement kit. To calibrate I would need equipment that I do not have available. This is really a pet project. I tried several sensors. The particle, CO2, temperature, humidity, pressure, Geiger are rather good in my opinion. On the NO2 I have reservations on calibration and overall design, but there is not much available. Overall, they are mainstream sensors.

However, the combination is good enough to show things you would not expect.

With the Atmoscan in the living room and the kitchen a room away, it detects huge peaks of particles when e.g. frying stuff. It feels the NO2 from the morning traffic even with the windows closed.

Was a Geiger counter really necessary? Does it show anything useful?

Luckily we haven't had nuclear incidents and war is not coming yet... Still, there are nuclear plants not that far away and the government distribute iodine pills for kids to be kept in the drawer in case of incidents... so I got suspicious. So far I have to say the readings are exactly in line with the expected background radiation (0.12 uSv/h)

What is the total cost of the device?

I already had many components at home and the links above give you an idea. Honestly, if you buy a ready-made NetAtmo or similar you save money. You can’t beat a Chinese company doing things at scale! However, if you enjoy making perhaps together with your kids, it is worth it. The good part is that I already tested (and discarded) a number of sensors for you....

How about PCBs? Can you sell me one?

I originally had 10 of them made by and my files worked out just fine. Good quality and cheap enough, 25USD / 20Euro for 10 PCBs. I used two and I am happy to send the residual ones for the bare cost (2 Euro + shipment, depending on location and shipping preferences). I am afraid I will have to pick the first ones that send me a private message.

Can you make a kit or a kickstarter campaign?

Flattering, but honestly I never thought it was innovative enough… and besides, NO TIME!!

However, should someone pick up the idea, a second iteration would be needed. There are some sharp edges in the design that would be worth correcting, but again I never had enough time for V2.

On Hardware: Can I add / remove a sensor, the screen etc to expand capabilities / reduce power consumption?

The display is connected without using MISO hence the CPU never reads from the display. Therefore you could just not connect the display ant it would work just fine. Having said so, the display is on only for some time after the last gesture was detected so it is not really impacting power consumption.

The sensors are instead power hungry and the whole thing uses easily 400/500mA. Dont forget the fan and also the fact that the particle sensor also has a built-in fan. The ESP also does not go to sleep mode, due to lack of GPIO pons. However, that would have perhaps saved 20mA…

The software is modular and you can easily add/remove processes and screens so you can add sensors or make it light on power by removing some, if you wish. The only limitation is the number of GPIO pins. However, sensors can be easily added if I2C, or alternatively an I2C expander could be used to add GPIOs...

To disable a sensor, for example to test a partial build, the best way in my opinion would be not to start the related process. This can be accomplished by commenting out the related enable() call in the void startProcesses() function in the main .ino file. Unless you want to structurally modify the system, I would not remove the processes altogether as the screen and MQTT processes will poll them. In this way they should just return zero. Please note that the interrupt input for the geiger board shall be pulled down if unused, otherwise the board will not boot.

What are the improvements you would have made if you had the time for a V2.0?

Not in any particular order..

  • The PCB could avoid copper behind the ESP8266 antenna. I totally forgot it and it makes the radiation diagram non-isotropic
  • The charger in my opinion is undersized for such a large battery / the battery is too large for the charger. There are other ICs and I would try another one.
  • There are better battery gauges.
  • I would add an ozone sensor
  • I would use an ESP32 for more GPIOs and Bluetooth sensors out of the main unit.
  • If I had more GPIOs either with the ESP32 or with an I2C expander I would use one to control the fan and another to power off the unit from software. Now when low battery, the only thing it can do it to display a low battery screen. This is in fact the biggest drawback of the design, as low battery situation is not gracefully handled.

On Software

It took me longer than the hardware... I think it contains a number of good concepts, alas not fully implemented. Specifically, I believe it should be cleaned up, potentially expanded and a generic framework for ESP8266 applications could be easily derived from it. No time. Anyone picking up the challenge?

Can you add Voice control?

Should be feasible. There are a number of ready-made libraries to control an ESP8266 with Alexa and I don’t see why the integration should be a problem. The interesting question is what you want to do with it, functionality wise. I don’t own an Amazon Echo so I never tried.

How did you make the laser cuts?

The drawings are made with SketchUp. The program is nice but seriously lacks exporting capabilities. However, the 30 days trial version helps as it has additional functionality. I then imported it in Inkscape for final processing.

Can you switch on/off sensors to save power, via MOSFETs?

Nice idea in principle, but most of these sensors need to be powered all the time as they have a warm up time. Besides... I run out of GPIOs in the ESP8266. I even had to use GPIO10 that officially is not functional, but does work just fine on the ESP12E.

What skills would I need?

To build it from scratch you would need some electronics design background. Not much really, nowadays with internet you don’t really need to read datasheets line by line as in my early days... If you use the outcome of my experimentation, you need some SMD soldering skills, mechanical skills and some patience.

Is this your first project?

It is my first instructable but not my first project. I tinkered a lot in the past but I really don’t have much time nowadays. I resurrected my rusty skills as I am trying to teach something useful to my kids..! I made a few more projects that I might one day publish..

First Time Author Contest 2018

Participated in the
First Time Author Contest 2018

Epilog Challenge 9

Participated in the
Epilog Challenge 9