Tempeh Tech

Temperature Tests


By Noël Jung


Content

  1. Intro
  2. Imports
  3. Code
  4. Conclusion

Intro

During the fermentation, I want to use two temperature DS18B20 sensors . One will be inside the tempeh, while the other will be placed elsewhere in the incubator. This allows me to get an idea about whether the fermentation itself produces heat or whether it just retains the heat from the incubator.
Since the fermentation goes on for several days, I must make sure that the RaspberryPi does not heat up too much. I therefor installed heat sinks that I glued onto the CPU and RAM.
I conducted a test run with the temperature sensors. They both stayed in the same glass of water for several hours. The goal was to see whether they would approximately measure the same temperature. During that time, I also monitored the Pi's CPU temperature to confirm that it stays constantly under a critical level.

Imports

In [4]:
import math
from datetime import datetime
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import graph_style # Contains styling parameters for the plots.

I wrote the data into a csv file. Via the parse_dates and date_parser arguments we can specify which columns pandas should treat as timestamps. I also transform the date into a more readable format (HH:MM:SS).

In [5]:
def time_parser(time_stamp):
    """ Specifies which format the timestamp in the input file has."""
    return pd.to_datetime(time_stamp, format='%H%M%S')

# Load data, specify which column contains time info.
CSV_FILE = r"../CSV/20230416_1110_comp.csv"
temp_test_data = pd.read_csv(CSV_FILE, parse_dates=["Time"], date_parser=time_parser)

# Transform time column into a readable format.
temp_test_data["Time"] = temp_test_data["Time"].dt.strftime('%H:%M:%S')

Furthermore, I think it is good to know how much time has passed since the experiment has started. Using the DataFrame.apply method we call a function that calculates the difference in minutes between the current, and the first time value in minutes for each row.

In [6]:
def time_dif(t1, t2, resolution="min", input_format="%H:%M:%S"):
    """ Returns differences between two time stamps."""
    absl_time_1 = datetime.strptime(t1, input_format)
    absl_time_2 = datetime.strptime(t2, input_format)
    diff_in_secs = (absl_time_2 - absl_time_1).total_seconds()

    if resolution == "min":
        return  int(divmod(diff_in_secs, 60)[0])

    return diff_in_secs

# Create column containing the time passed from start of experiment in min.
temp_test_data["min_passed"] = temp_test_data.apply(lambda row: time_dif(temp_test_data.iloc[0]["Time"],
    row["Time"],input_format='%H:%M:%S' ), axis=1)

Let's take a look at the DataFrame.

In [7]:
temp_test_data.head(3)
Out[7]:
Time name_dev_1 T_dev_1 name_dev_2 T_dev_2 T_CPU min_passed
0 11:10:48 28-3c01f09505ec 20.562 28-3c01f0953253 20.437 33.6 0
1 11:11:50 28-3c01f09505ec 20.562 28-3c01f0953253 20.562 34.0 1
2 11:12:52 28-3c01f09505ec 20.625 28-3c01f0953253 20.625 36.0 2

In our new column, we can easily see how long the measurement has gone on for. I had two temperatures sensors connected at the same time and was also monitoring the temperature of the CPU to see whether the heat sink I had installed had any effect and to estimate whether the Pi would get hot over long run times. I also saved the name of the temperature sensors as I read them from the Pi. I was afraid that the order of which I detect the devices and read their values could change. Let's quickly confirm that the name_dev_1 and name_dev_2 always contain the same value.

In [8]:
devices_column_1 = temp_test_data["name_dev_1"].unique()
devices_column_2 = temp_test_data["name_dev_2"].unique()
print(f'Values sensor 1: {devices_column_1}. Values sensor 2: {devices_column_2}.')
Values sensor 1: ['28-3c01f09505ec']. Values sensor 2: ['28-3c01f0953253'].

Code

