Creating an ESP8266 Based Wireless Thermometer
Tags: iot, embedded, influxdb, telegraf
Published: 2019-12-16 18:58:51

I've been taking a look at InluxData's TICK stack and figuring out some ESP8266 chips that I recently purchased. I always like to come up with practical projects to do when I'm learning about some new tech. When my dad came to me asking me to keep an eye on the temperature of his house while he was out of town, I knew exactly what I needed to build. It got even better when he told me his plan.

`"I've got an old thermometer that I'll duct tape to the window. That way you can come by a couple times a day and you only have to walk up to the window to see the temperature."`

I had better idea in mind.

## The Thermometer

### Hardware

The obvious part of this project is the thermometer itself. I had the smaller circuit components lying around from a previous project that I reused. If I planned on using this for longer, I would upgrade these components to something like a [TMP36](https://www.adafruit.com/product/165) or an environment sensor to get multiple measurements. The actual components I used are listed below.

- [ESP-01 wireless transceiver](https://www.amazon.com/gp/product/B01EA3UJJ4/ref=ppx_yo_dt_b_asin_title_o04_s00?ie=UTF8&psc=1)
- [ADS1050 12-bit ADC](https://www.amazon.com/gp/product/B00KLBTXME/ref=ppx_yo_dt_b_asin_title_o01_s00?ie=UTF8&psc=1)
- 10K Ohm resistor
- Thermistor

I also highly recommend a [programming adapter](https://www.amazon.com/gp/product/B07KF119YB/ref=ppx_yo_dt_b_asin_title_o02_s00?ie=UTF8&psc=1) and [breadboard adapter](https://www.amazon.com/gp/product/B01G6H46UK/ref=ppx_yo_dt_b_asin_title_o00_s00?ie=UTF8&psc=1) for the ESP-01. They make working with the ESP-01 much more enjoyable. I went a while where I was having to rewire the board every time I switched between programming and running the board. It gets old pretty fast.

### Wiring

![Wiring Schematic](https://thepracticaldev.s3.amazonaws.com/i/3ur4saoz79j7sozr1sao.jpeg)

The schematic shows how each component should be wired. I'll comment on a few of my decisions below.

#### Thermistor

To read from the thermistor, it will need to be in a voltage divider circuit. My thermistor read ~10k ohms at room temperature so I paired it with a 10k ohm resistor to give me a good range of values.

#### ADS1015

The ADDR pin makes it convenient to change the device address by wiring it to `VCC`, `GND`, `TX`, or `RX`. I chose to ground mine since this gives it the default address of `0x48`. If I had more than one I2C device then I would want to consider a different address. I didn't use the `ALRT` pin or three of the ADC inputs and so I left these pins disconnected.

#### ESP-01

The ESP-01 gave me the most trouble wiring up. It seems that everyone is doing it a little different depending on where you look online. There also seems to be a few different versions of the ESP-01 board.

IO2 always needs to be pulled high to boot correctly. IO0 needs to be pulled high on boot to run the program from flash. If it is pulled low on boot then the board will be put into programming mode. Once the board is running, these pins can be used otherwise, such as the I2C pins used above.

### Software

The ESP8266 has become so popular for programming that it is now supported by the Arduino IDE. An additional package will need to be installed but afterwards it is just like working with any Arduino board.

The following code is based on the Arduino example to send a POST from the ESP-01. I modified the code to use the ADS1015 library to read an ADC value and convert it to a temperature. I reused the lookup table from the previous project the thermistor is from. It is not as precise as I would've hoped but since I am reusing old parts I was glad to get it within a reasonable range.

```C++
#include <ESP8266HTTPClient.h>
#include <ESP8266WiFi.h>
#include <Wire.h>
#include <Adafruit_ADS1015.h>

Adafruit_ADS1015 ads1015;

#define SDA_PIN 0
#define SCL_PIN 2

#define SSID "ssid"
#define PASSWORD "password"

#define MESSAGE "conditions,location=dads temperature="
#define DESTINATION "http://192.168.1.30:8080/temp"

void setup() {
  Serial.begin(9600);

  WiFi.begin(SSID, PASSWORD);
  while (WiFi.status() != WL_CONNECTED) {
    // Wait for the WiFI connection completion
    delay(500);
    Serial.println("Waiting for connection");
  }
  Serial.println("Connected");

  // GPIO pins must be outputs to use with I2C
  pinMode(SDA_PIN, OUTPUT);
  pinMode(SCL_PIN, OUTPUT);
  ads1015.begin();
  Wire.begin(SDA_PIN, SCL_PIN);
}

void loop() {
  float temp = getTemp();
  String message = MESSAGE + String(temp);

    if (WiFi.status() == WL_CONNECTED) {
      HTTPClient http;

      http.begin(DESTINATION);  //Specify request destination
      http.addHeader("Content-Type", "text/plain"); //Specify content-type header

      int httpCode = http.POST(message); //Send the request
      String payload = http.getString(); //Get the response payload

      Serial.println(httpCode); //Print HTTP return code
      Serial.println(payload);  //Print request response payload

      http.end();
    }
    else {
      Serial.println("Error in WiFi connection");
    }

  delay(1000);
}

float getTemp() {
    // Create a table to quickly look up temp values
    static float tempTable[20][2] = {{11.6,     70},
                                    {11.4,  71},
                                    {11.1,  72},
                                    {10.8,  73},
                                    {10.6,  74},
                                    {10.3,  75},
                                    {10.1,  76},
                                    {9.8, 77},
                                    {9.6, 78},
                                    {9.4, 79},
                                    {9.2, 80},
                                    {8.9, 81},
                                    {8.7, 82},
                                    {8.5, 83},
                                    {8.3, 84},
                                    {8.1, 85},
                                    {7.9, 86},
                                    {7.7, 87},
                                    {7.6, 88},
                                    {7.4, 89}};

    int16_t adc = ads1015.readADC_SingleEnded(0);

    // Find the voltage
    float voltage =  ((1024-adc)*3.3)/1024;

    // Find the resistance of the thermistor
    float Rt = ((voltage * 10000)/3.3)/(1 - (voltage/3.3));

    // Adjust our precision to match the lookup tables
    Rt = (float)((int) Rt/100)/10;

    // Find the associated temp in the table
    int i = 19;
    float currTemp = tempTable[19][1];
    for(; i>=0; --i) {
        // Grab the value as long as Rt is lower then the table's R
        if(tempTable[i][0] <= Rt)
            currTemp = tempTable[i][1];
    }
    return currTemp;
}
```

## Pushing to the Cloud

The next step is a location to store the temperature readings. This is where the TICK comes in t play. I wanted somewhere that I could access from anywhere so I opted for InfluxDB Cloud. I'm planning on setting up an instance of InfluxDB locally but for this project the cloud was more convenient and the free tier was more than enough.

After creating an account with [InfluxData](https://www.influxdata.com), there is a little bit of setup to be able to store data from the device.

### Creating a Bucket

The first area to visit is the buckets list. Navigate to `Load Data -> Buckets` on the left panel. Select the blue `+ Create Bucket` button in the top right and fill in the necessary options. This bucket will hold all of the thermometer data.


![Buckets Page](https://thepracticaldev.s3.amazonaws.com/i/w9klslvmdsclryyprixf.jpeg)

### Creating a Token

The next page to visit the tokens list. Navigate to `Load Data -> Tokens` on the left panel. Select the blue `+ Generate` button and `Read/Write Token` from the dropdown menu. Enter a name for the token and choose your newly created bucket from the listed buckets. After saving the Token, click its name from the descriptions list and copy the token to be used later.

![Tokens Page](https://thepracticaldev.s3.amazonaws.com/i/okcmdydhoop9uh030kke.jpeg)

### Viewing Data

There are two more areas worth noting at this time. The first of these is the `Data Explorer` tab. This tab provides a convenient way to view the data being logged by devices. Nothing will exist at the moment since we don't have a middle device logging from the thermometer but it's worth knowing where to look later.

If a view of the data is created that you would like to save, clicking the `Save As` button in the top right will create a copy of the view on the `Dashboards` page. The `Dashboards` page allows you to create a number of convenient graphs that are easy to view without having to search each time you log in.

![Explorer Page](https://thepracticaldev.s3.amazonaws.com/i/d3psgoakqf29lwgimg4n.jpeg)

## Gathering the Measurements

The final component to setup is a fog device to receive metrics from the thermometer. I went with a Raspberry Pi W that I had lying around. Having a dedicated metrics device makes it easy to move the whole setup around. I could also set up multiple thermometers and have them all report to one device that pushes to the cloud.

I started with a fresh copy of Raspbian lite with SSH enabled and WiFi configured. The remaining steps involve installing telegraf and configuring it for the use with the thermometer.

### Installing Telegraf

The telegraf project has very good [documentation](https://docs.influxdata.com/telegraf/v1.12/introduction/installation/) for installing telegraf and having is run at boot. These are the commands I ran specifically for the Pi.

Add telegraf to the package manager:

```bash
sudo apt-get update && sudo apt-get install apt-transport-https

wget -qO- https://repos.influxdata.com/influxdb.key | sudo apt-key add -
source /etc/os-release
test $VERSION_ID = "10" && echo "deb https://repos.influxdata.com/debian buster sta
```

Install and start telegraf. This will install telegraf as a service that restarts on boot.

```bash
sudo apt-get update && sudo apt-get install telegraf
sudo service telegraf start
```

### Configuring Telegraf

The final step is creating a configuration file for telegraf. The default location for the telegraf config file is `/etc/telegraf/telegraf.conf`. Telegraf can be used to generate a new configuration file with the plugins that we will need for this project.

```bash
telegraf --input-filter http_listener_v2 --output-filter influxdb_v2 config > telegraf.conf
```

This will create the initial template for the config file. There are a few sections that will need to be modified for our setup.

```toml
[[inputs.http_listener_v2]]
  service_address = ":8080"
  path = "/temp"
  methods = ["POST", "PUT"]
  data_format = "influx"
```

This lets telegraf know that it should watch the `/temp` path on port `8080` for `POST` or `PUT` requests. It can expect the data it receives at this location to be using the influx line protocol format.

```toml
[[outputs.influxdb_v2]]
  urls = ["https://us-west-2-1.aws.cloud2.influxdata.com"]
  token = "<your token>"
  organization = "<your organization>"
  bucket = "<your bucket>"
```

The information for the output section will depend on how influx cloud was set up. The organization name is the name used when creating your account on InfluxDB Cloud. This can be found on the landing page of the cloud UI. The token and bucket values that were previously chosen when creating a bucket on InfluxDB Cloud. These can also be found by going to `Load Data -> Telegraf` and clicking the purple `InfluxDB Output Plugin`. This will assume the token is set using an `$INFLUX_TOKEN` environment variable.

That should be the last of the needed configuration for the Raspberry Pi. A reboot will start telegraf running on the Pi with the new configuration settings.

## Monitoring the Results

We can now return to the `Data Explorer` in InfluxDB Cloud and we will now be able to select data from `thermometer`. We will need to select one type of filter for the data to be displayed. I used the temperature field but it's worth trying different options until the graph represents what you like. Finally, clicking the submit button will update the graph to show the logged metrics.

![Temperature Dashboard](https://thepracticaldev.s3.amazonaws.com/i/eudv9dxxsqnpuk7v2n5w.jpeg)

## Conclusion

I now have a much more convenient way of keeping an eye on my dad's house while he is away. Time to compare it with his idea and see which thermometer is more accurate. Something tells me that his window thermometer might be a little on the cold side.