Sensor readings as JSON API using an ESP8266 NodeMCU dev kit

I've had some ESP8266 modules & dev kits laying around for quite a while now and a few months ago I started prototyping with them. Thought I'd make a basic example on how to present some sensor readings as a simple json api with only a few lines of code to get you started!

Goal

The goal is simple: a basic http endpoint that returns some json back with sensor readings, like below.

{
  "illuminance": {
    "visible": 905.0,
    "full": 1255.0,
    "ir": 347.0
  },
  "temperature": 24.51,
  "humidity": 37.49,
  "pressure": 1027.82
}

Hardware

First things first, getting the hardware needed! I've gotten everything of eBay for only a couple of bucks. I assume you have a breadboard and some jump wires but I'll link them too, just to be sure.

Wiring everything up

I don't have a schematic or anything, but the wiring is quite simple. Connect the 3.3V of the ESP8266 NodeMCU with the VCCGND with GNDD1 with SCL and D2 with SDA pins of the sensors.

+---------------------------------------------+
| ESP8266 NodeMCU | TSL2561 | BME280 | TMP102 |
|-----------------|---------|--------|--------|
| 3.3V            | VCC     | VCC    | VCC    |
| GND             | GND     | GND    | GND    |
| D1              | SCL     | SCL    | SCL    |
| D2              | SDA     | SDA    | SDA    |
+---------------------------------------------+

Software

I'm not going to go step by step through the code as it is quite straightforward but basically the only thing you really need to do is download the required libraries and extract them here ~/Documents/Arduino/libraries (or wherever it might be on Windows 🤷‍♂️).

Libraries

Program

This is the program, should work pretty much out of the box if everything is wired correctly. Just don't forget to substitute your WiFi credentials in WIFI_SSID and WIFI_PASS. Also my BME280 its address is 0x76, but sometimes it lives on 0x77 as well. You can double-check this using the I2C scanner.

#include <Wire.h>
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <Adafruit_Sensor.h>
#include <TSL2561.h>
#include <Adafruit_BME280.h>
#include <SparkFunTMP102.h>

// Configuration
#define WIFI_SSID ""
#define WIFI_PASS ""
#define WEB_SERVER_PORT 80
#define I2C_ADDR_TSL2561 0x39
#define I2C_ADDR_BME280 0x76
#define I2C_ADDR_TMP102 0x48

// Web server
ESP8266WebServer webServer(WEB_SERVER_PORT);

// Sensors
TSL2561 tsl2561(I2C_ADDR_TSL2561);
Adafruit_BME280 bme280;
TMP102 tmp102(I2C_ADDR_TMP102);

void setup()
{
  // Setup I2C
  Wire.begin(D2, D1);

  // Setup Serial
  Serial.begin(9600);
  Serial.println();

  setupWiFi();
  setupWebServer();
  setupSensors();
}

void loop()
{
  webServer.handleClient();
}

void handleRoot()
{
  Serial.println("[WebServer] Request: /");

  // Read illuminance (lx)
  float visible = tsl2561.getLuminosity(TSL2561_VISIBLE);
  float full = tsl2561.getLuminosity(TSL2561_FULLSPECTRUM);
  float ir = tsl2561.getLuminosity(TSL2561_INFRARED);

  // Read temperature (°C)
  float temperature = readTemperature();

  // Read humidity (%)
  float humidity = bme280.readHumidity();

  // Read pressure (hPa)
  float pressure = bme280.readPressure() / 100;

  // Build response
  String response = "";
  response += "{";
  response += "\"illuminance\":{";
  response += "\"visible\":";
  response += visible;
  response += ",\"full\":";
  response += full;
  response += ",\"ir\":";
  response += ir;
  response += "}";
  response += ",\"temperature\":";
  response += temperature;
  response += ",\"humidity\":";
  response += humidity;
  response += ",\"pressure\":";
  response += pressure;
  response += "}";

  // Send response
  webServer.send(200, "application/json", response);
}

float readTemperature() {
  float bme280Temperature = bme280.readTemperature();
  float tmp102Temperature = tmp102.readTempC();

  return (tmp102Temperature + tmp102Temperature + bme280Temperature) / 3;
}

void setupWiFi()
{
  Serial.println("[WiFi] Setup");
  Serial.print("[WiFi] Connecting to: ");
  Serial.println(WIFI_SSID);

  WiFi.begin(WIFI_SSID, WIFI_PASS);
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(200);
    Serial.print(".");
  }
  Serial.println();

  Serial.println("[WiFi] Connected!");
  Serial.print("[WiFi] IP: ");
  Serial.println(WiFi.localIP());
}

void setupWebServer()
{
  Serial.println("[WebServer] Setup");
  webServer.on("/", handleRoot);

  Serial.println("[WebServer] Starting..");
  webServer.begin();
  Serial.println("[WebServer] Running!");
}

void setupSensors()
{
  setupSensorTSL2561();
  setupSensorBME280();
  setupSensorTMP102();
}

void setupSensorTSL2561()
{
  Serial.println("[TSL2561] Setup");
  Serial.print("[TSL2561] Connecting..");

  while (!tsl2561.begin())
  {
    delay(200);
    Serial.print(".");
  }
  Serial.println();

  Serial.println("[TSL2561] Connected!");
}

void setupSensorBME280()
{
  Serial.println("[BME280] Setup");
  Serial.print("[BME280] Connecting..");

  while (!bme280.begin(I2C_ADDR_BME280))
  {
    delay(200);
    Serial.print(".");
  }
  Serial.println();

  Serial.println("[BME280] Connected!");
}

void setupSensorTMP102()
{
  Serial.println("[TMP102] Setup");
  Serial.println("[TMP102] Connecting..");

  tmp102.begin();

  Serial.println("[TMP102] Assuming connected!");
}

Normally if you now compile & upload the above code to your ESP8266 module it should print out the IP address somewhere on the serial console.

[WiFi] Setup
[WiFi] Connecting to: Wouter's Place
..................................................................
[WiFi] Connected!
[WiFi] IP: 10.10.10.77
[WebServer] Setup
[WebServer] Starting..
[WebServer] Running!
[TSL2561] Setup
[TSL2561] Connecting..
[TSL2561] Connected!
[BME280] Setup
[BME280] Connecting..
[BME280] Connected!
[TMP102] Setup
[TMP102] Connecting..
[TMP102] Assuming connected!

And when making an http request to this ip you should get the sensor readings as json back 😄!

TLDR

You can find the entire project on Github.