Read Temperature with Python and forward to MQTT

Anybody who wants to read the Temperature and Humidity on his RPI 3 GPIO with OH2 installed.

Thanks to the Python Script from debsahu@home-assistan.io

read.py

#!/usr/bin/python

# Import required Python libraries
import paho.mqtt.client as mqtt
import pigpio
import time
import DHT22

# Define GPIO to use on Pi
GPIO_PIR = 23

print "Temperature Module Test (CTRL-C to exit)"

# Set pin as input
pi=pigpio.pi()
s=DHT22.sensor(pi,GPIO_PIR)

lastpubtemp=0.0
lastpubhumi=0.0

def checkBound(new, prev, maxdiff):
  if ((new < (prev - maxdiff)) or (new > (prev + maxdiff)) ):
    result = True
  else:
    result = False
  return result

try:

  #print "  Ready"
  mqttc = mqtt.Client()
  mqttc.connect("192.168.0.111", 1883)
  #mqttc.loop_start()

  # Loop until users quits with CTRL-C
  while True :

    s.trigger()
    #print('{:3.2f}'.format(s.temperature()/1.))
    temp_temp=(s.temperature()/1.)
    #print('{:3.2f}'.format(temp_temp))
    temp_humi=(s.humidity()/1.)
    #print('{:3.2f}'.format(temp_humi))
    #print "T:%3.2f TD:%3.2f H:%3.2f HD:%3.2f" % (temp_temp, abs(lastpubtemp-tem
p_temp), temp_humi, abs(lastpubhumi-temp_humi))
    #if ((abs(lastpubtemp-temp_temp) >= 0.1) and (temp_temp > 0.0)):
    if (checkBound(temp_temp,lastpubtemp,0.1) and (temp_temp > 0.0)):
      mqttc.publish("home/temperature", '{:3.2f}'.format(temp_temp), qos=0, retain=True )
      #print "PUB: T:%3.2f TD:%3.2f" % (temp_temp, abs(lastpubtemp-temp_temp))
      lastpubtemp=temp_temp
    #if ((abs(lastpubhumi-temp_humi) >= 0.1) and (temp_humi > 0.0)):
    if (checkBound(temp_humi,lastpubhumi,0.1) and (temp_humi > 0.0)):
      mqttc.publish("home/humidity", '{:3.2f}'.format(temp_humi), qos=0, retain=True )
      #print "PUB: H:%3.2f HD:%3.2f" % (temp_humi, abs(lastpubhumi-temp_humi))
      lastpubhumi=temp_humi
    mqttc.loop()

    # Wait for 10 seconds
    time.sleep(10)

except KeyboardInterrupt:
  #mqttc.loop_stop()
  mqttc.disconnect()
  s.cancel()
  #print "  Quit"

Install pigpio

wget https://github.com/joan2937/pigpio/archive/master.zip
unzip master.zip
cd pigpio-master
make -j4
sudo make install

Download DHT22 Python script and add to the same directory like the Python script above: http://abyz.co.uk/rpi/pigpio/code/DHT22_py.zip

Add to items/mqtt.items

Number
        Indoor_Temp
        "Innen Temperatur [%.1f °C]"
        <temperature>
        ["CurrentTemperature"]
        {mqtt="<[mosquitto:home/temperature:state:default]"}

Number
        Indoor_Hum
        "Innen Feuchtigkeit [%.1f %%]"
        <humidity>
        ["CurrentHumidity"]
        {mqtt="<[mosquitto:home/humidity:state:default]"}

Now run read.py

sudo python read.py

You can enjoy it with homekit too :wink:

3 Likes

Nice write-up.

I did something very similar. I used Adafruit_DHT instead of pigpio. It simplifies the code slightly. I hadn’t realized that the paho.mqtt had a client class. I switched my code over to using that. Not using it causes a TCP connect every time one sends a message (I suspect). I also have a PIR on my Pi that fires an event in my script. I like to keep the lights on when there is someone in the garage, at least after sunset.

I thought this was an interesting article that I thought I might implement: http://rawmeat.org/python-exponential-smoothing.html. I have heard that the DHT22 can be a little jumpy and this will smooth the numbers.

2 Likes

My script with temperature smoothing (and PIR which is irrelevant to the subject but might be interesting to someone). Some of the code didn’t really appear to format as it was written. Anyway, the smoothing algorithm worked well.

#!/usr/bin/python
import sys
import signal
import time
import json
import Adafruit_DHT
import RPi.GPIO as GPIO
import paho.mqtt.client as mqtt
from datetime import datetime
    
print('Version 0.03')   
sensorType = Adafruit_DHT.DHT22
sensorPin = 4                       # Connected to DHT22
occupiedPin = 22                    # LED turned on when motion is detected
unoccupiedPin = 17                  # LED turned on when motion is not detected
motionPin = 12                      # Connected to HC-SR501
mqttServer = "<server>"
mqttTopicPrefix = "State/Garage/{0}"
mqttTopicOccupied = "Occupied"
mqttTopicTemperature = "Temperature"
mqttTopicHumity = "Humidity"
mqttUserId = "<userid>"
mqttPassword = "<password>"
jsonMotion = '{{ "status": {{ "occupied": "{0}", "timestamp": {1} }}}}';                    # JSON for motion
jsonHumidity = '{{ "status": {{ "humidity": {0:0.2f}, "timestamp": {1} }}}}';               # JSON for humidity
jsonTemperature  = '{{ "status": {{ "temperature": {0:0.2f}, "timestamp": {1} }}}}';        # JSON for temperature    
motionStates = [ "OPEN", "CLOSED" ]

