Cheap Home Air Quality Monitoring

I’ve wanted to monitor room temperature and humidity in my home for quite some time and I recently came across the ESP8266 Wi-Fi microcontrollers that were ideal for the job. Prior to the ESP8266 becoming mainstream, I was considering running a 1wire network over spare phone wires, similar to this weather station setup.


Thankfully, the ESP8266 chips are cheap enough that fitting one in each room of a house is practical. On top of that, people had already made inroads to getting code running on the ESP, including a webserver returning readings from a DHT22 temperature and humidity sensor.

The ESP8266 chip itself includes a Tensilica Diamond Standard core, although documentation is a little sparse. Fortunately, setting up a toolchain is relatively straight forward, and there is a (proprietary) SDK available that allows custom firmware to be built and flashed onto the modules.


The hardware I threw together is very simple, and hooks up a DS18B20 1wire temperature sensor and a DHT11 temperature and humidity sensor up to the two GPIOs hooked up to pins on the module. Since I wanted a handful of the the boards, I opted to design the circuit in Fritzing and got a pack of boards from DirtyPCBs. The schematic and board are below:


Temp Mon PCB

The schematic is straight-forward; 5V comes in from a USB socket and goes through a 250mA fuse and gets dropped to 3.3V by a TS1086CZ or AMS1117 module. The switch S1 allows the GPIO pin to be grounded to program the module, with the UART pins broken out to a separate header.

Software – ESP8266

The software is quite simple. Using sprite_tm’s webserver code and building on Martin’s additions for the DHT sensor, my code adds a DS18B20 driver and support for the cheaper DHT11 sensor.

In addition to serving the sensor values over HTTP, the code periodically sends a JSON packet over UDP to a logging host (a Raspberry Pi) which stores it in a MySQL database and an rrdtool database.

I noticed after a few days of running, the ESP boards can get quite warm. Although I don’t think it affected the sensors, it seemed sensible to avoid leaving the ESP8266 online if it only needed to periodically log data. Fortunately, there is a “deep sleep” function in the SDK, but unfortunately this requires the RESET pin to be soldered to the RTC pin to trigger wakeups. With the chips spending most of the time asleep, they don’t get warm at all.

Software – Logging

A python script listens on the UDP port for incoming packets, deserialises them, and logs them. The readings from the DHT sensor are susceptible to noise (possibly a bug in the driver), so the script includes logic to reject samples that are outside a window.

RRDTool expects updates for every value at once, so it’s not possible to use a single RRD to store the data from multiple hosts as it comes in at different times. Instead, each host needs its own RRD, so I use a script to create them as I add hosts to the network.

Software – Visualising

I initially used a Shiny app to load the data from the SQL database, but this became unworkable quite quickly; without a lot of spare RAM, MySQL resorts to creating temporary tables on the SD card which is tragically slow.

Shiny Dashboard

For quick at-a-glance readouts, I switched to RRDTool. Although it’s not as pretty as ggplot2, it’s lightweight enough to render graphs regularly and serve them as static images, which fits far more comfortably on the Raspberry pi. The script just iterates over each host and plots it for various intervals, saving the images to a webserver’s directory.

Bill of Materials & Cost

  • PCBs: $14/12 – £0.78ea
  • USB B Connector: £2.51/5 – £0.50ea
  • Fuse holder: £1.29/10 – £0.13ea
  • Fuse: ~£0.10ea
  • ESP8266: £8.36/4 – £2.09ea
  • DS18B20: £4.99/5 – £1.00ea
  • DHT11: £3.04/5 – £0.61ea
  • AMS1117 module: £2.62/5 – £0.52ea
  • Switch: £1.99/5 – £0.40ea
  • 4.7kR resistor: 2x~£0.02ea
  • Project box: £3.36/5 – £0.67ea

That works out at approximately £6.84 (amortising the cost of the PCBs), or £7.93 each for the 5 nodes I built. I’m ignoring the cost of power supplies as I have at least one USB power source and cable in the rooms I’m monitoring. Given there are more GPIOs on board to the ESP8266, there’s also scope for adding more sensors to them later on.


Reading ROMs and Saving Pokemon

Early Game Boy cartridges that save games use battery-backed SRAM cartridges to hold data between plays. It’s recently arisen that the batteries in them are expected to start dying soon. Critically: a lot of Pokemon from a lot of childhoods are facing extinction.

Luckily, thanks to the Pandocs [1] and a lot of patience from other developers, people have started building their own dumpers. This post details another implementation, built for PIC16 devices instead of the existing Arduino and AVR implementations. It is heavily based on the InsideGadgets Arduino GBCartRead project [2].

There's a ROM dumper under this rat's nest, honest.

The Hardware

I built my dumper around a PIC16F690. It’s got just enough pins to drive a couple of shift registers, a UART and to leave enough pins left over for I/O. The most difficult part to get is the cartridge connector – a donor Game Boy is required unfortunately.

Schematic for the dumper

The schematic is fairly straightforward. The PIC communicates with the PC via a MAX232. The address lines are driven via two 74HC594 shift registers. Data and control pins take up the remaining pins. Power is supplied via a LM7805 linear regulator (not shown).

The cartridge requires a connection to GND and +5V, as well as a pull-up resistor from +5V to the /RST pin (30)

The Software

Game Boy cartridges are fairly simple devices. They use a 32-pin connector which consists of power, three control lines, a 16-bit address bus and an 8-bit data bus. The cartridge shares the address bus with other devices and is mapped to the region 0x0000 to 0x7FFFF [3].

Talking to a cartridge is fairly simple:

  1. Write the address you want to read or write to the address bus
  2. (optional) Write the data you want to write to the data bus
  3. (optional) Raise MREQ if you’re reading SRAM (Pokemon live there)
  4. Raise RD or WR to read or write respectively
  5. Drop RD, WR and MREQ
  6. (optional) Read the data from the address bus

The WR, RD and MREQ pins are all active high.

Cartridges larger 16KB require bank switching which is handled by  a device called the memory bank controller (MBC). The MBC also controls access to the SRAM, as well as any other peripherals on the cartridge.

The MBC is controlled by writing commands to specific addresses. These addresses vary between MBCs, although most seem to enable RAM when 0xA0 is written to 0x0000. The MBC type is readable from the cartridge descriptor which is at 0x0134-0x0148 [1].

The firmware for the PIC is available at GitHub [4].

Rescuing the Pokemon

To use the dumper just connect to it via the serial port. The interface is fairly straight forward. It waits for confirmation before reading the descriptor, it’s pretty straight forward from there. The most important mode to use first is the diagnostics mode, which needs to be run without a cartridge connected.

On the Importance of Testing

Continuity testing is important!

The diagnostics mode is ideal for catching faults before they have real implications.

Faults like this one. In this case the WR pin hadn’t been properly soldered. Since the pins are active low and they’re connected via pull-down resistors. That means that the WR signal was 1, so the attempt to read RAM ended up writing garbage to it. I euthanised my Pokemon.

Dumping Data

Retrieving data (hopefully having not destroyed it) can be done using RealTerm [5]. It features true raw logging which is necessary, as, for example, PuTTY interprets some control characters, even in raw logging mode.

Future Work

At the moment the interface is a bit clunky, as is having to use RealTerm to dump things. I’ll be changing it to speak the same protocol as the original Arduino GBCartRead [2] which will make dumping things easier.


[1] : The Pandocs – Everything you wanted to know about Game Boy but were afraid to ask

[2] : GBCartRead

[3] : Game Boy memory map

[4] : GBCartRead-PIC16

[5] : RealTerm