AirStation enviromental sensor – reloaded

AirStation 2.0 passed 180h testing.

now: 6/6 sensors online for “Weinheim, DE” in a Grafana-Dashboard

pre-assembly: Geiger J321 tube + logic (l), ESP8266 (c), BM280 (ct), SDS011 (r), ZE25-O3 (rb)
AirStation dashboard (Grafana) showing daily aggregated data

Following an initial version, I wanted to learn and extend from this experience:

  • Ozone sensor employed so far was a Winsen MQ131 analog sensor, sampled via a voltage divider/ADC circuit. The sensor required extensive burn-in and base resistance calibration and appeared to be critically sensitive to moisture – far beyond what was charted in the MQ131 datasheet’s figure 4: Rs/Rs0 vs. Temperature plot for 3 distinct values of rH%. While I was able to derive a continuous correction function(rH%,T) by interpolating between the graphs provided by the datasheet, this eventually did not resolve the humidity issue. From analysis of environmental data collected so far, MQ131 “runaways” in O3 reading coincided nicely with early morning humidity values rH > 65%. Looks like we have to abandon this cheap thing and go for Winsen’s digital sensor: ZE25-O3. Fortunately, AliExpress was quick to deliver !
  • AirStation’s central library “myTaskSchedulder” is controlling the asynchronous scheduling of various data sampling tasks and pre/post processing requirements and has seen multiple updates and fixes inspired by the ongoing StratoExplorer project which equally benefits from the lib’s capabilities.
  • While O3 and particulate matter are already quite rare for privately operated sensors, I wanted to add this one more thing that would make it unique of it’s kind: γ/β radiation ambient dose rate, i.e. a Geiger-Mueller tube counter.

AirStation 2.0 specs after success lab testing:

Implementation notes:

ESP8266 is ideally suited for WiFi based IoT applications thanks to multiple, configurable digital ports. However, simultaneously connecting that many digital applications to a single device is creating issues due to subtle limitations on the CPU’s pins, i.e. interrupt capability, logic levels, behavior during device boot and flashing.

Ozone: ZE25-O3 connected via (Software-)Serial (9600, 8N1). Sensor has two modes that can be selected via serial: i. periodic reports, ii. on-demand reports. The latter mode appears handy but has a documented issue with ESP8266 software-serial. That’s why we operate in (default) periodic mode and invest a bit in parsing queued messages from the serial buffer. Since we are anyhow running out of hardware-serials on ESP8266 (and wanted to keep Serial-0 for bench-diagnostics), sw-serial is a good choice, because it’s ESP8266 implementation is able to handle multiple connections (at low baud rate). With choosing periodic-mode, we can use RX on pin D6 and abandon host TX, thus releasing one interrupt-capable digital pin D5 for the radiation sensor count trigger. Digital O3 readout is in ppb units, conversion to µg/m³ applied taking into account temperature correction according to EU standards. Sampling every 10 minutes for 1 minute at 2s intervals.

Particulate Matter (PM): SDS011 connected via (Software-)Serial (9600, 8N1). Needs both RX/TX to read data from device and switch on/off fan and laser. Warm up time 30s, 2 minutes sampling at 2s intervals, triggered every 3 hours: commanded by myScheduler. Caution: From available pins D7/D8 use D7 for RX. D8 is pulled to GND, boot fails if HIGH. Humidity correction applied according to Zbyszek Kiliański.

Radiation: Interrupt controlled, Geiger-Mueller tube J321 identified as J305/M4011 compatible. Count-per-minute / dose [µS/h] conversion factor: 0.00655. Connected to ESP D5 with an ISR which handles event counting independently from any other tasks. Rate computation + statistics controlled via myTaskScheduler: every 10 minutes for 5 minutes of event data.

BME280 THP: Connected via standard I2C bus SCL/SCA (D1/D2). Continuous sampling every 2s on a 5 minutes sliding average.

Code sample showing basic AS2.0 design:

main.cpp:

[..]

#include <myWifi.h>
#include <AirStation.h>
#include <AirStation_cfg.h>

#include <SensorPM.h>
#include <SensorO3.h>
#include <SensorTHP.h>
#include <SensorRAD.h>

[..]

