Tim's blah blah blah

Calibrating esp8266 temperature sensors

Self-heating is a problem for temperateure sensing with ESP8266 boards. While these boards consume ~1W, depending on the setup this can lead to several degrees self heating. Here I describe how I calibrate my self-heating boards against a thermally optimized sensor and a COTS evohome radiator thermostat.

Setup

I developed two PCBs to link together ESP8266 boards with CO2/T/RH/P sensors, however these suffer from self-heating.

Calibration

Get reference time & values

HATOKEN=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiI0NjRiNjVmYWQwYzE0N2YwOTcyNzY4MTY2ODZjZDY0MiIsImlhdCI6MTYzMjA0ODcwMywiZXhwIjoxOTQ3NDA4NzAzfQ.KgkMcrXo9W1tZO5S3b1MyANO0mleCX7E2mebCjCETPA
HAURL=http://proteus.lan:8123/api
REFSENSOR1=climate.study_guest
REFSENSOR2=climate.study_guest
TGTSENSOR1=sensor.sensorthing_jgijsen1_bme280_temperature
TGTSENSOR2=sensor.sensorthing_jgijsen1_ds18b20_temperature
TGTSENSOR3=sensor.sensorthing_jgijsen1_mh_z19_temperature

# Start & end time of calibration measurement
STARTTIME=2021-09-23T08:00:00+0200
ENDTIME=2099-12-31T00:00:00

curl -X GET -H "Authorization: Bearer ${HATOKEN}" \
        -H "Content-Type: application/json" \
        "${HAURL}/history/period/${STARTTIME}?end_time=${ENDTIME}&filter_entity_id=${REFSENSOR1}" | \
        jq -r '.[0][] | "\(.attributes.current_temperature),\(.last_updated)"' > refsensor1.csv
curl -X GET -H "Authorization: Bearer ${HATOKEN}" \
        -H "Content-Type: application/json" \
        "${HAURL}/history/period/${STARTTIME}?end_time=${ENDTIME}&filter_entity_id=${REFSENSOR2}" | \
        jq -r '.[0][] | "\(.attributes.current_temperature),\(.last_updated)"' > refsensor2.csv


curl -X GET -H "Authorization: Bearer ${HATOKEN}" \
        -H "Content-Type: application/json" \
        "${HAURL}/history/period/${STARTTIME}?end_time=${ENDTIME}&filter_entity_id=${TGTSENSOR1}" | \
        jq -r '.[0][] | "\(.state),\(.last_updated)"' > tgtsensor1.csv

curl -X GET -H "Authorization: Bearer ${HATOKEN}" \
        -H "Content-Type: application/json" \
        "${HAURL}/history/period/${STARTTIME}?end_time=${ENDTIME}&filter_entity_id=${TGTSENSOR2}" | \
        jq -r '.[0][] | "\(.state),\(.last_updated)"' > tgtsensor2.csv

curl -X GET -H "Authorization: Bearer ${HATOKEN}" \
        -H "Content-Type: application/json" \
        "${HAURL}/history/period/${STARTTIME}?end_time=${ENDTIME}&filter_entity_id=${TGTSENSOR3}" | \
        jq -r '.[0][] | "\(.state),\(.last_updated)"' > tgtsensor3.csv

Make time-weighted average

iptyhon3-3.8

import pandas
import matplotlib.pyplot as plt

# Load data, parse 'unavailable' as NA
# (Alternatively, use np.isreal() https://stackoverflow.com/questions/21771133/finding-non-numeric-rows-in-dataframe-in-pandas)
refsensor1 = pandas.read_csv('refsensor1.csv', parse_dates=True, index_col='DateTime', 
                                names=['T', 'DateTime'], header=None, sep=',',
                                na_values='unavailable')
tgtsensor1 = pandas.read_csv('tgtsensor1.csv', parse_dates=True, index_col='DateTime', 
                                names=['T', 'DateTime'], header=None, sep=',',
                                na_values='unavailable')