mqttc = mqtt.Client()

def getTime():
    # Save a few key strokes
    return datetime.now().strftime('%H:%M:%S')
    
def signalHandler(signal, frame):
    # Clean up on CTRL-C
    print '\r\n' + getTime() + ': Exiting...'
    mqttc.loop_stop()
    mqttc.disconnect()
    GPIO.cleanup()
    sys.exit(0)
    
def mqttPublish(topic, msg):
    # Publish to MQTT server
    thisTopic = mqttTopicPrefix.format(topic)
    mqttc.publish(thisTopic, msg)
    
def motionHandler(channel):
    # Called when motion sensor state changes
    motionValue = GPIO.input(motionPin)
    print("motionValue={0}".format(motionValue))
    
    if (motionValue):
        GPIO.output(occupiedPin, GPIO.HIGH)
        GPIO.output(unoccupiedPin, GPIO.LOW)
    else:
        GPIO.output(unoccupiedPin, GPIO.HIGH)
        GPIO.output(occupiedPin, GPIO.LOW)

    msg = jsonMotion.format(motionStates[motionValue], getTime())
        mqttPublish(mqttTopicOccupied, msg)

    
def smoothTemps(period=10):
    """ 
    Exponential moving average. Smooths the values over the period.  Send
    in values - at first it'll return a simple average, but as soon as it's
    gathered 'period' values, it'll start to use the Exponential Moving
    Averge to smooth the values.

    period: int - how many values to smooth over (default=1000). 
    
    Gratitude to Rikard Anglerud - http://rawmeat.org/python-exponential-smoothing.html
    """
    multiplier = 2 / float(1 + period)
    cumulative_temp = yield None  # We are being primed

    # Start by just returning the simple average until we have enough data.
    for i in xrange(1, period + 1):
        cumulative_temp += yield cumulative_temp / float(i)

    # Grab the simple average,
    ema = cumulative_temp / period

    # and start calculating the exponentially smoothed average we want.
    while True:
        ema = (((yield ema) - ema) * multiplier) + ema

def CtoF(value):
    return value * 9 / 5.0 + 32
    
signal.signal(signal.SIGINT, signalHandler)
GPIO.setmode(GPIO.BCM)
GPIO.setup(occupiedPin, GPIO.OUT)
GPIO.setup(unoccupiedPin, GPIO.OUT)
GPIO.setup(motionPin, GPIO.IN)
GPIO.add_event_detect(motionPin, GPIO.BOTH, callback=motionHandler)
GPIO.output(unoccupiedPin, GPIO.HIGH)
GPIO.output(occupiedPin, GPIO.LOW)
time.sleep(2)
mqttc.username_pw_set(mqttUserId, mqttPassword)
mqttc.connect(mqttServer, 1883)
mqttc.loop_start()

# Initialize and prime the temperature value smoother
st = smoothTemps()
next(st)

while True:
    humidity, temperature = Adafruit_DHT.read_retry(sensorType, sensorPin)
    
    if humidity is not None and temperature is not None:
        temperature = CtoF(st.send(temperature))
        timestamp = getTime()
        msg = jsonHumidity.format(humidity, timestamp)
        print(msg)
        mqttPublish(mqttTopicHumity, msg)
        msg = jsonTemperature.format(temperature, timestamp)
        print(msg)
        mqttPublish(mqttTopicTemperature, msg)
        
    else:
        print(getTime() + ': Error - Failed to get reading')
    
    time.sleep(60 * 10)     # Every ten minutes

1 Like

I will try ro build on weekend Leds under my bed with PIR which should turn on if it detects my feeds. The LEDs will be SK6812 with Warmwhite LED and RGB.

If somebody interested we can build together.

EDIT: It will run over an ESP8266 -> MQTT -> OH2

1 Like

Hmm, I used LinkSprite based ESP8266 with a HC-SR501 motion sensor. I used that particular board because it has a +5v out which is required by that particular sensor. Unfortunately I found that it threw a lot of false positives and I ended up replacing it with a Raspberry Pi. Let me know if you have better success.

HI, I have tried with your code and can’t run with below error,
I have tried either python 2.7 or python 3 all fail

with python 2.7 : sudo python dht22.py
File “dht22.py”, line 74
cumulative_temp = yield None # We are being primed

with python 3:sudo python3 dht22.py
File “dht22.py”, line 37
print ‘\r\n’ + getTime() + ‘: Exiting
’
^
SyntaxError: invalid syntax

How am I able to run your code, I believe I have all the required library installed

P.S. after check on http://rawmeat.org/python-exponential-smoothing.html, I found it’s due to some spacing in the code, after reformat few line of code, it runs.

Glad you found the issue. Sometimes stuff gets messed up when pasted into this box I am currently typing in. By the way the smoothing value is way too high. It will average the temperatures out over about a week. I should edit that.

oh, I was also wonder why the temp can keep all day almost no change (within 1 degree),
that was the reason,

I modified the post to smooth over ten. I’m at work so I don’t actually remember what value I settled on. Do you have any feedback on a good value for this?

I got this error:

File “read.py”, line 40
temp_temp=(s.temperature()/1)
^
SyntaxError: invalid syntax

If you had used my script I would have been able to help you. The only suggestion I have is that the script you copied on line 40 reads:

temp_temp=(s.temperature()/1.)

Note the period after the one.

Do you have a current working version of this script?

Is the version posted above not working? If not then yes I do and can repost.

I tried to remove the format errors but failed.