// myTaskScheduler is given a reference time: SysTime
// SysTime is initialized with reference to wifi's getTimestamp()
// (This works equally well myGPS lib, providing sat-based time)
//
auto wifi = myWifi(CBN_HOME);
auto st   = new SysTime(std::bind(&myWifi::getTimestamp, wifi));
auto ts   = new myTaskScheduler(st);
          
[..]

//
// SYSTEM SETUP
//

void setup() {

  Serial.begin(28800); 
  Serial.println("AirStation - setup:");
 
  // Tasks being registered with myTaskScheduler
  // Custom task must inherit from a generic Task class
  //
  ts->addTask("THP", new SensorTHP)->setOnError(RECOVER);
  ts->addTask("PM" , new SensorPM )->setOnError(RECOVER);
  ts->addTask("O3" , new SensorO3 )->setOnError(RECOVER);
  ts->addTask("RAD", new SensorRAD)->setOnError(RECOVER);

  ts->setPrintLevel(DEBUG_LEVEL);

  wifi.start();

  // some configuration options for SysTime
  st->setTimeformat(&formatAirStationTimestamp);
  st->setSyncInterval(12*3600);
  st->autoTimeformat(true);
  st->setSyncMode(AUTO);

  [..]

  bool status = ts->resetTasks() && ts->testTasks();
  Serial.printf("SETUP: %s\n", status ? "OK":"FAILED");

}

//
// MAIN LOOP
//
void loop() {
  
  server.handleClient();  // handle webservice requests

  ts->run();              // have myTaskSchedule do its scheduling business
  st->checksync();        // check sync with time reference

}

Next steps:

Finally assembly caused unexpected harm to the O3 digital sensor: When the sensor was mounted into a short plastic tube connecting it with the atmosphere outside the box, solvent-containing plastic glue was used. While drying, intense hydrocarbon vapor however “blinded” the sensor beyond recovery – a fact that was clearly pointed out in the sensor’s data sheet: “Do not leave the module in a high concentration of organic gas for long time which will cause the sensor zero point to drift and recover slowly” – I told myself: r.t.f.m, next time I will study every tiny detail of hardware documentation ! A replacement sensor is on it’s way from China.

Edit 25.08.2022: The new sensor has arrived from China and was tested ok. But, getting stable O3 readings is a challenge ! Two reasons for this: 1) 4-sensors setup: It appears that AirStation – once booted – performs flawlessly to process data from all sensors. However, with co-existence of radiation and ozone sensors (and the increase in wiring and EMI), there is an issue with ESP8266 boot mode due to “floating” pins which require the RST button to be pressed to properly initiate boot. 2) The Winsen ZE25-O3 is not only very sensitive to gas concentration but also to any airflow impacting the cover membrane of the device. Isolating the chip in a tube connected to a hole in the AirStation box turned out to be a bad idea in “outdoor” conditions. Any air movement flowing alongside or into the opening is inducing pressure oscillations which are being magnified by the sensor. Therefore it is more appropriate to locate the sensor protected within the box and rely on “smooth” air exchange induced by regular runs of the PM sensor fan.

Edit 14.09.2022: Winsen ZE25-O3 sensor has been relocated inside the box and EMI issues have been fixed. AirStation now running on ALL sensors: temperature, humidity, pressure, particulate matter, ozone, ambient radiation. This has required a downtime on Sep 14 resulting in a gap in the data recordings.

良药苦口 – good medicine tastes bitter

In the middle of a global crisis focused on the current developments in Ukraine, the vast ocean of social media is rapidly populated by massive shoals of fish swimming in various directions, occasionally swallowed by trawling (read: trolling) sharks. To stay in calm waters, I chose to sketch my view on the tiny island of my personal website – comments welcome via LinkedIn or contact email !

I was born in Germany and identify myself as a European citizen. This is based on my perception of a common European cultural and ethical heritage, i.e. a peaceful co-existence of distinct people and common values of freedom. Having traveled extensively throughout the world, I have learned about different cultures and ethics in both geographically and politically distant countries. While I am – not without difficulties – able to identify myself with nations very much distinct from my own background, I understand that close listening and mutual respect is definitively a good basis to develop friendship and prosperity.

Presently, one is witnessing an outcry to more closely stand together and defend the values of Europe, embodied by the European Union acting as a political organization. I do think that the process of identification and re-alignment of values is very much desirable and vital to the existence and development of any organization. But I admit I do have my issues when it comes to the development and justification of some of its policies and actions:

