Temperature and Humidity monitoring with Arduino

Years ago, I bought an Arduino Uno and an Ethernet Shield to play around with. It was laying in my cabinet, buried underneath many other things. As I am working more and more in my office at home, I wanted to monitor the temperature and humidity in there. Also to check the different “climate zones” in my house, I was curious to see how the temperature in different rooms change over time.

Technology

One day I was looking through the cabinet and found the Arduino and the HanRun HR911105A 17/02 Ethernet Shield (I couldn’t find this one anywhere on the net, therefore I have no link). Looking through some other stuff I had laying around, I came across that educational box I bought with some electrical components. In there was an NTC Thermistor that I could use for mornitoring the temperature. In anotehr box I found a humidity sensor HIH-4030 from SparkFun I totally forgot I had.

I quickly took a perfboard, cut it to size, and soldered the components on to it.

Gathering Data

I decided to provide the data of the sensors via HTTP/JSON with the Arduino. As I already had a RaspberryPi running other stuff, I used it to collect the data and visualize it.

Taking measurments

Temperature

On the Arduino, two measurements are taken. The first is the temperature of the Thermisor:

int thermoPin = A0;

float cal = 6.8;
float offset = 338;

float readTemp() {
  float value = analogRead(thermoPin);
  return ((value - offset) / cal);
}

The raw value of the Thermistor needs to be transformed by a calibration factor and an offset.

Humidity

The second measurement is the humidity, corrected for the temperature:

#include <SparkFun_HIH4030.h>

#define HIH4030_OUT A1
#define HIH4030_SUPPLY 5

HIH4030 sensorSpecs(HIH4030_OUT, HIH4030_SUPPLY);

float humidity = sensorSpecs.getTrueRH(temp);

As I use an off the shelve humidity sensor, there is a Library for the Arduino present, that I just use.

Provide Data via HTTP

I took a good part of the Arduino example for the WebServer. I adapted the code to respond with the correct HTTP Headers and a JSON Body.

void printHttpHeader(EthernetClient client) {
  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: application/json");
  client.println("Connection: close");
  client.println();
}

void printJsonBody(EthernetClient client) {
  Serial.println("Read temperature");
  float temp = readTemp();

  Serial.println("Read humidity");
  float humidity = sensorSpecs.getTrueRH(temp);

  Serial.println("Get current time");
  ntp.update();
  unsigned long epochTime = ntp.getEpochTime();

  Serial.print("Print response to client at ");
  Serial.println(epochTime);
  client.print("{");

  client.print("\"time\":");
  client.print(epochTime);
  client.print(",");

  client.print("\"temp\":");
  client.print(temp);
  client.print(",");

  client.print("\"unit\":\"C\"");
  client.print(",");

  client.print("\"humidity\":");
  client.print(humidity);

  client.print("}");
}

Collect Data using CronJob on RasPi

On the RasPi I used a simple cronjob to collect the data. But it also transforms the JSON data to a CSV row.

* * * * * curl -s 192.168.2.202 | jq -r '.| [.time, .temp, .humidity] | join(",")' >> ~/data/temperature_office.csv

Visualization

To take a look a the data, I put together a small skript in python on the RasPi. It uses dash to plot a simple graph that let’s you switch between the temperature and relative humidity. The pandas library imports the data from the CSV file.

# Import packages
import pandas as pd
import plotly.express as px
from datetime import datetime
from dash import Dash, dcc, html, Output, Input
from pandas import DataFrame
from plotly.graph_objs import Figure

# Initialize the app
app = Dash(__name__)

app.layout = html.Div([
    html.H4('Room climate'),
    dcc.Graph(id="time-series-chart"),
    html.P("Select:"),
    dcc.Dropdown(
        id="data",
        options=["temp", "humidity"],
        value="temp",
        clearable=False,
    ),
])


def convert_ts_to_str(ts: int) -> str:
    return datetime.utcfromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S')


def read_data() -> DataFrame:
    df: DataFrame = pd.read_csv('~/data/temperature_office.csv')
    df["hrtime"] = df["time"].map(convert_ts_to_str)
    return df


def select_scaling(data: str) -> list[int]:
    return [0, 45] if data == "temp" else [0, 100]


@app.callback(
    Output("time-series-chart", "figure"),
    Input("data", "value"))
def display_time_series(data: str) -> Figure:
    df: DataFrame = read_data()
    y_range: list[int] = select_scaling(data)
    fig: Figure = px.scatter(df, x=df.hrtime, y=data, range_y=y_range)
    fig.update_traces(marker=dict(size=2))
    return fig


# Run the app
if __name__ == '__main__':
    app.run_server(host='0.0.0.0', debug=True)

Improvements

Although this works at the moment, there are some fields for improvement.

Ethernet Shield Connection issues

After some time, the connection to the Arduino via Ethernet does not respond anymore. I am going to see, if using the NTP client on the Arduino might cause this issue. Therefore the time is not taken on the Ardunio and sent over in the response, but rather cronjob would add it to the CSV file.

Different Sensors

In this proof of concept I us a very basic thermistor to measure the temperature. But there are lots of different sensors available that also offer for example a I2C interface, or combine the temperature and humidity sensore in one package. One of those could be the Adafruit AHT20

Transfer of Data

To get the data, currently it is pulled peridoically by the RasPi from one Arduino. This could be improved by not using a cronjob, but rather have the Python script running on the RasPi do it. But, as I intend to set up some more of those, it would be nice, if the Arduinos would push their data to the RasPi.

Data storage and visualisation on RasPi

With the intention to use more than one Arduino to push its data to the RasPi, it could be an improvement to store the data in a more sophisticated way. For example it could be stored in something like InfluxDB.

Conclusion

This project was more a proof of concept in the first place. I wanted to answer some questions:

  • How can I make use of the hardware that I have laying around?
  • How difficult or easy is it, to set up so that it is worth having more of those Arduinos to monitor multiple rooms at a time?