tgtsensor2 = pandas.read_csv('tgtsensor2.csv', parse_dates=True, index_col='DateTime', 
                                names=['T', 'DateTime'], header=None, sep=',',
                                na_values='unavailable')
tgtsensor3 = pandas.read_csv('tgtsensor3.csv', parse_dates=True, index_col='DateTime', 
                                names=['T', 'DateTime'], header=None, sep=',',
                                na_values='unavailable')

# Show data
plt.figure(100)
plt.clf()

plt.plot(target_bme280, 'g-', )
plt.plot(target_ds18b20, 'b-', )
plt.plot(target_mh_z19, 'r-', )
plt.plot(reference, 'k-')

# Time select useful window (chi by eye). Ignore if using all data
tmin = '2021-09-21 17:00:00'
tmax = '2099-09-21 17:00:00'

reference = reference[(reference.index>tmin) & (reference.index<tmax)]
target_bme280 = target_bme280[(target_bme280.index>tmin) & (target_bme280.index<tmax)]
target_ds18b20 = target_ds18b20[(target_ds18b20.index>tmin) & (target_ds18b20.index<tmax)]
target_mh_z19 = target_mh_z19[(target_mh_z19.index>tmin) & (target_mh_z19.index<tmax)]

# Downsample to 15 minute buckets
target_ds18b20_down = target_ds18b20.resample('15T').mean()
target_bme280_down = target_bme280.resample('15T').mean()
target_mh_z19_down = target_mh_z19.resample('15T').mean()
reference_down = reference.resample('15T').mean()

# Get common timeframe 
tmin=max(reference_down.index.min(), target_bme280_down.index.min(), target_ds18b20_down.index.min())
tmax=min(reference_down.index.max(), target_bme280_down.index.max(), target_ds18b20_down.index.max())

reference_down = reference_down[(reference_down.index>tmin) & (reference_down.index<tmax)]
target_bme280_down = target_bme280_down[(target_bme280_down.index>tmin) & (target_bme280_down.index<tmax)]
target_ds18b20_down = target_ds18b20_down[(target_ds18b20_down.index>tmin) & (target_ds18b20_down.index<tmax)]
target_mh_z19_down = target_mh_z19_down[(target_mh_z19_down.index>tmin) & (target_mh_z19_down.index<tmax)]


# Plot data
plt.figure(110)
plt.clf()

plt.plot(target_bme280, 'g-', )
plt.plot(target_bme280_down, 'g--')

plt.plot(target_ds18b20, 'b-', )
plt.plot(target_ds18b20_down, 'b--')

plt.plot(target_mh_z19, 'r-', )
plt.plot(target_mh_z19_down, 'r--')

plt.plot(reference, 'k-')
plt.plot(reference_down, 'k--')

# Plot correlation / coefficient
plt.figure(120)
plt.clf()
plt.plot(reference_down.values, target_ds18b20_down.values, 'go')
plt.plot(reference_down.values, target_bme280_down.values, 'bo')
plt.plot(reference_down.values, target_mh_z19_down.values, 'ro')

# Get difference to calculate

bme280off = (target_bme280_down.interpolate('linear') - reference_down.interpolate('linear')).mean()
ds18b20off = (target_ds18b20_down.interpolate('linear') - reference_down.interpolate('linear')).mean()
mh_z19off = (target_mh_z19_down.interpolate('linear') - reference_down.interpolate('linear')).mean()


plt.figure(130)
plt.clf()

plt.plot(target_bme280 - bme280off, 'g-', )
plt.plot(target_bme280_down - bme280off, 'g--')

plt.plot(target_ds18b20 - ds18b20off, 'b-', )
plt.plot(target_ds18b20_down - ds18b20off, 'b--')

plt.plot(target_mh_z19 - mh_z19off, 'r-', )
plt.plot(target_mh_z19_down - mh_z19off, 'r--')

plt.plot(reference, 'k-')
plt.plot(reference_down, 'k--')

#smarthome #esp8266