Let's first take a look at how the CPU temperature changes over time.
In [9]:
def floor_timestamp(timestamp, old_format="%H:%M:%S", new_format="%H:%M"):
    """Rounds down (!) to minutes."""
    absl_time = datetime.strptime(timestamp, old_format)
    return  absl_time.strftime(new_format)

def space_time_label(ticks, n_ticks=50, ceil_to_min=False):
    """Takes a list of ticks and reduces it."""
    ith_of_n = int((math.floor(len(ticks)/n_ticks)))
    reduced_ticks = ticks[::ith_of_n] # Retrieve every ith tick.

    if ceil_to_min:
        return list(map(lambda time: floor_timestamp(time), reduced_ticks))

    return list(reduced_ticks)

# Set theme for graphs.
axes_style = sns.axes_style(style=graph_style.style)
sns.set(rc={"figure.figsize":(10, 4), })
sns.set_theme(style=axes_style, palette=graph_style.colors_pomegranate ,font=graph_style.FONT)

# Space y-labels, select y-ticks (every 10 values of the min_passed column).
spaced_out_ticks = space_time_label(temp_test_data["Time"], 10)

# Initialize graphs.
T_CPU_plot = sns.lineplot(x=temp_test_data["min_passed"],
    y=temp_test_data["T_CPU"], **graph_style.lineplot_kwargs)

# Apply styles.
T_CPU_plot.set_title(label="CPU temperature over time", **graph_style.title_style)
T_CPU_plot.set_xlabel(xlabel="Time in minutes", **graph_style.axes_style)
T_CPU_plot.set_ylabel(ylabel="Temperature in °C", **graph_style.axes_style)
T_CPU_plot.tick_params( **graph_style.tick_style)
T_CPU_plot.set(xlim=(0,576), ylim=(30, 50));
After booting up, the Pi's CPU temperature first increases but then stays stable between 40 and 50 °C.
Likewise, we can plot the temperatures that the two sensors measured over the day. For testing, I put them in a glass of water.
In [10]:
# Rearrange dataframe from wide to long form (seaborn requirement).
long_T_dev_df = temp_test_data.melt(id_vars=["min_passed"],
    value_vars=["T_dev_1", "T_dev_2"], var_name="dev_nr", value_name="T_in_C" )

# Initialize graphs.
T_dev_plot = sns.lineplot(data=long_T_dev_df, y="T_in_C", x="min_passed", hue="dev_nr", **graph_style.lineplot_kwargs)

# Apply legend styles.
handles, labels = T_dev_plot.get_legend_handles_labels()
legend_title = T_dev_plot.legend(title="Thermometer", handles=handles,  labels=["1", "2",], **graph_style.legend_style)
plt.setp(legend_title.get_title(), **graph_style.legend_title_style)

# Apply graph styles.
T_dev_plot.set_title(label="Measured temperatures by two devices", **graph_style.title_style)
T_dev_plot.set_xlabel(xlabel="Time in minutes", **graph_style.axes_style)
T_dev_plot.set_ylabel(ylabel="Temperature in °C", **graph_style.axes_style)
T_dev_plot.tick_params( **graph_style.tick_style)
T_dev_plot.set(xlim=(0,576), ylim=(20, 35));

In the beginning, I poured warm water into the glass. Later, at around minute 200, I added a bit of boiling water. These are the two spikes seen in the graph. Both sensors seem to show approximately the same temperature (the lines lie on top of each other). We can also confirm that by looking at the numbers.

In [11]:
T_diff = temp_test_data.apply(lambda row : abs(row["T_dev_1"] - row["T_dev_2"]), axis=1)
print(f"The temperature difference is maximum {T_diff.max()} and on average {T_diff.mean():.2f} °C.")
The temperature difference is maximum 0.25 and on average 0.14 °C.

Conclusion

The CPU temperature does not exceed 50 °C. This is low enough and an improvement to before the installation of the heat sinks.
Both sensors measured virtually same temperatures. Before conducting the test, I decided that anything below 3 °C difference in the measurements would be acceptable. Hence, the actual differences, maximum 0.25 °C, are much better than expected.