Now let's go over the code that runs on the sensor. The code checks the temperature and humidity, formats it, then publishes directly to the MQTT server.

MQTT Secrets Settings

Since the code publishes directly to the MQTT server, there are a few more secret settings that the code expects to find. If your MQTT server has no username and password, you can change the value to None, however in general, the Home Assistant MQTT broker is setup to be password protected by default.

MQTT_BROKER = "192.168.1.1"
MQTT_PORT = 1883
MQTT_USERNAME = "myusername"
MQTT_PASSWORD = "mypassword"

Full Code Listing

# SPDX-FileCopyrightText: 2021 Melissa LeBlanc-Williams for Adafruit Industries
#
# SPDX-License-Identifier: MIT

"""
SHTC3 Temperature/Humidity Sensor Example for
using CircuitPython with Home Assistant
Author: Melissa LeBlanc-Williams for Adafruit Industries
"""

import os
import time
import ssl
import json
import alarm
import board
import socketpool
import wifi
import adafruit_minimqtt.adafruit_minimqtt as MQTT
import adafruit_shtc3

PUBLISH_DELAY = 60
MQTT_TOPIC = "state/temp-sensor"
USE_DEEP_SLEEP = True

# Connect to the Sensor
i2c = board.I2C()  # uses board.SCL and board.SDA
# i2c = board.STEMMA_I2C()  # For using the built-in STEMMA QT connector on a microcontroller
sht = adafruit_shtc3.SHTC3(i2c)

wifi.radio.connect(os.getenv("CIRCUITPY_WIFI_SSID"), os.getenv("CIRCUITPY_WIFI_PASSWORD"))
print("Connected to %s!" % os.getenv("CIRCUITPY_WIFI_SSID"))

# Create a socket pool
pool = socketpool.SocketPool(wifi.radio)

# Set up a MiniMQTT Client
mqtt_client = MQTT.MQTT(
    broker=os.getenv("MQTT_BROKER"),
    port=os.getenv("MQTT_PORT"),
    username=os.getenv("MQTT_USERNAME"),
    password=os.getenv("MQTT_PASSWORD"),
    socket_pool=pool,
    ssl_context=ssl.create_default_context(),
)

print("Attempting to connect to %s" % mqtt_client.broker)
mqtt_client.connect()

while True:
    temperature, relative_humidity = sht.measurements

    output = {
        "temperature": temperature,
        "humidity": relative_humidity,
    }

    print("Publishing to %s" % MQTT_TOPIC)
    mqtt_client.publish(MQTT_TOPIC, json.dumps(output))

    if USE_DEEP_SLEEP:
        mqtt_client.disconnect()
        pause = alarm.time.TimeAlarm(monotonic_time=time.monotonic() + PUBLISH_DELAY)
        alarm.exit_and_deep_sleep_until_alarms(pause)
    else:
        last_update = time.monotonic()
        while time.monotonic() < last_update + PUBLISH_DELAY:
            mqtt_client.loop()

How the Code Works

First we start with our imports. Many of the imports are ESP32-S2 specific because of the built Wi-Fi functionality, but this list also includes json, adafruit_minimqtt, and adafruit_shtc3, which we'll go over later.

import os
import time
import ssl
import json
import alarm
import board
import socketpool
import wifi
import adafruit_minimqtt.adafruit_minimqtt as MQTT
import adafruit_shtc3

In the next section, there are a few settings that you can adjust.

First, the PUBLISH_DELAY setting is the amount of time in seconds to wait before updating the temperature and humidity.

The MQTT_TOPIC is the topic that is published on the MQTT server. To read more about MQTT Topics, you can check out the MQTT Topics section of our All the Internet of Things Protocols guide.

The USE_DEEP_SLEEP setting defines how we want to wait until we publish. If the setting is True, it uses Deep Sleep to stop execution, put the board into a low power mode, and then restart the script after a certain amount of time, so it will need a little additional time to reconnect to WiFi. If the setting is False, it will just wait the amount of time in PUBLISH_DELAY and the publish again. You can read more about the Deep Sleep feature in our Deep Sleep with CircuitPython guide.

If you plan to modify the code to respond to MQTT requests, you will want to have USE_DEEP_SLEEP set to false. However, setting up an MQTT subscription will not be covered in this guide because Home Assistant won't be polling for the Temperature/Humidity sensor.

PUBLISH_DELAY = 60
MQTT_TOPIC = "state/temp-sensor"
USE_DEEP_SLEEP = True

The next line, we initialize to the sensor by passing in the I2C bus. 

# Connect to the Sensor
sht = adafruit_shtc3.SHTC3(board.I2C())

Now that the code has secrets, it uses that to connect to the WiFi access point and create a socket pool. Sockets are how CircuitPython establishes communication over the internet.

wifi.radio.connect(secrets["ssid"], secrets["password"])
print("Connected to %s!" % secrets["ssid"])

# Create a socket pool
pool = socketpool.SocketPool(wifi.radio)

The MQTT library is initialized next using the settings in the secrets file and the socket pool. Once it is initialized, the code attempts to connect to the MQTT server.

# Set up a MiniMQTT Client
mqtt_client = MQTT.MQTT(
    broker=os.getenv("MQTT_BROKER"),
    port=os.getenv("MQTT_PORT"),
    username=os.getenv("MQTT_USERNAME"),
    password=os.getenv("MQTT_PASSWORD"),
    socket_pool=pool,
    ssl_context=ssl.create_default_context(),
)

print("Attempting to connect to %s" % mqtt_client.broker)
mqtt_client.connect()

Now we get to the main loop. The loop will really only come into play if USE_DEEP_SLEEP is False which we'll explain in the next section.

First we grab our temperature and humidity from the sensor. Then we create a dict that will hold the structure for our JSON output. We are adding a temperature and humidity settings to the dict. Finally we use the json.dumps() function to convert the dict structure to a JSON string.

temperature, relative_humidity = sht.measurements

output = {
    "temperature": temperature,
    "humidity": relative_humidity,
}

print("Publishing to %s" % MQTT_TOPIC)
mqtt_client.publish(MQTT_TOPIC, json.dumps(output))

This last section is all about waiting until it is time to publish the temperature and humidity again.

If USE_DEEP_SLEEP is True, then we create an alarm to tell the program to stop running and restart from the beginning after a certain amount of time.

Otherwise, we'll make use of the loop. The last_update is set to time.monotonic(), which is a running counter that keeps the relative time and is useful for measuring elapsed time. Then a simple while loop is used to wait until it is time to publish again. Inside this loop, the mqtt_client.loop() function is called which is useful if you plan on having the code respond to a subscription.

if USE_DEEP_SLEEP:
    mqtt_client.disconnect()
    pause = alarm.time.TimeAlarm(monotonic_time=time.monotonic() + PUBLISH_DELAY)
    alarm.exit_and_deep_sleep_until_alarms(pause)
else:
    last_update = time.monotonic()
    while time.monotonic() < last_update + PUBLISH_DELAY:
        mqtt_client.loop()

Debugging the Sensor

If you would like to monitor what the sensor is doing, you can look at our guide on Connecting to the Serial Console with CircuitPython. Once you are connected, it can help with any troubleshooting.

Using Other Sensors

If you would like to use other Temperature and Humidity sensors, then you can modify the code to do so. You'll need to change the import line to your sensor and a simple test example is usually included with each library for the sensor that allows you to get the values that you need.

Once you have the values, you can just plug them into the dict and the rest should just work.

This guide was first published on Feb 03, 2021. It was last updated on Dec 11, 2023.

This page (Code the Sensor) was last updated on Dec 11, 2023.

Text editor powered by tinymce.