Defending values routinely seems to translated into a bellicose scheme of identifying “other” values and interests as adverse or hostile and to implement defense as a sole show of (military or economical) force towards the alleged power of the enemy.

To make it crisp and clear, I condemn the invasion of Ukraine by the Russian Federation and there is simply no excuse to not immediately stop this war and to enter into a multilateral diplomatic process of negotiations. Now the house is on fire, it is first priority to put out the flames, then enter into (1) post-fire investigation and the implementation of (2) preventive actions.

Regarding these two actions I am of the opinion that the view has to be considerably enlarged from the narrow perspective of blaming “apparent” ideology or mad man’s action towards a deeper look into the cultural and historical background and developments leading up to the fatal spark igniting a war: Together with culture and history of a nation closely come its identity and pride – but sometimes also humiliation and pain. It is crucial to understand that a dissent or hostile attitude perceived and criticized by one party is frequently rooted in fear or abasement felt by the other side. All these are ingredients to a chemical cocktail that is susceptible to ignite – in contact with a catalyst, which can be as focused as a single political leader. It is important to independently attempt to defuse both sides: the flammable cocktail and its catalyst.

In this post I am aiming at prevention “beyond Russia” and I would like to focus on the cocktail rather than the catalyst.

In several aspects the international curriculum of Russia recalls parallels with China. In their long history, not only both countries experienced periods of impressive geo-political and economic influence, but also had to suffer – for prolonged periods of time – from decay of their glory due to economic and political factors acting from both the inside as well as under foreign pressure. Understandably nations are striving to preserve or restore their self-confidence, identity and global influence. To the present point in time, however, both nations achieved very different levels of development which can be analyzed objectively, i.e. economic prosperity (domestic / trade), civil rights, freedom of press, involvement in amicable international relationships.

With Russia currently being forced into deep political and economic isolation, I regrettably see many people in Europe anxiously looking at China to embody a sort of upcoming “Russia 2.0” which could serve as justification for adopting a rather contentious position in EU – China relationships. In the early days of the Ukraine war, I was initially pleased to see (on 03 March 2022) French president Macron, German chancellor Scholz and Chinese president Xi to engage in a joint video call.

Not being divided by ocean waters and by sharing the same Eurasian land mass bridging from Europe to Asia, I suggest that the current situation should be regarded by both European and Chinese leaders as a unique and huge opportunity to invest into furthering mutual understanding of the historic cocktail and to attempt to speak out and align cultural values and economic interests. “Trade leads to Peace” has been stated as early as by the ancient Greek philosopher Plutarch and since has been repeatedly reflected in EU foreign policies. Of course, I hear people instantly reminding that fair trade might be in danger dealing with a giant China – but c’mon Europe, stop lamenting and boost innovation and competition in lieu of regulation ! China has undertaken a giant and long term effort to re-energize the ancient Silk Road land-based trade routes to Central Europe, rolling out their “Belt & Road Initiative” (one belt one road: 一带一路), facing geo-graphic/political challenges to circumnavigate Caspian and Black Sea waters via either Iran/Turkey or Russia. Eventually, both sides – Europe and China – have a common and vital interest in seeing Russia stabilize and prosper among them in respectful co-existence. Frankly, I think it’s more of a duty and responsibility to the world than a mere opportunity.

Coming back to the title of my post, I believe that debugging the world shall put considerably more effort on coordinated initiatives involving both Europe and China and that this process will be sort of a bitter pill to swallow for people so far exclusively focused on transatlantic security and trade policies.

BirdyIO goes Grafana

while using Grafana to put my compute center under comprehensive monitoring and alerting, I played with connecting the BirdyIO event database with a custom dashboard dynamically shared on this blog entry… cool stuff, very useful for other projects, such as: AirStation and StratoExplorer.

live data:

focus on 2021 nesting season:

Below we focus specifically on the 2021 spring/summer season. One can easily distinguish initial nest-“scouting” activity, followed by active nesting (with predominantly directed in/out traffic) in May 2020. For this activity, bird species was visually identified as: great tit (Parus major). The nesting ended sharply with the youngsters moving out and adding a terminal peak of non-directed activity. Interestingly, in June 2020 there was a secondary wave of serious in/out traffic, without visible signs of true nesting activity. Perhaps this is to be attributed to food search and insect having accumulated inside the birdhouse ?

we have not yet received any bookings on BirdyBnB this year, but looking forward to hosting new guests !